DSP/BIOS


中断处理任务一般要划分为两个部分:一个是控制部分,花时间少,放在HWI函数中;另一部分是处理部分,放在SWI函数或任务中处理。

4种API实现实时监测数据的采集:LOG,STS,HST,TRC。

DSP/BIOS支持4种线程

(1)硬件中断(HWI): 频率可达200KHz(5us),处理时限在2us~100us,包括CLK函数

2)软件中断(SWI:时限100us以上,SWI允许HWI将一些非关键处理在低优先级上延迟执行,这样可以减少在中断服务程序中的驻留时间

(3)任务TSK:任务与软件中断不同的地方在于在运行过程中可以被挂起。DSP/BIOS提供了一些任务间同步和通讯的机制,包括队列、信号灯和邮箱。

(4)后台线程(IDL):MAIN->空闲循环: 运行那些没有执行期限(deadlines)的功能。

线程的选择:有时限要求、部分有时限要求、不如其他线程要求的时间严格、没有时限要求(idle线程)

HWI 其实就是中断服务。当硬件中断产生之后,DSP/BIOS就会调用相应的HWI函数。如果把HWI设置为Dispatch的话,则会在调用HWI函数的前 后自动调用HWI_enter和HWI_exit。在HWI函数的执行时,若有其他的硬件中断产生,当前的HWI会被新的中断抢占,也就是说DSP会先去 执行新的HWI。如果希望当前的HWI在不被其他的HWI打断的话,可以在不能被打断的代码前后调用HWI_disable和HWI_enable。 HWI的优先级是硬件级别的优先级(固定的),若同时有多个中断向DSP请求的话,它决定DSP先响应哪个中断。而中断所对应的HWI则是可以被任何其他 的HWI抢占。
SWI有15级优先级,高优先级的SWI可以抢占低优先级的SWI。一般通过SWI_post(或者类似的函数)来启动它。SWI和HWI一样都是不能被阻塞(blocking)的线程。也就是说一旦它们被运行,就要运行到终点为止,除非被其它的线程抢占。

HWI和SWI都使用系统堆栈,而每个TSK都有自己的堆栈。可以在TSK线程之间随意地互相切换,切换时DSP/BIOS将自动地更新堆栈寄存器,因此TSK线程可以被阻塞。这样TSK就可以写成一个死循环:

While(1){
   Do_some_task();
   Yield_to_other_task();
}

Do_some_task 做这个TSK所要做的事情,Yield_to_other_task则把控制权转给其他的TSK。例如如果是把控制权转给同样优先级的其他TSK,则可以 调用TSK_yield函数。如果是要把控制权转给低优先级的TSK,则可以调用TSK_sleep函数让自己休眠一段时间,或者调用SEM_pend函 数等待。除非TSK中调用了HWI_disable或者SWI_disable,否则它在任何时候都可以被HWI或者SWI抢占。
TSK和电脑上的线程有些类似,而HWI和SWI则不一样。下面举一个例子说明一下:假设有线程SWI1,SWI2,TSK1,TSK2。SWI1的优先级大于SWI2,TSK1的优先级大于TSK2。
则:
若 在SWI2运行当中,SWI1被post了的话,DSP马上转到SWI1运行,并且直到SWI1运行结束之后再继续SWI2的运行。这就是说SWI只能被 抢占,不能被阻塞。这是因为所有的SWI和HWI都公用系统堆栈,一旦SWI1运行,堆栈的最上层就变成了SWI1的环境,除非SWI1运行结束,是无法 切换回到SWI2的环境中去的。

若在TSK2运行当中,TSK1进入ready状态的话,DSP马上转到TSK1运行。稍后如果TSK1被阻塞的 话,DSP再继续TSK2运行,当TSK1所等待的信号就绪之后,再转入TSK1运行,如此反复。也就是说DSP可以在TSK之间相互切换。这正是因为每 个TSK都有自己独立的堆栈可以保存自己的运行环境。

有时限要求

线程是否需要时钟中断或其它硬中断触发?

1.1 时钟中断触发,则可选(a)CLK线程,(b)PRD函数,(c)task/SWI:当线程需要挂起的时候。

1.2 其它中断触发,选HWI线程

1.3 应用程序运行到一定的时候满足条件才运行:

(a)一段时间expiration才运行,选PRD函数

(b)其它程序条件:task/SWI

(c)硬件中断是条件:处理分为2个线程, HWI+task/SWI

线程的选择:task or SWI ?

1)需要函数在等待资源时能够挂起吗?

