《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 设计应用 > MPC5604B处理器μC/OS-II的移植及优化
MPC5604B处理器μC/OS-II的移植及优化
来源:电子技术应用2012年第11期
谢乐寅,胡凤鉴,罗 峰
1.同济大学 汽车学院,上海201804; 2.同济大学 新能源汽车工程中心,上海201804; 3.同济大学飞思卡尔汽车电子联合实验室,上海201804
摘要: 介绍了飞思卡尔MPC5604B微控制器的硬件系统资源、结合硬件资源的μC/OS-II操作系统的移植内容以及采用PowerPC内核硬件指令对μC/OS-II操作系统任务调度算法进行优化,以提高程序执行效率的方法。实验表明,本方法适用于飞思卡尔所有采用e200z0内核的微控制器。
中图分类号: TP316.84
文献标识码: A
文章编号: 0258-7998(2012)11-0022-04
μC/OS-II porting and optimization for MPC5604B microcontroller
Xie Yueyin,Hu Fengjian,Luo Feng
1.School of Automotive Studies, Tongji University, Shanghai 201804,China; 2.Clean Energy Automotive Engineering Center, Tongji University, Shanghai 201804,China; 3.Tongji University & Freescale Automotive Electronics Laboratory, Shanghai 201804,China
Abstract: This paper introduces presenting an effective method which utilizes PowerPC core software instructions to optimize the task scheduling algorithm of μC/OS-II operating system, hardware sources of MPC5604B and the corresponding porting details of μC/OS-II operating system. The test result shows that the method could be used by all Freescale microcontrollers which utilize e200z0 as their cores.
Key words : μC/OS-II;MPC5604B;code porting;optimization

    基于实时操作系统RTOS和C语言的开发,具有良好的可继承性,在处理器升级以及更换处理器类型时,只需要在系统底层做相应的移植,大部分应用程序代码可以不做修改就可以移植过来使用。μC/OS-II是一个完整的、可移植、可固化、可裁剪的占先式实时多任务内核。μC/OS-II绝大部分代码采用ANSI的C语言编写,包含一部分汇编代码,使之可供不同架构的微处理器使用。μC/OS-II运行稳定可靠,通过了美国航空管理局的认证,可以应用到汽车、飞机等安全性要求严格的场合[1]。

1 MPC5604B硬件资源
1.1 MCU基本参数

    MPC5604B是飞思卡尔公司基于PowerPC架构针对车身控制领域而设计的新型处理器。该处理器采用e200z0核,时钟频率可达64 MHz,内核集成有中断向量控制器和存储保护模块。存储模块包括512 KB的程序Flash、64 KB的数据Flash、48 KB的SRAM,而且都具有错误检查和纠正(ECC)功能。通信接口包括3路FlexCAN、4路LINFlex、3路SPI以及1路IIC。时钟模块包括2路eMIOS。此外,还有36通道的ADC模块以及启动辅助模块[2-3](BAM)等。
1.2 e200z0指令模型
    PowerPC架构Book E中定义了固定32 bit长度的指令集,但是一些指令不需要32 bit长度,16 bit长度就可以执行同样的操作,所以e200z0内核采用可变长度编码(VLE)指令集。该指令集是32 bit和16 bit指令长度混合指令集,相比于Book E中固定长度指令集,VLE指令集代码密度更高,节省了存储空间。
    在汇编代码形式上,16 bit VLE指令集在汇编指令前加se前缀,32 bit VLE指令集在汇编指令前加e前缀。以stw指令为例,VLE指令和Book E 32 bit指令区别如表1所示。

    软件向量模式下,外围设备的所有中断最终都只响应一个中断,即偏移为0x0040的外围设备中断(IVOR4)。当外围设备发生中断后,执行IVOR4中断处理函数,该函数读取INTC_IACKR寄存器确定具体该处理哪一个外围设备的中断函数。软件向量模式的好处是所有的外设中断只需要写一个上下文保存和切换函数,节省了用户的代码编写量和存储空间。
    硬件向量模式就是在表2所示的中断向量表后面距离中断向量基址寄存器(IVPR)偏移0x800位置再添加了一个外围设备中断向量表。当发生外设中断时,寄存器直接跳转到相应的外设中断处理函数。硬件向量模式其中断响应更迅速,但每一个中断处理函数都需要有单独的上下文保存和切换函数,其代码量很大。
