DSP

DSP——DSP/BIOS学习笔记之(三)——TSK和MBX

2019-07-13 09:50发布

转自:http://blog.sina.com.cn/s/blog_638c189f0100kvlm.html

TSK:
task,任务.在Scheduling(时序)中,其优先级高于IDL(背景程序),低于SWI(软中断)和HWI(硬中断).可以被软硬中断打断,也可以被其它事件阻塞(blocked),顺便说一下,SWI和HWI是不能阻塞的.
 tsk和swi和hwi类似,可以调用一个函数,还可以传递多达8个参数,不同的tsk可以有15个优先级,执行任务的顺序相当于一个优先队列,如果一个tsk被阻塞,会自动将这个tsk放到同优先级tsk的后面,等待条件满足的时候执行.  tsk有个重要的函数TSK_yield,当一个tsk调用这个函数的时候,会将自己挂起(pend),将控制权交给接下来的一个tsk,让他执行.查看不同的tsk的状态,可以选择"DSP/BIOS -> Kernel/Object View ",在出现的窗口左边点击tsk,就可以在右边看到所有tsk的状态(ready:准备好,running:运行中,blocked:被阻塞,terminal:结束) mbx:
mailbox,邮箱.在Synchronization中.一般作为不同的任务(tsk)之间传递数据.
mbx有两个属性:
Size:这里面可以存放的单个数据的大小,比如想在这里面存放int类型的数值,那么Size就设为4.
Length:里面最多能存放多少个大小为Size的数据,如果设为2,那么就能存放2个.
主要函数: MBX_pend: 语法:
status = MBX_pend(mbx, msg, timeout);
参数:
MBX_Handle mbx; 
Ptr msg;    
Uns timeout;  
返回值:
Bool status;  
描述: 如果mbx不为空,将复制其中的第一个数据到msg指定的地址并且返回TRUE.如果为空,这个任务将被挂起,直到MBX_post函数被调用或者timeout设置的时间到.如果timeout的值是SYS_FOREVER,那这个任务将一直挂起,直到MBX_post函数被调用,如果timeout的值是0,那么将立即返回FALSE. MBX_post: 语法:
status = MBX_post(mbx, msg, timeout);
参数:
MBX_Handle mbx;       
Ptr msg;          
Uns timeout;        
返回值:Bool status;    描述:
MBX_post 检查mbx中是否有空闲的位置,如果有,则将msg的内容写入mbx.同时将下一个MBX_post任务(如果有的话)设为ready状态.如果mbx已经满了,而且timeout等于SYS_FOREVER,这个任务将被挂起,等待MBX_pend函数被调用.如果timeout等于0,那么立即返回FALSE.如果timeout是不为0的数,这个任务将等待这么长的时间,如果还是没有MBX_pend函数被调用,则返回FALSE.
A task switch occurs when calling MBX_post if a higher priority task is made ready to run, or if there are no free message slots and timeout is not 0. 这两个东西还有不少可以测试的地方,比如tsk的优先队列是怎么排的,调用TSK_yield后排到什么地方,mbx未放满是不是能读取,未读完是不是能放入,pend和post的Timeout值有什么用,这些等下次有空研究的时候补充.   在调试TI的一个例程tsktest(位置:X:CCStudio_v3.1 utorialsim64xx sktest,其中的sim64xx根据你使用的DSP类型而定)的时候,花了不少时间,才终于理解了这里面任务调度的顺序. 程序中有一个mbx,能放2个8B的数据,有4个tsk,1个是读mbx,3个写mbx,这三个写mbx的tsk调用的是同一个函数,不过为了区分,调用的时候传递了一个不同的参数.程序运行的结果这里就不写了. 我经过改变mbx的length和write函数的循环写入次数后,可以确定mbx有自己的一个任务队列,当mbx满的时候,post过来的tsk会被blocked,但是这个post的动作已经是放在mbx的任务队列中,等到mbx被读空的时候,会自动开始这个post动作.如果在这个post动作后面还排着其它的动作,那么会接着执行后面的动作.   举个现实中的例子,假设有ABC三队人在排队买票,每队3人,分别叫A1A2A3B1B2B3C1C2C3.他们在售票大厅外面的广场排队,售票大厅有一个门,要进大厅的人,必须在门前排成一队,然后才能进去.从各自的队伍排到门口的队伍,按照ABC的顺序,门口的队伍每队只能有一人.大厅里面有2个窗口在卖票,如果两个窗口都有人在买,那么后面的人只能排在门口,不能进大厅.只有当在两个窗口买票的人都离开后,排在后面的前两个人才能进大厅.进入大厅的人,如果自己的队伍还有人,必须通知下一个人过来排队.那么看一下买票和排队的顺序.一开始A1A2到门口,大厅空,两人进去,通知A3过来排队
A3到门口,里面满了,在门口排队,A队已经一人在门外,就轮到B队了B1到门口排队,排在A3后面,B组结束,轮到C队,C1排到B1后面,C队结束.现在开始第一轮卖票,A1A2买完走人,大门可以进人,A3进大厅,因为A队没人了,不用通知,接着B1进入大厅,顺便通知B队的B2过来排队,B2只能排在C1后面.
大厅又满,禁止进人,
开始卖票,A3B1买完走人.大门开,C1进门,通知C2过来排队,C2排在B2后面,
B2进门,通知B3来排队,B3排在C2后边.大厅满,
开始卖票,C1B2买完走人.大厅空,门开,C2进,通知C3,C3排在B3后边,B3进大厅,B队没人,不用通知,大厅满,开始卖票,C2B3买完走人,大厅空,门开,C3进,后边没人,不用通知,由于所有队都排完,大厅虽然没满,也开始卖票,C3买完走人.卖票的人等了一会,发现没人来买了,就关门回家了.买票的顺序就是A1A2 A3B1 C1B2 C2B3 C3,再和tsk和mbx类比,三个tsk就是ABC三条队伍,每次要post一个数据,就是来门口排队,而门口的队伍就是mbx的任务队列,只有mbx中的内容被读完,才会依次启动任务队列中的任务 1、它们都是任务间通信的手段,但是应用场合不同
 信号量用作任务同步或者资源的互斥访问
