这些是关于DSP/BIOS的笔记,注意是针对CCS3.0和DSP/BIOS 5.31的
1、 新建的platform必须存为platforms.tci而不是帮助文档里说的某个特殊的名字
2、 线程与中断(包括软、硬中断)之间不能使用semaphores来交互的传递信息,因为在中断中不能调用SEM_PEND这个函数;任务(task)线程若想终止中断函数的运行,必须调用HWI_enable、HWI_disable、SWI_enable、SWI_disable这些函数。
3、 软中断和任务(task)的主要区别是,软中断不能暂停运行来等待某个条件达到要求再继续运行。当软中断被优先级更高的中断打断时,它将停止运行并在需要时重新运行;而任务通常是在循环中运行并允许挂起。
4、 软中断由相关函数触发,例如SWI_post()。
5、 不能调用DSP/BIOS定义的MOD_F_这一类型的函数,例如CLK_F_isr,因为他们仅仅能作为定义的变量。
6、 用户不能定义MOD_ 、MOD_F开头的变量和函数名,这是系统保留的。
7、 在程序中使用DSP/BIOS定义的模块(module)的对象(object),要使用extern来说明,例如:extern LOG_obj trace
8、 在程序中必须包含<program>.cfg.h这个头文件,这是由DSP/BIOS自动产生的相关C头文件。
9、 静态定义的对象(object)不能使用XXX_delete()这样的函数来清除。
10、 绝大多数XXX_create()函数接收其被调用时的最后一个参数XXX_Attrs(注意字母的大小写)作为其新定义的对象的属性,当其为空时则会按照默认属性进行设置。
11、 可以在工程中的汇编文件中使用一个_main函数来代替c文件中的mian()函数。
12、 -u _symbol(例如 –u _malloc)通知连接器使用DSP/BIOS库而不是运行支持库(run time library)中的该函数。
13、 汇编语言中,.asg与.set用法相似,二者区别是.set是将一个变量地定义为常数(并且不能再重新定义),而.asg是将一个字符串定(包括常数)义为一个替代它的变量。
14、 由于main()函数没有使能中断,所以在这里可以单独使能某些中断,但不能使用HWI_enable()来使能全部中断。
15、 对于C5500系列DSP,允许软件通过IVPD和IVPH两个寄存器重新编写中断向量表的起始地址。如果要这样做,必须在硬件启动(hardware reset)之后将合适的地址值写入这两个寄存器然后进行软件启动(software reset)。
1、 对于C5500系列DSP,有三种栈模式(stack mode)可供选择:C54X_TSK(默认模式)、USE_RETA、NO_RETA。若不使用默认模式则必须通过CCS的编译工具正确的配置启动向量(reset vector)的第28、29位。什么时候使用哪种模式?
2、 可以-x来迫使连接器(linker)重新查找库文件来找到第一次未能找到的函数。例如,C++中的new()和delete()函数不被DSP/BIOS库所支持,在使用的时候可能产生一个连接错误,这时可以使用-x选项来避免这个错误。
3、 对于C55x和C6x系列DSP,所有的被DSP/BIOS调用的用户函数都要遵守其相关的C编译器的寄存器规则,无论是使用C还是汇编来编写这些函数。
4、 由于main()函数执行的时候DSP/BIOS还没有完全初始化完成,所以,在main()函数里有相当一部分DSP/BIOS API函数不能调用。
其中,DSP/BIOS的初始化是由DSP_init和DSP_start两部分组成的,在运行main()函数之前运行DSP_init,在之后运行DSP_start。
所以,在DSP_start当中进行初始化的模块,像中断、定时器、调度(schdulers)等模块的相关函数就不能在main()中使用;而内存管理是在DSP_init中进行初始化的则可以,例如,动态分配内存(MEM_alloc、MEM_free)、动态生成和删除模块对象(TSK_create、TSK_delete)。
5、 可以通过不使能GBL模块的全局属性“Enable Real Time Analysis”来优化代码量和执行速度。
6、 LOG对象的logtype属性分为:fixed(固定)和circular(循环)两种,其中,fixed表示当LOG对象的buffer存储满了数据以后则停止继续存储;circular表示当LOG对象的buffer存储满了以后自动覆盖前面的内容,结果就是fixed对象所存储的数据是第一次的数据而circular对象存储的数据是最后一次对象的数据。
7、 LOG对象的buffer大小可以根据使用需要进行修改。例如,使用LOG_printf若是显示足够快,则将LOG对象buffer改小以后不会导致显示左面的标号丢失则可以适当改小buffer。
8、 在使用DSP/BIOS架构的DSP工程中,CPU在工作时执行的是以下这些操作中的一种:
1) 硬件中断服务程序
2) 软件中断服务或周期函数程序
3) 任务(task)函数
4) 在idle循环时的用户定义的函数
5) 通过HST通道向主机传输数据
6) 将实时分析数据传送到DSP/BIOS分析工具中
1、 可以通过给一个硬件中断定义一个监视变量来跟踪它被触发的次数。DSP/BIOS会给每一个硬件中断自动定义一个STS对象来。
2、 除了观察硬件中断和堆栈指针以外,还可以在硬件中断时观察任何一个寄存器和变量的情况。静态对象在每次硬中断时都会更新,这会耗费20到30个指令周期。
3、 观察分配的堆栈是否超出的方法:
1) 在任一个硬件中断函数设置中,使能implicit instrumentation。
2) 在HWI模块中选中该中断对应的对象,设置其属性为STS_delta(*addr)。
3) 在STS模块中找到该中断相关的对象,设置其“prev”属性为“0xBEEF”(5000和2800系列DSP)。
4) 载入并运行程序,使用Statistics View察看属性的改变。
5) 任何超出堆栈的情况发生都会导致该对象出现非零值。
4、 可以通过设置来观察定时器中断的延迟,即中断触发标志使能到第一条中断指令之间的执行时间,具体设置见SPRU423F P84,5500系列DSP由于其定时器的访问不在数据存储空间,所以不能观察。
5、 Kernel Object View不会通过idle线程自动更新,必须在断点的时候或通过右键刷新来更新其内容。
6、 Kernel Object View中的MEM选项主要是显示heap的各种属性。SPRU423F P93
7、 MADU:minimum addressable data unit,对于C5000系列DSP来说是16bit的word。
8、 基于DSP/BIOS架构的DSP软件系统包括4中线程:硬件中断(HWI,包括时钟函数)、软件中断(SWI,包括周期(PRD)函数)、任务(TASK)、后台函数(IDL)。
9、 软件中断应当用于管理100ms或需要更多时间的事件。
10、 各种线程的选择规则:
1) HWI应该用于响应函数时间小于5ms,并且若不及时响应,其相关数据就有可能丢失的情况;SWI和TSK应当用于响应函数时间为100ms左右的情况。
2) 对于线程之间关系简单的使用SWI,较复杂的使用TSK。TSK可以被挂起而SWI不能;SWI由于使用单独的堆栈相对TSK更有内存使用的效率。SWI在运行之前,其使用到的数据必须已经准备好,可以通过SWI对象的邮箱来通知它。
3) IDL函数在系统不使用CPU的时候运行。
4) CLK函数直接使用timer中断触发。基本的CLK函数:PRD_clock为周期函数产生一个tick,用户可以增加运行在相同频率的时钟函数。由于这些函数时作为HWI来处理的,所以必须尽量减少运行时间。
PRD函数作为SWI处理,所有的PRD函数拥有相同的SWI优先级
1、 可以通过HWI_disable、SWI_disable、TSK_disable等函数禁止这些线程的运行,并且当当前最高优先级的线程是Task时,这个Task如果被阻塞(block),那么将禁止其他的线程的运行。
2、 在使用HWI对象时,若其中断处理函数使用C语言来编写则一定不能使用interrupt关键字或INTERRUPT pragma,因为HWI对象调用的函数已经包含了这些功能。
3、 在SWI和TSK中可以通过调用HWI_disable()和HWI_enable()/HWI_restore()来禁止和使能HWI中断函数。这些函数是通过操作INTM位来控制全局中断。HWI_enable()函数是使能全局中断,HWI_restore()函数是将调用HWI_disable()之前的值装载进去。
4、 HWI产生时,需要在执行其ISR之前将它所用到的可能改变的寄存器保护起来,返回时要将这些值填写回去。DSP/BIOS提供HWI_enter和HWI_exit宏来完成这两种功能。
在使用C语言编写ISR程序时,可以通过DSP/BIOS设置其HWI对象的属性使用“HWI dispatcher”自动地完成这两个步骤,也可以在自己的程序中调用HWI_enter和HWI_exit宏来完成这两个功能。
5、 在DSP/BIOS架构下,若在处理中断时不使用HWI dispatcher则应该调用中断处理函数前后调用HWI_enter和HWI_exit进行相关处理。(SPRU423F example 4-3、4-4、4-5)
6、 软中断(SWI)和基于一些类型的SWI指令是不一样的,DSP/BIOS的SWI中断是与DSP的类型无关的。SWI中断是由类似SWI_post这样的函数触发的。SWI适合用于那些发生频率较低的或对于实时性要求低于HWI中断的任务。
1、 SWI可以由:SWI_andn
SWI_dec
SWI_inc
SWI_or
SWI_post
这些函数触发。
2、 可以在HWI中触发一个SWI,这个SWI要么处于HWI_enter和HWI_exit之间,要么由HWI_dispather调用。
3、 每一个SWI可以有两个变量。
4、 动态定义SWI(调用SWI_create函数)只能是在task这一级,而不能在HWI和SWI中。
5、 SWI的优先级0级最低,14级最高。
6、 系统堆栈(system stack)保存了每一次SWI发生时需要保存的寄存器内容,每增加一级SWI优先级就会在其中分配一部分内存,所以,内存效率最高的用法是将所有的SWI设定为同一个优先级,不过这是不可能的,所以应该尽量设置少的SWI优先级来保证减少system stack的大小。
默认的system stack的大小是256words,可以通过设置.tcf文件中MEM的属性(点右键)改变,system stack的估计大小会显示在.tcf文件顶部的状态栏中。
7、 如果TSK管理器被使能,则在SWI模块中会有KNL_swi对象来管理task,而KNL_swi的SWI优先级为最低:0级,其他的SWI对象不能被设置为这个最低的优先级(0级)。
8、 相同优先级的SWI的运行顺序是不能设置的。
9、 当一个SWI的ISR程序从响应列表中移出的时候,它的mailbox的值会被重置为初始值(即在.tcf中设置的值);如果一个SWI的ISR程序在运行的时候它被触发,则该SWI的mailbox的值会被设置成这次触发相关的值,但不会影响当前运行的程序(包括在其中调用SWI_getmbox()时的返回值)。
这就是说在一个SWI的ISR程序没有完成时也能更新它的mailbox的值。
1、 触发SWI的API函数有不同的功能:
1)如果SWI的ISR函数不论其是否在运行都必须对每次触发都响应,则使用 SWI_inc函数。
2) SWI_andn(&swiHandle, data)可以检验其参数的data的某些位是否为“1”,是的话则运行ISR函数。
3) SWI_or(&swiHandle, data)直接触发ISR函数,在ISR程序中调用SWI_getmbox()函数得到data值,根据data的具体数值不同可以再使用switch-case语句运行不同的程序分支,注意,此前要在设置中将该SWI属性中的mailbox参数设置为0,否则不能运行正确的程序分支。
4) 如果需要设置某一事件多次发生才触发一次SWI的ISR函数则可以调用 SWI_dec()函数,这时,在SWI的该模块将其“mailbox”属性设置为需要重复的次数,每调用一次SWI_dec()函数则该数字减少1,当减少到0的时候,运行ISR函数。
2、使用SWI的好处:
1)可以在所有HWI使能的时候运行。
例如,在一个HWI中定义一个共享数据结构,如要在一个task中改变它则要先禁止该HWI,这样将会影响到系统的实时性;如果是在一个SWI中定义这个结构则不会存在这种问题。通常是将一个ISR分为两个部分,需要严格遵守时间限制的放在HWI中,不那么严格的放在SWI中。
2) SWI可以调用一些函数而HWI不能。
因为在DSP/BIOS更新其内部数据结构时,SWI是不会运行的。必须对限制哪些函数能在HWI中调用,哪些只能在SWI中调用非常熟悉。Functions Callable by Tasks, SWI Handlers, or Hardware ISRs in the TMS320 DSP/BIOS API Reference Guide for your platform that lists DSP/BIOS functions and the threads from which each function can be called.
SWI可以调用任何不会被阻塞的API函数,则任何能够被阻塞的函数和调用这类函数的函数都不能被SWI调用。
3、当一个SWI中断了另一个线程的时候,DSP/BIOS会自动保存绝大部分的寄存器的值,所以不论是使用C或是汇编来编写ISR程序都不必再保存这些寄存器,当然,最保险的方法是在使用汇编的时候再将没有保存的寄存器保存一下(对C54x来说是ar1、ar6和ar7)。对于HWI来说,必须使用HWI_enter和HWI_exit或是HWI dispatcher来保存这些值。
Note:
An important side effect of SWI_disable is that task preemption is also
disabled. This is because DSP/BIOS uses software interrupts internally to
manage semaphores and clock ticks.
4、每一个task都有自己的堆栈来保存其局部变量。可以在对象属性中具体规定其堆栈的大小。
5、包含有semaphore、mailbox等资源的task不能被释放,否则会导致错误。应该先释放这些资源再释放包含它们的task。
6、对于C55x DSP/BIOS将分配给task的内存平均分给系统和用户。
7、如果希望一个task初始化以后不执行,等到需要时再运行,可以将它的优先级设置为-1,再在程序中进行改变。
8、当一个task处于terminate态以后就不再运行。
9、一个task当其分配的堆栈不够大时将占用别的task的部分,这可能导致致命的错误,所以可以使用TSK_ceckstacks和TSK_stat来测试task的堆栈大小