1 BootROM及其运行
在F281x、C281x、R281x器件中都有一块4K×16位的BootROM。当引脚MP/nMC的状态反映到XINTCNF2中为0时,BootROM 被映射到地址空间0x3FF000~0x3FFFC0上。片上ROM 在出厂前就已经烧写好了一个启动程序以及一些其他数据和表格(版本信息、复位向量、中断向量表、IQmath表等)。其空间分配如图1所示。
其中,中断向量表在VMAP=1、ENPIE=0(PIE未使能向量表)、MPNMC=0时有效。另外,当VMAP=1、ENPIE=0时.系统将从BootROM 的0x3FFFC0复位。虽然复位后ENPIE为零,但大部分系统都是需要使能外部中断扩展模块的,即用户程序中需要将ENPIE置1。由此可见,BootROM 唯一常用的向量只有复位向量;而其他的中断向量是指向MOSRAM 用于芯片测试的,通常用不到。状态位和向量表映射关系如表1所列。
2 从上电复位到用户代码
当系统重启(上电或热启动)时,引脚XMPNMC的信号将被锁存到XINTF的配置寄存器XINTCNF2中。重启之后,XMPNMC的状态不再反映到XINTCNF2,这时,可以用软件来修改它的状态,从而确定程序要访问的是内部地址还是外部地址。但是,像F2810这样的器件,没有XINTF,它的XMPNMC在芯片内部被拉低,也就是说,当器件重启后,它总是自动从内部的BootROM 启动。如果XMPNMC为高电平,则表示系统将从XINTF zone7中获取复位向量。即从外部获取中断向量(地址见表1)时,必须确保复位向量所指向正确的地址。这一般在希望自己编写启动程序时使用。本文对此不作具体讨论。
当XMPNMC为低电平时,系统从内部获取复位向量。这个复位向量指的就是上文中提到的BootROM 中位于0x3FFFC0的向量。此向量指向固化在BootROM 中的InitBoot函数。所以上电复位后,程序将跳转到InitBoot函数。
InitBoot函数首先对器件初始化,F281X器件将被配置为F28x工作模式。如果希望执行C2xLP兼容程序,则需要用户自己写程序配置。PLL 配置将保持不变。PIE使用缺省状态,即不使能。另外要注意,M1的前80个字将用做BootROM 的堆栈.用户应避免使用。初始化完成后,程序转向执行SelectBootMode函数。此函数将扫描
通用I/O口(GPIO),以确定启动模式,如表2所列,包括跳转到Flash、跳转到H0 SARAM、跳转到OTP等模式。
不同的模式有不同的程序起点(entrypoint)。对于Flash、H0 SARAM、OTP模式,有一个固定的跳转地址(见表2);而对于从SCI、SPI启动时,程序起点将调用bootloader按一定的格式从外部获取。
最后,BOOtROM 执行exitboot函数。执行这个函数包括:置CPU状态为缺省,将SP指向0x400,跳转到程序起点等工作。exitboot执行后CPU 状态为:ACC=0,RPC=0,P= 0,XT=0,ST=0,XAR0=XAR7=0,SP=0x400,ST1=0x0A0B。
以上是BootROM 完成的工作。从entrypoint开始,就进入用户程序区了。对于汇编程序,可以在程序起点处写一条跳转到Start(如果程序起点是Start)的指令。汇编情况比较简单,跳转到Start后,各项初始化代码工作都由自己完成。对于C语言程序,通常的做法是在程序起点处放置一条跳转指令,转到_c_init0。然后程序的执行分为使用或不使用BIOS两种情况。
从rts.src中提取boot28.inc文件,其中包括对于不使用BIOS的情况下,启动后从_c_init0到main函数中间所做的工作。因为这段代码是由C编译器自动运行的,因而常被初学者忽视,以致对其后自己编写的C代码的运行环境不清楚。这一段程序主要完成以下工作:
这段代码声明了2个全局变量:_stack,系统堆栈栈底;_c_int00,启动函数。下面列出了C运行环境的初始化程序部分代码:
C28OBJ ;选择C28x对象模式
C28ADDR ;清除地址模式位
C28MAP ;设置MOM1模式
CLRC PAGE0 ;使用堆栈寻址模式
MOVW DP,#0 ;初始化DP指向低64K地址
CLRC OVM ;关闭溢出模式
ASP ;确保SP对齐
这些代码设置了C语言的运行环境。在用户程序中编写的汇编代码不应该破坏这个环境,否则C语言将无法正常运行。
3 中断代码的执行
F28x系列的DSP支持1个不可屏蔽中断(NMI)和16个可屏蔽中断(INT1~INT14、RTOSINT、DLOGINT)。其中,INT1~INT12由PIE控制单元管理。每个INT可以对应8个外设中断,即PIE可以控制96个中断源。下面对可屏蔽中断响应过程作一介绍:
由于置位了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需要满足以下要求:
移植工作包括以下几个内容:
移植的难点在于实现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数字信号处理器上。
文章评论(0条评论)
登录后参与讨论