2 μC/OS-II调度原理及软件实现
2.1 原理及移植

    μC/OS-II实时操作系统的工作原理是:最大程度地让处于就绪状态的最高优先级任务处于运行状态。在μC/OS-II中,主要通过如下三方面的机制来实现实时性[4]。
    (1)用户主动调用API函数。在用户当前任务执行到一定步骤,比如已经执行结束或者需要暂停挂起当前任务时,调用系统函数OSSemPost()、OSTimeDly()、OSSemPend()等API函数。在这些函数里,通过调用OS_Sched()函数,如果检测到当前有更高优先级别的任务处于就绪状态,则调用OS_TASK_SW()函数切换到更高优先级的任务执行。
    (2)通过系统时钟(Systick)进行调度。每隔一个系统时钟间隔进行一次任务就绪状态检测,如果检测到有更高优先级的任务处于就绪状态,则执行任务切换。系统时钟间隔选择要合适,太长会影响系统实时性,太短会造成系统花费过多的资源处理定时器中断。
    (3)通过外部中断程序触发任务切换。当发生了外部中断,造成系统任务就绪状态的变化、退出中断处理函数时,调用OSIntExit()函数。在这个函数里,如果检测到更高优先级任务处于就绪状态,则调用OSIntCtxSw()函数执行任务的切换。
    通过上面三种机制实现了系统最大程度的实时性调度。系统移植主要的任务是:结合具体的硬件平台,实现任务堆栈的建立,任务调度过程中上下文的保存和切换以及系统时钟的实现。主要包括OS_CPU.h、OS_CPU_C.c以及OS_CPU_A.s几个移植文件。
2.2 实现OS_TASK_SW()函数
    OS_TASK_SW()函数用于实现任务级别的任务切换。根据前面介绍的MPC5604B异常向量表,采用位于中断向量表偏移地址为0x0080(IVOR8)的系统调用(system call)异常来实现该函数,程序如下:
    #define OS_TASK_SW()   asm(“se_sc”);
2.3 实现OSTaskInit()函数
    OSTaskInit()函数在任务创建时被调用,为新创建的任务在RAM中申请一块栈区,并把堆栈指针传递给该任务的控制模块TCB中。在程序执行中,需要保存的寄存器有:通用寄存器R0~R30、器件模式寄存器MSR、连接寄存器LR、计数寄存器CTR、存储/恢复寄存器对SRR0/SRR1、整型异常寄存器EXR。PowerPC架构中没有专门的堆栈指针寄存器,采用通用寄存器R1作为堆栈指针寄存器。
2.4 任务上下文保存和恢复函数
    μC/OS-II中,执行OSStxSw、OSIntCtxSw、OSStartHighRdy等任务切换函数时,需要保存任务上下文prologue或者恢复任务上下文epilogue。执行这些操作,要对CPU内部寄存器进行直接访问。C语言不能实现对这些寄存器的直接访问,需要通过汇编代码来执行这些与处理器直接相关的操作。下面是保存任务上下文prologue的汇编实现代码:
    prologue:       .macro
               e_add2i.r1,-STACK_FRAME_SIZE
               e_stwu     r1,0(r1)
               e_stw     r0,4(r1)
               ……
               e_stw     r31,4*31(r1)
               mfmsr     r0
               e_stw     r0,    XMSR(r1)
               ……
               mfcr     r0
               e_stw     r0,    152(r1)
               mfmsr     r0
    epilogue函数执行与prologue相反的操作,即把任务堆栈中存储的数据恢复到寄存器中。
2.5 系统时钟函数OSTickISR
    OSTickISR()函数是系统时钟处理函数,主要目的是让MCU每隔一定的时间去执行OSTimeTick()函数。MPC5604B所采用的e200z0内核没有专门的系统时钟(Systick)模块,因此选择外设中的周期时钟通道0(PIT0)作为系统时钟基准。处理函数只需要完成如下两项任务:调用OSTimeTick()函数和清除通道中断标志即可。实现代码如下:
    void Pit1ISR(void) {
      OSTimeTick ();
      PIT.CH[1].TFLG.B.TIF = 1;
    }
    这里中断控制器(INTC)采用软件向量模式。下面介绍如何配置软件向量工作模式的PIT0中断。
    (1)在链接命令文件(lcf)中申请一块区域存储中断向量表,链接脚本如下:
    MEMORY
    { ……
    interrupts_flash: org = 0x00010000, len = 0x00010000
    ……}
    GROUP :
    { ……
    .ivor_branch_table(VLECODE)LOAD(ADDR(interrupts_flash)) : {}
    ……  } > interrupts_flash
    (2)将OSExtIntISR函数添加到中断向量表中,代码如下:
    .extern OSExtIntISR
    ……
    .section .ivor_branch_table, text_vle
    ……
    .align SIXTEEN_BYTES
    IVOR4trap:  e_b      OSExtIntISR
    ……
    OSExtIntISR函数中,通过读取INTC_IACKR寄存器,查询当前具体是哪一个外设处理函数需要被执行。而相应的外设函数指针存放在一个数组中,初始化中断时,要把该数组赋值给INTC_IACKR寄存器,代码如下:
    INTC.IACKR.R=(uint32_t)&IntcVectorTable[0];
    uint32_t IntcVectorTable[] = {
    (uint32_t)&dummy……
    (uint32_t)&Pit1ISR,
    ……}
