1 基础类的模块:时钟管理CLK
CLK模块为用户周期性调用函数提供方法,同时对一些代码评估工具提供时间参考。
实际上,CLK模块完全依赖DSP的定时器中断。
CLK管理器还允许随意建立各种时钟函数,当定时器中断发生时,CLK管理器就执行这些时钟函数。
当定时器的计数寄存器减到0时,便产生定时器中断。DSP/BIOS内核进入中断服务子程序CLK_F_isr函数,完成低分辨率计数器加1,执行所有CLK模块预定义函数。其中低分辨率寄存器是DSP/BIOS内核中的一个32位的寄存器(地址0x7c和0x7d单元)
添加时钟中断函数时,若使用C语言函数,则前面要有下划线。且不能使用汇编宏HWI_enter和HWI_exit,也不能使用关键字interrupt,因为他们都是在CLK_F_isr子程序中被调用执行的。
设置CLK属性:
Object Memory CLK模块使用的存储器在那个段中,一般默认为IDATA段。
Enable CLK Manager 片上定时器硬件被CLK模块使用。如果不希望使用片上定时器驱动系统时钟,可清楚选项。
Use high。。。。使用高分辨率定时器。否则使用低分辨率定时器。
DSP/BIOS提供了两种独立的时隙管理方法:高分辨率和地分离时隙管理。无论是那个,都含有一个32位计数器。
低分辨率的计数器记录了定时中断发生的次数,也就是说,低分辨率的时隙管理时钟和定时器中断时钟一致。用CLK_getltime得到定时的周期寄存器的值。
高分辨率时间是定时器的计数寄存器被减的次数,也就是对机器时钟(CLKOUT)除以定时器中TDDR寄存器的值加1后得到的频率进行计数的结果。用CLK_gethtime得到高分辨率计数器的值。因为CLK模块是其他许多模块的基础,显然高分辨率的时隙管理能提高统计模块对代码执行时间估计的准确性。
Microseconds/Int 中断间隔
Directly。。。。用户自己直接定义硬件定时器中的prd和tddr寄存器。这时,Microseconds的值,有prd和tddr寄存器的值和CPU的时钟速度计算得到。
Fix TDDR 如果被选中,下面的TDDR寄存器的值,不会随Microseconds项被修改而变化。
TDDR 和PRD寄存器,分别表示定时器中两个寄存器。
CLK API
(1)Uns ncounts = CLK_countspms(void)
返回每毫秒的定时器高分辨率时钟的计数值
(2)LgUns currtime = CLK_gethtime(void)
返回高分辨率时钟的计数值 超过32bit归零
高分辨率时钟是DSP时钟除以(TDDR+1)
(3)LgUns currtime = CLK_getltime(void)
返回低分辨率时钟的计数值 超过32bit归零
定时器的中断频率是DSP时钟除以(TDDR+1)再除以(PRD+1)
(4)Uns period = CLK_getprd(void)
返回CLK管理器周期寄存器的值
2 基础类的模块:周期函数PRD
在我们的应用程序中,有一部分程序可以根据实时时钟来确定该函数运行的时间,也还有一些应用需要根据I/O是否可用或者已经计划好的事件来确定运行的时间。用PRD可以完成这些要求。
通常PRD对象可以用CLK模块来管理,也可以自己调用PRD_tick函数来管理。
2.1 根据实时时钟确定函数的运行时间。
首先在CLK管理属性,设置中断速率,然后在PRD模块属性中,设置用CLK管理模块驱动PRD。然后在每个PRD对象中设置相关函数和执行频率。
2.2 根据I/O的可用性或者一些其他事件确定函数运行时间。
如果用户想自己管理周期函数的计数器,则必须取消PRD模块中的用CLK模块管理驱动PRD选项。用户必须自己调用PRD_tick函数来对周期计数器加1
PRD对象的执行函数是有配置工具静态规定的。实际上是由内核的PRD-swi来管理的。内核自动简历一个软中断模块,函数计数达到预定值,内核启动软中断模块。
(1)LgUns num = PRD_getticks(void)
返回32位周期函数管理计数值
(2)void PRD_start(PRD_Obj* period)
启动PRD模块计数器,一般地,mode=one-shot
(3)void PRD_stop(PRD_Obj* period)
关闭PRD模块计数器
(4)void PRD_tick(void)
对周期模块的计数器加1,以便周期模块管理器确定哪个周期性函数得以运行
3 基础类的模块:软件中断管理SWI
SWI模块管理软件中断服务程序。这些中断服务程序与HWI硬件中断服务程序有密切关系。一般情况下,
会将日常的大多数处理函数放到这些软件中断服务程序中管理运行。
一、SWI模块概述
在DSP/BIOS内核中,系统管理并运行的线程分为四个等级:
硬件中断服务程序、软件中断服务程序、任务和后台空闲函数,优先级依次降低。每个软件中断服务程序都对应一个函数,当然,每个软件中断夜可以单独设置优先级。
高优先级的软件中断会抢占正在执行的低优先级的软件中断(因此可以说DSP/BIOS是一个基于优先级的抢占式实时内核)。
所有软件中断都是通过DSP/BIOS内核的API调用来启动。一旦启动了一个SWI对象,此时,系统将为该SWI对象中的函数创建一个运行时间表。因此,当一个软件中断被启动后,其对象函数不一定会立即执行,而是会按照时间表在执行队列中根据优先级排队等候运行。DSP/BIOS根据软件中断优先级来判断是否要暂停当前运行的线程。需要注意的是,如果一个软件中断在它开始运行前已经被启动许多次,由于HWI和更高优先级的中断正在执行,这个软件中断只会运行一次,后面几次启动会被忽略。
为了便于控制,系统为每个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转去执行高优先级软中断对应的函数,低优先级的实时性将得不到保证,当有多级优先级及系统复杂情况下甚至引起系统瘫痪。也不要设置很多的优先级。当然这也不是绝对的,如果系统规划的好,利用好软中断的基于优先级抢占式的特点会大大简化你的设计。
二、模块配置
软件中断可以在DSP/BIOS的配置文件中静态说明。打开DSP/BIOS配置文件,展开“Scheduling”选项,即可选择SWI模块。
comment:添加一段注解来说明该SWI对象。
function:该软件中断对象将调用的函数名。
priority:显示SWI对象的优先级。
mailbox:设置邮箱的初始值。
arg0,arg1:软件中断函数的两个指针类型变量。该变量可以在启动运行软件函数时由内核传递给该函数。
三、模块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对象的地址。
四、SWI的执行
通过调用SWI_andn,SWI_sec,SWI_inc,SWI_or,SWI_post可以使软件中断被调度执行。这些函数本身可以在程序的任何地方调用------中断服务程序ISR中,周期函数中,空闲函数中或其他软件中断函数中。
当一个SWI对象被触发时,SWI管理器将该软件中断添加到一个被触发软件中断的列表中等待,然后SWI管理器检查软件中断当前是否被使能。如果使能,SWI管理器将该SWI对象的优先级和当前运行线程的优先级进行比较。若当前运行线程是后台空闲循环IDL或是一个更低优先级的SWI,那么SWI管理器将这个SWI对象从被触发SWI对象列表中移除,并将CPU控制权从当前线程交给SWI对象,开始执行SWI函数。
Note:1.当一个SWI开始执行后,必须无阻塞地运行到结束;
2.当在HWI中调用时,调用任何会触发软件中断的SWI函数的代码必须包装在一个HWI_enter/HWI_exit
宏调用中,或者由HWI调度程序调用;
3.如果一个软件中断在SWI管理器将其从被触发SWI对象列表中移除之前,被触发多次,其SWI函数只
会执行一次。这个类似硬件中断的特征:即在CPU清除中断标志寄存器中相应的中断标志为之前,如
果该硬件中断触发多次,对应的HWI只会执行一次。
五、SWI对象的邮箱
每个SWI对象有一个邮箱,可以决定是否触发该中断。用于触发SWI的API函数可以对邮箱值作不同的操作,并根据不同的限制条件触发SWI对象。其中SWI_post,SWI_or,SWI_inc无条件的触发。
表1-1 SWI对象触发函数
动作
将油箱看作位掩模
将油箱看作计数器
不改变油箱值
要访问一个SWI对象的邮箱值,可以在该SWI函数调用SWI_getmbox,这个函数只能在SWI函数中调用,其返回值是该SWI对象从被触发SWI对象队列中移除之前其邮箱的值。当SWI管理器从被触发对象列表中移除一个SWI对象时,其邮箱值被复位为其初始值。如果在SWI函数执行的时候该SWI对象又一次被触发,其邮箱值也会被相应的更新,然而这些都不会影响SWI函数执行时调用SWI_getmbox返回的值,该值在SWI对象被从被触发SWI队列中移除那一刻锁存起来的油箱值。
1.SWI_inc:一个SWI对象在被移除队列之前,如果被多次触发,一般情况下,SWI管理器只会调度其函数执行一次,然而,如果应用程序需要该SWI函数在这种情况下执行多次,可以利用SWI_inc来触发该SWI对象。如果使用SWI_inc触发一个SWI对象,一旦SWI管理器调用并执行对应的SWI函数,就可以再函数中访问该对象的油箱值,获知SWI在运行之前被触发了几次,然后可以将相同的一段程序执行同样的次数来满足应用程序的需求。
2.SWI_andn:如果一个软件中断必须在多个事件都发生的条件下才能被触发,应该使用SWI_andn。例如一个软家中断在执行之前必须等待两个不同设备的输入数据,其邮箱应该配置有两个被置1的比特位。
3.SWI_or:在某些情况下,SWI函数需要根据触发事件的不同来调用不同的函数,这是可以用SWI_or。
4.SWI_dec:如果应用程序需要同一个事件必须发生多次之后才触发一个SWI,可以使用SWI_dec。
4 其他基础类的模块:信息输出管理LOG
使用方法与上面模块类似。
5 其他基础类的模块:存储器管理MEM
我们可以通过MEM管理来压缩代码的大小,这点对小存贮器系统来说很重要,可以通过取消动态分配能力或者清楚空白存储区来减少对存储区的浪费。选择属性下面的No Dynamic。。。,程序不能调用任何MEM模块提供的API函数,或者其他对象创建函数。我们只能通过配置工具在CDB配置文件中静态创建程序所需要的所有对象。另外,也可以使用配置工具在一个单独的存储段中创建和取消动态存储区。
MEM提供的API函数,主要是存储器动态管理的函数,如内存申请和释放等,他们都是针对系统定义的堆进行操作,所以在MEM模块管理中必须保留足够的动态堆。
MEM模块的数据段,程序段详解:
1 DSP/BIOS内存管理全局参数设置
Reuse Startup。。。选中该项,则启动过程完成后,启动代码部分(.sysinit)所占用的存储器可以被再次使用。
Argument Buffer Size 表示.args部分的大小,包含了程序main函数的命令行参数
Stack Size 全局堆栈大小,单位是MAUs,最小的数据存储单元,对于C5000来说,最大存储单元为16位大小的字。
No Dy。。。禁止动态分配存储器。选择后,程序可能不能在任何的DSP/BIOS模块内调用MEM_alloc等分配,释放函数,所有DSP/BIOS对象的数据段,malloc/free函数使用的数据段以及存储动态任务的堆栈段都被设为MEM_NULL
Segment For ...在运行时用XXX_create创建的对象保留的存储段。从堆中分配,如果为MEM_NULL,则无法使用XXX-create函数动态创建DSP/BIOS对象。
Segment free 与上面类似。释放空间。
2 DSP/BIOS数据管理参数设置
与前面类似,后面下拉内容,表示该项被安排在XXX存储器块中。
DSP/BIOS Init Tables(.gblinit)包含了DSP/BIOS全局初始化表的存储段。
TRC....包含了TRC mask 变量和TRC初始值的存储段。
DSP/BIOS Conf Section 配置数据存储段。
3 DSP/BIOS程序代码参数设置
BIOS NORPTB Secion 存储DSP/BIOS代码的存储段。当使用far模型时,DSP/BIOS代码一定要放在重叠页上,也就是片内存储器。
只有把全局设置对话框中的CALL模型设置为Far 该选项才可见,该存储段必须放过在程序存储器中地址0x0000和0x7fff之间。
4 DSP/BIOS用户应用程序内存管理参数设置
5 代码段和数据段装入地址管理参数设置
6 其他基础类的模块:全局设置GBL
7 其他基础类的模块:C5000系列专用模块C54X
8 其他基础类的模块:C6000系列专用模块C62X和C64X
9 通信类模块:实时数据交换RTDX
RTDX Mode 选择主机与DSP目标系统的连接模式,默认采用JTAG,即使用JTAG调试电缆连接。要真正实现RTDX,目标系统和主机之间就必须使用JTAG仿真器。若没有实际的目标系统,则使用软仿真环境时可以使用仿真模式。如果该属性设置不正确,则在装载程序时就会跳出一条消息:RTDX目标应用与仿真协议不匹配。
10 统计类模块:STS模块
STS模块为每一个统计对象定义了一个数据结构,该数据结构中包含了三个数据
Count 统计数据的计数值
Total 统计数据的综合
Maximum 统计数据的最大值
11 统计类模块:TRC模块
TRC模块管理一系列统计跟踪打开或关闭的控制位。这些控制位能通过时间日志和统计累加器完成对程序信息的实时捕捉。为了提高效率。除非打开相应的跟踪统计功能,DSP/BIOS目标应用程序不会存储这些事件日志和统计信息。