2)函数和其它线程有复杂的关联(相互依赖)或复杂的数据共享吗?

3)希望函数有自己的栈吗?(不是共享系统栈)

4)希望线程使用LCK, MBX, or SEM modules吗?

5)希望线程在created, deleted, exited, made ready to run, or becomes thecurrent thread时运行一个函数吗?(任务线程支持这些事件的全局函数挂钩(global function hook),例如,希

望所有任务都调用一个函数来保存/回复外部硬件寄存器)。

(6)这个线程比一个线程(上面的回答至少一个yes)优先级低的线程。

有一个回答yes,选task,全no,选SWI;

TaskSWI的区别

a)资源不能得到时,任务可以在执行时挂起,软件中断不行;

bDSP/BIOS提供大量的结构用于任务间通讯和同步:信号,消息邮箱,资源锁;这些结构软件中断不能使用;

c)每个任务有其自己的栈,软件中断共享系统栈

d)在任务created,deleted, exits, or switches thread context时,和特定系统有关(System-specific)的函数可以被调用,这些函数用于extend a tasks context beyond the basic processor register set

e)任务的优先级低于软件中断,高于背景idle线程,共16个优先级,用户使用1150好任务running the DSP/BIOSidle loop.

线程的选择

一部分有时限要求:把处理分成2个线程:HWI+TASK/SWI

不如其它线程的时限要求严格:关键要看是什么触发线程,是一段时间间隔完还是一些其它程序条件。同前有时限要求1.3.

线程选择小结

选线程类型:1、看是否和中断有关 2、是否周期 3、是否时限限制

(1)   选CLK 对象:时钟中断触发;

(2)  周期PRD对象:周期运行的功能;

(3)  HWI对象:由硬件中断触发且有时限要求;

(4)  SWI对象:需要执行完的任务,但时限要求没有HWI高。

TASK对象:线程选择中的6个问题,看是否需要,等待资源时挂起,数据共享和复杂关系,和其它线程通讯,有自己的栈,任务切换时调用同一个函数,比task优先级低。

线程优先级:硬件中断---软件中断(软件中断不能被阻塞)---任务(共有15个任务,加上TSK_idle共16个)。任务在等待某个资源有效时可以被阻塞,后台线程idle_loop是优先级最低的线程。

任务的调度

运行态(Running),代表任务是处理器当前正在执行的线程,以TSK_RUNNING表示。

就绪态(Ready),代表任务一旦获得处理器的处理时间就可以执行,以TSK_READY表示。

阻塞态(Blocked),代表任务必须等到某个事件的发生才可以运行,以TSK_BLOCKED表示。

终止态(Terminated),代表任务已经结束,不会再运行,以TSK_TERMINATED表示。

 

 

DSP/BIOS功能模块

LOG模块目标程序执行时,可以使用LOG模块中的事件日志来记录实时的事件。我们可以使用系统日志,也可以创建用户自定义的日志。如果日志类型是循环的,那么缓存区始终保存的是最后一次记录的信息。如果日志类型是固定的,那么缓存区始终保存了第一次记录的信息。LOG模块的系统日志存储与系统事件有关的消息,这些系统消息应该是我们在TRC跟踪模块中激活了的事件。
 为了减少运行时间,日志数据的格式化处理总是在主机上完成。也就是说,由运行CCS的主机而不是DSP目标系统来处理这些日志数据,理解这点很重要。我们通常使用LOG_printf函数来替代标准C语言中的printf函数,以便更快的在CCS中显示需要打印的信息。
    日志缓存区是在数据存储器中一段固定大小的存储空间。在日志缓存区里,一个消息占用4个字的存储空间。第一个字用来存储序号,这些序号控制事件日志用正确的顺序显示日志。剩下的3个字记录数据,它们是调用API函数时写进日志的。