至于邮箱,名副其实的,可以“邮寄”一些东东给别的任务
举个例子的话,我觉得可以说信号量就像别人打你电话只响一下(具体代表什么含义在于你们之间的约定),而邮箱就像短信哇,写啥都行。当然,二进制信号量也可以用邮箱实现。
2、任务与事件的概念不同
 任务作为一个竞争系统资源的单位,各个待执行的任务被系统统一调度;
而任务之间(或者任务与中断之间)的互动体现在信号上,也就是事件。某些事件的发生可以使任务的运行状态发生变化。
比如两个任务A、B同时竞争某个资源,以信号量为途径实现互斥。
在A获得了信号量后,在使用资源的时候,B由于等待信号量而处于阻塞状态。
A使用完毕资源后,告知B“我用完了,你们用吧”,这个事件的传达是通过信号量完成的。
3、这个问题懒得敲了,复制粘贴之
 “对信号量的初始计数值赋值。该初始值为0到65,535之间的一个数。如果信号量是用来表示一个或者多个事件的发生,那么该信号量的初始值应设为0。如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为1(例如,把它当作二值信号量使用)。最后,如果该信号量是用来表示允许任务访问n个相同的资源,那么该初始值显然应该是n,并把该信号量作为一个可计数的信号量使用。”

  可以一个任务对应多个事件吧,看你怎么设计了,比较简单的情况的话,一个任务可能也就关注一个事件。 关于初始值的问题,你说的应该是信号量的初始值吧,信号量最大的作用就是共享和竞争资源初始值为n的情况,比如有n台打印机,每个时间点上一台打印机只有一个任务可以访问,如果有m个任务都想使用打印机,而且m>n的话,就有资源的征用问题,引入一个初始值为n的信号量,此信号量的数值所代表的就是“当前还有多少打印机可以使用”。想用打印机的任务去看信号量,如果还有的话就Take之,否则就等待(阻塞)或者先干别的去(不等待立即返回),同样的,使用过打印机的就Give之,以便其他任务可以使用打印机。
  初值为0的情况也是普遍存在的,例如一个数据处理系统,任务A产生数据,缓存到一定数目后交给任务B做处理,这个事件使用信号量做通知的手段。那么系统初始化时,对B而言,是没有数据要处理的,那么信号量自然为0了。每当任务A提交一次数据,对信号量加1(做SemGive),任务B发现信号量非0后,就去处理数据,处理一个,对信号量减1(做SemTake)
DSP/BIOS内核的应用程序中,优先级从低到高有四个线程:
后台线程(IDL)线程;
TSK(任务)线程;
SWI线程(软件中断);
HWI线程(硬件中断);
应用程序从默认进入点_c_int00开始运行,首先完成系统的初始化,包括DSP/BIOS配置中指定的各个寄存器的设置以及PLL倍频时钟的设置等,然后调用main()函数,在main()函数结束返回,调用BIOS_start(),开始按优先级检测并执行HWI,SWI,TSK,当前面所有线程都没有执行时(没有HWI和SWI,有的任务已经完成,而有的被挂起),开始进入IDL_F_LOOP循环,执行后台的IDL线程(LOG模块的信息显示就是在IDL线程中完成的,CPU负荷图的数据也是这时读取并送到PC主机,以便完成图形绘制等)。在IDL_F_LOOP中,若有HWI,则立即暂停IDL,切换到硬件中断服务子程序。当硬件ISR完成后,根据具体情况判定有无SWI或唤醒任务,最后才回到IDL继续运行。
 注意,DSP/BIOS内核仍然是个多任务的内核,但并不是一个严格意义上的抢先式内核。除了HWI,其他所有线程都必须自己自动放弃对CPU的控制,以便完成切换。当然,当硬件中断发生后,也会产生线程的切换,这时所有线程都将按优先级重新安排运行,所以从这点讲,DSP/BIOS内核也具有抢先的特点。
  在DSP/BIOS内核应用中,用户可以将自己的处理代码(如G723的编码、解码,MP3 解压等)放到硬件中断,软件中断,任务或后台线程中的任何一个线程中调用运行。一般推荐在SWI和TSK中调用。用户可以在DSP/BIOS的配置文件中简单说明这些软件中断或任务(即所谓的静态说明),也可以在运行过程中通过DSP/BIOS内核提供的API函数调用动态创建。
  API函数调用几乎都可以使用C或C++语言直接调用。通过这些API,用户可以完成硬件中断的管理,软件中断的启动,任务的切换,各线程之间的同步和数据通信等