外设处理函数在数组中的位置根据其中断编号决定。
    以上介绍的是与MPC5604B处理器直接相关的代码,其他移植部分代码都有成熟的代码可以参考,这里不作介绍。
3 任务调度算法硬件指令优化
3.1 μC/OS-II最高优先级就绪任务查询原理

 


    μC/OS-II操作系统最初是针对8 bit机写的,采用了一个全局字节变量OSRdyGrp和全局字节数组OSRdyTbl[8]实现了对64个任务就绪状态的管理。OSRdyTbl[8]数组共64 bit,表示64个优先级的任务的就绪状态,如果为1,则表示该优先级的任务已经就绪。这64个任务,从0~63分成8组,OSRdyGrp字节的每一位,就代表OSRdyTbl[8]哪一组中有任务处于就绪状态。优先级就绪表如图1所示。
    操作系统查询最高优先级就绪任务的步骤是:先查询OSRdyGrp字节中为1的最低位。例如,如果OSRdyGrp字节为0x1C(00011100B),则处于就绪状态的优先级最高的组的索引值为Y=2;然后查询OSRdyTbl[2]中字节为1的最低位,这里假设OSRdyTbl[2]为0x12(00010010B),则该组中优先级最高的位索引值为X=1.从而可以计算出优先级Prio=Y<<3+X=17。
    根据前面的描述,处于就绪状态的任务中最高优先级查询的软件算法代码如下:
    INT8U   y;
    y = OSUnMapTbl[OSRdyGrp];
    OSPrioHighRdy=(INT8U)((y << 3)+OSUnMapTbl
[OSRdyTbl[y]]); [1]
    OSUnMapTbl是一个常数表格,该表格的作用是查询索引OSRdyTbl[y]对应的字节为1的所有位中的最低位所在位置。例如,如果OSRdyTbl[y]=12,则OSUnMapTbl[12]=2。这种查表法通过空间换时间,避免了软件逐位判断的时间开销。
3.2 基于MPC5604B硬件指令的优化
    在PowerPC e200z0内核指令中,有一条指令cntlzw(Count Leading Zeros Word):其前导0计数指令,该指令在32 bit e200z0内核处理器中用于查询一个32 bit数从高位开始的0的数量。比如内核寄存r5中存储了0x02000000的值,执行指令(cntlzw r3,r5)后,r3中得到的数值为6,这与操作系统任务调度过程中查询最高优先级就绪任务的软件算法要实现的目的是一致的。
    以上介绍的软件调度算法,其实就是查询OSRdyTbl[8]数组共64 bit中为1的最低位的位置。在e200z0内核运算中,可以把就绪表拆成两个32 bit数,通过执行一次或者两次cntlzw指令就能确定就绪的最高优先级任务所在的位置。
    为了保证操作系统的实时性,&mu;C/OS-II操作系统会频繁地进行查询就绪任务状态表。每一次硬件中断,用户调用的任务调度API函数以及系统时钟Tick函数都会执行就绪状态表查询函数。如果采用硬件指令替换软件调度算法,将大大提升系统任务调度的性能。
3.3 硬件指令的优化实现
    所谓任务调度函数硬件优化就是采用cntlzw硬件指令取代软件算法的任务调度查询函数。要实现该优化过程,需要完成下面两个步骤。