LOG模块配置:打开DSP/BIOS配置文件,然后展开“Instrumentation”选项,LOG - Event Log Manager菜单下:“LOG_system”对象是系统创建该配置文件时自动添加的,用于系统事件记录;“LOG_msg”是用户自定义。鼠标右键单击该对象名称,在弹出的菜单中选择“Properties”激活属性窗口。
comment:添加一段注解来说明该LOG对象。
bufseg:选择日志缓冲区的存储段的名称。
buflen:说明日志缓冲区的大小(以字为单位)。
logtype:说明日志类型,循环或者固定。在缓冲区内,原有的循环类型的日志可以被新的事件覆盖,但是固定类型的日志不能被覆盖。因此,当你的日志消息可以正常显示但不更新时,请设置日志类型。
fixed(固定):只存储其最先接收的信息,当消息缓冲区满时就会拒绝接收新的信息。
circular(循环):当消息缓冲区满时,新日志会自动覆盖原有的日志。
datatype:如使用LOG_printf函数来打印输出日志信息时,请选择“printf”类型。如使用LOG_event函数记录日志信息,请选择“raw data”。
format:当datatype选择“raw data”即原始数据作为数据类型,那么就要写一段“printf风格”的格式串。

LOG模块API函数说明

1、  LOG_disable:关闭指定对象的日志记录功能,此时日志缓冲区的内容将不会被更新。

2、LOG_enable:允许日志记录事件。DSP/BIOS默认日志记录功能为打开状态。
3、LOG_error:将一个事件、数据或者出错信息按指定的格式串写入系统日志。
4、LOG_event:将一个为格式化的事件消息吸入日志中。
5、LOG_message:功能及用法与LOG_error相同,只是它要受跟踪管理模块的影响。
6、LOG_printf:在指定的LOG窗口中显示消息,等效于标准C的printf()函数。
7、LOG_reset:复位日志缓冲区。

PRD模块概述:PRD函数大多被用于那些需要定时执行的函数,特别是一些需要周期性地执行而其执行频率很低地函数。如:键盘等慢速I/O设备地扫描,WATCHDOG地监控等。这些简单地应用情况我们仅仅需要在创建PRD模块时说明该PRD模块执行地时间间隔即可。而有些时候,我们需要根据条件来启动周期性函数,或对一些周期性函数进行延时操作,这是就可以调用PRD_start和PRD_stop等API函数来增强PRD模块地管理。
    PRD函数实际上是由内核的PRD_swi(SWI对象)来管理的。当用户在DSP/BIOS配置工具中建立一个PRD对象后,系统内核将自动创建一个软件中断模块PRD_swi。当PRD_tick函数计数达到预设值时,内核启动PRD_swi模块,并由该模块具体确定将哪个PRD对象放到执行队列中等待运行。
  
PRD模块配置
1、根据实时时钟确定函数运行地时间:打开PRD模块地属性修改窗口,勾选“Use CLK Manager to driver PRD”框,以启动运行周期函数管理程序。然后在每个PRD对象属性窗口中,设置该对象调用函数的执行频率。
注意:当程序中有多个PRD对象时,所有的PRD对象都是由同一个周期计数器来驱动的。通常情况下,是由DSP/BIOS的时钟CL模块来管理的。每一个PRD对象在不同的周期内完成自己的功能。
2、根据I/O地可用性或者一些其他事件确定函数运行时间: 如果用户想自己管理周期函数的计数器,则需勾除“Use CLK Manager to driver PRD”框,然后在程序中自己调用PRD_tick来对周期计数器加1,已保证周期函数能顺利启动。
PRD模块API函数说明

1、  PRD_getticks:返回周期性函数执行的计数值;

2、PRD_start:启动该PRD模块计数器;
3、PRD_stop:停止该PRD模块计数器。
4、PRD_tick:系统内核或用户调用该函数完成对PRD管理模块的计数;

SWI模块概述:在DSP/BIOS内核中,系统管理并运行的线程分为四个等级:硬件中断服务程序、软件中断服务程序、任务和后台空闲函数,优先级依次降低。每个软件中断服务程序都对应一个函数,当然,每个软件中断夜可以单独设置优先级。高优先级的软件中断会抢占正在执行的低优先级的软件中断(因此可以说DSP/BIOS是一个基于优先级的抢占式实时内核)。                                                                                        
    
