3 中断代码的执行
F28x系列的DSP支持1个不可屏蔽中断(NMI)和16个可屏蔽中断(INT1~INT14、RTOSINT、DLOGINT)。其中,INT1~INT12由PIE控制单元管理。每个INT可以对应8个外设中断,即PIE可以控制96个中断源。下面对可屏蔽中断响应过程作一介绍:
- 外设发出中断请求。
- DSP看中断请求是否被允许。设计PIEIER、PIEACK、IER、INTM 等寄存器和标志位的设置。
- 如果中断允许,则先执行完进入解码的二阶段之后的指令,将其他指令冲出流水线。系统将自动保存STO、T、AL、AH 、PL、PH 、AR0、AR1、DP、ST1、DBGSTAT、PC、IER,然后获取中断向量,加载到PC。
注意:当中断被允许后会立即清除IFR 中相应标志位;但是,如果此时中断信号仍有效(保持低电平),那么,相应IFR标志位又会被置位(不过这时此中断不会被立即响应)。这是因为CPU禁止了所有的硬件中断响应,当ISR开始执行时,它才解除禁止;并且,在执行ISR之前(此时原来的IER 已保存),当前中断的IER 中相应位被清零。也就是说,同一中断源的中断不会再被响应,要等到中断服务子程序中用户来使能中断(如果需要嵌套),或者等到中断返回自动恢复IER。 - 执行中断服务子程序。
由于置位了INTM、DBGM,所以可屏蔽中断默认是不被允许的。如果要嵌套,则需程序员自己动手清除禁止中断标志。另外,中断里面LOOP、EALLOW 、IDLESTAT都被清零了,这样中断服务子程序有了一个全新的上下文。
4 从RAM 中执行代码
通常情况下,程序是保存在Flash里面的,CPU从Flash中取指运行;但是,有时会要求将程序调到RAM 中来执行。一方面是为追求更高的速度;另一方面是为了让Flash有最好的运行性能,需要修改Flash的等待状态周期,使能Flash Pipeline,而对Flash的操作必须在RAM 里面执行,这些操作函数就必然要从Flash中调到RAM 中执行。对于这些程序,在启动后用户程序中需要先完成存储器拷贝工作。拷贝到RAM 中之后,才能调用这些函数,顺序不能乱。
5 在DSP上移植实时操作系统
所谓移植,就是使一个实时内核能在某个微处理器或微控制器上运行。在移植软件之前,先要正确配置处理器的运行模式,了解处理器的中断方式、中断向量地址等。这些工作在F28x系列DSP中由BootROM 中固化的程序完成。另外,为了方便移植,大部分的RTOS代码都是用C语言写的;但仍需要用C语言和汇编语言混合编写一些与处理器相关的代码。这是因为在读写处理器寄存器时只能通过汇编语言来实现。
对于同时使用汇编语言和C语言的实时操作系统移植,必须小心使用汇编语言,防止破坏C语言运行环境。一方面不可以改变相关状态位;另一方面汇编函数的编写需要遵循C编译器的调用规则。从复位到用户程序编译
器做的设置工作见前文。
中断发生时,TMS320LF28x处理器自动保存了不少寄存器,但是如果中断服务子程序中要用其他寄存器,那么开始时要自己写现场保护程序。就实时操作系统而言,进入中断和退出中断须对系统堆栈进行现场保护。维护堆栈结构时,需要注意处理器堆栈的生长方向。虽然绝大多数微处理器和微控制器的堆栈是从上往下长的,但TI公司的DSP一般为从下往上长。
一般实时操作系统需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得系统能够保护临界段代码免受多任务或中断服务例程(ISRs)的破坏。最简单的实现方法是直接调用处理器指令来禁止中断和允许中断。
笔者选择了目前应用比较广泛的实时操作系统uc/OS II。要移植uc/OS II需要满足以下要求:
- 处理器的C编译器能产生可重入代码;
- 用C语言就可以打开和关闭中断;
- 处理器支持中断,并且能产生定时中断(通常在10~100 HZ之间);
- 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈;
- 处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈或内存中的指令。
移植工作包括以下几个内容:
- 用#define设置一个常量的值(OS_CPU.H);
- 声明10个数据类型(OS_CPU.H);
- 用#define声明3个宏(OS_CPU.H);
- 用C语言编写6个简单的函数(OS_CPU_C.C);
- 编写4个汇编语言函数(OS_CPU_A.ASM)。
移植的难点在于实现OS_CPU_A.ASM。这个文件的实现需要十分清楚处理器启动过程和中断处理,以及代码的运行过程。由于TI公司的DSP堆栈从下往上长,所以移植时需要置OS_STK_GROWTH为0。可以简单地使用TIMS320I F28x的中断使能和禁止命令来实现OS_ENTER_CRITICAL()、OS_EXIT_CRITICAL()两个宏。
以下是移植时OS_CPU_A.ASM 文件里任务切换的代码。任务切换时,须时刻注意自己设计的堆栈结构。
- _OSCtxSw:
- CALL _CTX_SAVE
- LDPK _OSTCBCur ;OSTCBCur->OSTCBStkPtr = SP
- LAR AR3,_OSTCBCur
- MAR *.AR3
- SAR AR1,*,AR1
- _OSIntCtxSw:
- CALL _OSTaskSwHook ;OSTaskSwHook()
- LDPK _OSTCBHighRdy ;OSTCBCur=OSTCBHighRdy
- BLDD _OSTCBHighRdy,#_OSTCBC ur
- LDPK _OSPrioHighRdy ;OSPrioCur=OSPrioHighRdy
- BLDD _OSPrioHighRdy,#_OSPrioCur
- LDPK _OSTCBHighRdy ;SP=CKSTCBHighRdy->OSTCBStkPtr
- LAR AR3,_OSTCBHighRdy
- MAR *,AR3
- LAR AR1,*
- B _CTX_REST,AR1
结语
本文详细说明了从上电复位开始,DSP中程序运行的过程;分析了固化在片上ROM 的程序以及由编译器自动生成的程序。另外,通过对DSP/BIOS启动、中断执行、从RAM 中执行代码等问题的探讨,了解DSP的运行机制,掌握移植实时操作系统的关键技术知识。笔者成功地将实时操作系统uc/OS II移植到了TMS320LF2812数字信号处理器上。