3.3.1 重写函数OS_SchedNew
    MPC5604B所采用的是e200z0内核为32 bit指令长度,要查询64 bit的长度,需要分成2次。先查询低位32 bit,如果查询到有就绪位置位,则把结果赋值给全局变量OSRdyTbl并退出函数;如果低32 bit没有查询到就绪位,则用同样方法查询高32 bit长度,并把查询结果加上32后赋给OSRdyTbl。具体的实现代码如下:
    static asm void OS_SchedNew(void)
    {  e_lis r9,OSRdyTbl@ha
       ori r9, r9,OSRdyTbl@l
       e_lwz r8,0(r9)
       cntlzw r8,r8
       e_cmpi 0,0,r8,32
       se_bne  __HighWord
       e_lwz  r8,4(r9)
       cntlzw r8,r8
       e_addi r8,r8,32
       __HighWord:
       e_stb r8,OSPrioHighRdy
       se_blr    }
    在操作系统中,函数OS_EventTaskRdy也用到了就绪最高优先级任务查询。按照上面的代码,把prio= (INT8U)((y<<3)+x)语句用相应的汇编指令替换掉即可,这里不再赘述。
    相比于软件调度算法代码,经过优化的调度函数指令执行时间缩短一半以上,主要省掉了查询调度表OSUnMapTbl[256]。此外,对于处理能力比较强的32 bit机,如果需要扩充&mu;C/OS-II最大任务数量(如1 024个任务数量),则采用硬件指令cntlzw处理任务调度将比较容易完成这个操作。而&mu;C/OS-II最初的任务系统是针对8 bit机写的,直接采用软件算法扩充到支持1 024个任务数会比较麻烦,并要消耗更多的资源。
3.3.2 修改任务控制块初始化OS_TCBInit
    OS_TCBInit函数中有以下代码:
    ptcb->OSTCBBitY=(INT8U)(1<<ptcb->OSTCBY);
    ptcb->OSTCBBitX=(INT8U)(1<<(ptcb->OSTCBX));
    该段代码用于创建任务时,需要计算该任务优先级掩码。但是,该段代码计算出来的掩码却不适用于前面替换的硬件指令(cntlzw)调度函数。其原因分析如下:
    设优先级就绪表OSRdyTbl[0..3]当前的值为{0x00,
0x02,0x00,0x22},则执行了e_lwz r8,0(r9)指令后,r8寄存器中32 bit数值为0x00020022。采用cntlz r8,r8计算前导零值为14。而实际上此时OSRdyTbl代表的就绪最高优先级值为9,差别在于对0x02这个值的解析。按照cntlzw对0x02(00000010B)的解析,其前导零数量为6,则所计算出的优先级为8+6=14;而根据前面的掩码计算公式可知,0x02(00000010B)表示第1组中第1位(均从0开始计算)所代表的优先级为8&times;1+1=9。
    因此,为了适应硬件指令cntlzw,需要修改掩码计算方法。采用了cntlzw指令后,状态就绪组变量OSRdyGrp以及管理该变量的掩码OSTCBBitY已经没有用处,因此,只需要将函数修改为:
    ptcb->OSTCBBitX=(INT8U)(1<<((7-ptcb->OSTCBX))。
    同理,涉及到修改x掩码位的所有函数(OSMutexPend、OSMutex_RdyAtPrio、OSTaskChangePrio)都要做同样的调整。调整过后,系统就可以正常运行了。
    本文详细介绍了基于MPC5604B的&mu;C/OS-II操作系统的移植程序以及操作系统最高优先级就绪任务查询算法的硬件指令优化。在移植过程中,首先要分析硬件系统资源,使用链接命令文件(lcf)分配代码和数据段;然后要熟练掌握e200z0核汇编指令,实现操作系统堆栈维护、中断上下文保存、时钟tick、任务切换等底层函数的编写;最后在系统移植过程中,要善于发现和利用处理器一些特殊的指令,实现软件算法的硬件优化,以提高程序的执行效率。
参考文献
[1] (美)LABROSSE J J,著.嵌入式实时操作系统&mu;C/OS-II (第2版)[M].邵贝贝,译.北京:北京航空航天大学出版社,2003.
[2] SOJA R,BANNOURA M,著.MPC5553/5554微处理器揭秘[M].龚光华,宫辉,安鹏,译.北京:北京航空航天大学出版社,2010.
[3] Freescale Semiconductor,Inc.MPC5604B/C microcontroller reference manual[Z].2011.
[4] 孙旭祥.浅析实时操作系统的任务调度[J].信息对抗,2005(6):37-39.

此内容为AET网站原创,未经授权禁止转载。