所有软件中断都是通过DSP/BIOS内核的API调用来启动。一旦启动了一个SWI对象,此时,系统将为该SWI对象中的函数创建一个运行时间表。因此,当一个软件中断被启动后,其对象函数不一定会立即执行,而是会按照时间表在执行队列中根据优先级排队等候运行。DSP/BIOS根据软件中断优先级来判断是否要暂停当前运行的线程。
    为了便于控制,系统为每个SWI对象都设置有一个16位的邮箱(Mailbox),可以利用邮箱的值有条件的启动这个软件中断。系统内核会自动维护邮箱的管理。DSP/BIOS内核提供了SWI_disable和SWI_enable操作来禁止或允许软件中断。同时还为软件中断设置有15个优先级,最高优先级为SWI_MAXPRI(14),最低优先级为SWI_MINPRI(0),0优先级为KNL_swi对象保留,KNL_swi对象的任务是执行任务调度程序,该对象由内核自动创建,高优先级的软件中断会打断正在运行的低优先级的软件中断。如果启动的两个软件中断的优先级相同,那么先启动的软件中断会先执行。

    中断线程(包括硬件中断和软件中断)都是使用相同的堆栈来执行的。当中断发生时,新的线程就会添加到栈顶,系统会执行一次任务切换(Context Switch)。由于高优先级软件中断会打断低优先级的软件中断的运行,所以SWI模块在运行高优先级软件中断前会自动保存寄存器中的内容。在高优先级软件中断运行完成后,寄存器会恢复原来的内容,以便继续运行原来的低优先级中断。如果没有启动其他高优先级的软件中断,低优先级的软件中断就会运行。DSP/BIOS内核虽然具有抢占的特点,但如果没有导致任务切换的API函数调用,系统则不会主动切换道其他线程去执行的。(理解这点在实际应用中很重要,即如果现在运行的是低优先级软中断对应的函数,如果你不在函数中调用如SWI_post()启动更高优先级的软件中断或启动了比自身低的优先级中断,则当前软中断就不会被打断,执行直到退出)。

       个人经验:尽量不要在一个软中断对应的函数中去启动另一个比其本身优先级高的软件中断,因为根据抢占原则,其本身将被打断,从而CPU转去执行高优先级软中断对应的函数,低优先级的实时性将得不到保证,当有多级优先级及系统复杂情况下甚至引起系统瘫痪。也不要设置很多的优先级。当然这也不是绝对的,如果系统规划的好,利用好软中断的基于优先级抢占式的特点会大大简化你的设计。

二、LOG模块配置


       
软件中断可以在DSP/BIOS的配置文件中静态说明。打开DSP/BIOS配置文件,展开“Scheduling”选项,即可选择SWI模块。
 comment:添加一段注解来说明该SWI对象。
 function:该软件中断对象将调用的函数名。
 priority:显示SWI对象的优先级。
 mailbox:设置邮箱的初始值。
 arg0,arg1:软件中断函数的两个指针类型变量。该变量可以在启动运行软件函数时由内核传递给该函数。

三、LOG模块API函数说明

1、SWI_andn:该函数提供的参数与邮箱值做“与”运算,若邮箱为0,则启动该软件中断;

2、SWI_dec:邮箱值减1,若邮箱值为0,则启动该软件中断,并恢复邮箱到初始值;
3、SWI_disble:禁止软件中断;
4、SWI_enable:允许软件中断;
5、SWI_getmbox:返回邮箱的值,注意该函数只能在软件中断函数中调用;
6、SWI_getpri:返回软件中断优先级;
7、SWI_inc:启动该软件中断,并对邮箱值加1;
8、SWI_or:启动该软件中断,并且邮箱值与该函数提供的参数做“或”运算;
9、SWI_post:启动软件中断;
10、SWI_raisepri:将软件中断优先级升高;
11、SWI_restorepri:恢复软件中断的优先级;
12、SWI_self:返回SWI对象的地址。

 

你可能感兴趣的:(达芬奇DSP)