Cortex-M3(5) 移植uCOSII相关的汇编代码分析

参考:https://blog.csdn.net/qq504196282/article/details/52997251?locationNum=13&fps=1

 

文件os_cpu_a.asm跟处理器的汇编级编程密切相关,该文件时移植的重点文件

一变量引用

       IMPORT  OSRunning            

       IMPORT  OSPrioCur

       IMPORT  OSPrioHighRdy

       IMPORT  OSTCBCur

       IMPORT  OSTCBHighRdy

       IMPORT  OSIntNesting

       IMPORT  OSIntExit

       IMPORT  OSTaskSwHook

          

       EXPORT  OSStartHighRdy              

       EXPORT  OSCtxSw

       EXPORT  OSIntCtxSw

        EXPORT  OS_CPU_SR_Save                                      ;

        EXPORT OS_CPU_SR_Restore   

/*****************PendV_Handler修改过***********************/  

       EXPORT  PendSV_Handler

使用IMPORT定义的表示这个是外部标号,不是在本文件中定义的,EXPORT表示这些标号是在本文件中定义,可供外部其他文件调用

EXPORT关键字跟编译器有关,能被keil识别,不能被IAR识别

二 常量定义

/*********中断控制及状态寄存器ICSR的地址*******************/

NVIC_INT_CTRL       EQU     0xE000ED04 

/************系统优先级寄存器地址***************************/

NVIC_SYSPRI2         EQU     0xE000ED20 

/************PendSV中断和系统节拍中断**********************/

NVIC_PENDSV_PRI    EQU     0xFFFF0000 

/************触发软件中断的值*******************************/                                        

NVIC_PENDSVSET    EQU     0x10000000 

EQU指令类似于c语言的#define,

这里涉及到中断控制及状态寄存器NVIC_INT_CTRL,系统异常优先级寄存器NVIC_SYSPR12,以及PendSv中断。

补充产生中断后,MCU的具体行为:

当CM3开始响应一个中断时,会在它看不见的体内奔涌起三股暗流:

 入栈: 把8个寄存器的值压入栈

取向量:从向量表中找出对应的服务程序入口地址

 选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC

入栈

响应异常的第一个行动,就是自动保存现场的必要部分:依次把xPSR, PC, LR, R12以及

R3‐R0由硬件自动压入适当的堆栈中:如果当响应异常时,当前的代码正在使用PSP,则压入

PSP,即使用线程堆栈;否则压入MSP,使用主堆栈。一旦进入了服务例程,就将一直使用

主堆栈。

假设入栈开始时,SP的值为N,则在入栈后,堆栈内部的变化如表9.1表示。又因为AHB

接口上的流水线操作本质,地址和数据都在经过一个流水线周期之后才进入。另外,这种入

栈在机器的内部,并不是严格按堆栈操作的顺序的——但是机器会保证:正确的寄存器将被

保存到正确的位置,如图9.1和表9.1的第3列所示。

取向量

当数据总线(系统总线)正在为入栈操作而忙得团团转时,指令总线(I‐Code总线)可

不是凉快地坐着看热闹——它正在为响应中断紧张有序地执行另一项重要的任务:从向量表

中找出正确的异常向量,然后在服务程序的入口处预取指。由此可以看到各自都有专用总线

的好处:入栈与取指这两个工作能同时进行。

更新寄存器

在入栈和取向量的工作都完毕之后,执行服务例程之前,还要更新一系列的寄存器:

�� SP:在入栈中会把堆栈指针(PSP或MSP)更新到新的位置。在执行服务例程后,

将由MSP负责对堆栈的访问。

�� PSR:IPSR位段(地处PSR的最低部分)会被更新为新响应的异常编号。

�� PC:在向量取出完毕后,PC将指向服务例程的入口地址,

�� LR:LR的用法将被重新解释,其值也被更新成一种特殊的值,称为“EXC_RETURN”,

并且在异常返回时使用。EXC_RETURN的二进制值除了最低4位外全为1,而其最低4

位则有另外的含义(后面讲到,见表9.3和表9.4)。

以上是在响应异常时通用寄存器的变化。另一方面,在NVIC中,也伴随着更新了与之相

关的若干寄存器。例如,新响应异常的悬起位将被清除,同时其活动位将被置位。

异常返回

当异常服务例程执行完毕后,需要很正式地做一个“异常返回”动作序列,从而恢复先

前的系统状态,才能使被中断的程序得以继续执行。从形式上看,有3种途径可以触发异常

返回序列,如表9.2所示;不管使用哪一种,都需要用到先前储的LR的值。

有些处理器使用特殊的返回指令来标示中断返回,例如8051就使用reti。但是在CM3中,

是通过把EXC_RETURN往PC里写来识别返回动作的。因此,可以使用上述的常规返回指令,

从而为使用C语言编写服务例程扫清了最后的障碍(无需特殊的编译器命令,如__interrupt)。

在启动了中断返回序列后,下述的处理就将进行:

1. 出栈:先前压入栈中的寄存器在这里恢复。内部的出栈顺序与入栈时的相对应,堆栈指

针的值也改回去。

2. 更新NVIC寄存器:伴随着异常的返回,它的活动位也被硬件清除。对于外部中断,倘若

中断输入再次被置为有效,悬起位也将再次置位,新一次的中断响应序列也可随之再次开始。

具体分析ucosii为什么涉及到PendSV中断

现在的cpu都是按照程序指针pc的指向来运行程序的,当ucos进行任务切换时候,如果一个高优先级任务就绪,在进行任务调度切换时,将打断正在运行的低优先级任务,为了保证高优先级任务运行完成后,能够回到低优先级任务被打断处继续运行,需要将断点处的程序指针压入堆栈,当高优先级任务结束后,再从堆栈中将断点程序指针放回程序指针PC中,即可实现任务切换无缝衔接。可现在大部分cpu 对pc寄存器压堆栈,弹堆栈的指令,因此当ucosii进行任务切换时,要考虑迂回操作,实现对pc寄存器的改写。还好目前cpu有中断处理机制,能实现对pc寄存器的改写,当cpu响应到一个中断时,系统会自动将断点指针压入堆栈,而中断返回指令,自动将堆栈中的断点指针弹回到pc寄存器,恢复中断的程序,实现对断点的保护与恢复。

Ucosii使用了这个机制,当需要进行任务切换的时候,通过软指令,触发一次中断,间接的保存和恢复了pc寄存器,当然stm32在响应中断时也会保存与恢复除了pc以往的其他寄存器,但不是全部。Ucosii触发的这个中断即是PendSV中断,任务切换时,只需要通过软指令触发PendSV中断,在PendSV中断服务程序中,保护被中断的任务现场,恢复将要运行的任务现场,同时cpu在响应中断过程中自动改写pc指针,即通过人为触发中断,欺骗cpu改写pc寄存器,完美实现任务切换。

NVIC_INT_CTRL:

       地址为0xE000ED04,改寄存器可设置一个挂起的NMI,设置或清除一个挂起PendSV,设置或清除一个挂起systick,查找挂起异常等功能。对于ucos移植,相关的为该寄存器的bit28,即PENDSVSET,当设置为1,则挂起PendSV,为0则不挂起。因此当需要通过软指令触发一个PendSV中断时,可将NVIC_PENDSVET写入中断控制状态寄存器NVIC_INT_CTRL

 NVIC_SYSPRI2:

系统异常优先级是用来设置系统异常的优先级的,地址范围是0xE000ED81-0xE000ED23.

系统异常优先级寄存器位分布图

跟移植相关的为PendSV中断的优先级,上面移植代码中设定系统异常优先级寄存器NVIC_SYSPRI2 地址为0xE000ED20,设定掩码NVIC_PENDSV_PRI为0xFFFF0000,将掩码写入地址0xE000ED20,即将systick的优先级与PendSV的优先级均设定为0xFF,即设定优先级最低。

三开关中断

以上两段代码为开关中断的程序段,OS_CPU_SR_Save段为关闭中断,并返回PRIMASK寄存器数值的程序段。OS_CPU_SR_Restore段为将堆栈中保存的中断开关状态回写到PRIMASK寄存器中。

调用此函数之前,必须先定义局部变量cpu_sr,进入中断之前保存CPU状态,退出时恢复状态

四 调度器

OSStartHighRdy程序段被ucos的OSStart调用,实现运行第一个优先级最高的任务,前提是任务已创建。

程序首先设定PendSV优先级,设定PSP堆栈指针,设定系统运行标志OSRunning

然后通过软指令触发PendSV中断,在PendSV中断服务程序中实现任务现场保护与切换,即完成任务调度,开始运行第一个程序。

第一段:设定systick优先级与PendSV优先级设定均为0xFF,即设定优先级最低。

第二段:设定堆栈指针PSP为0,因为此处是系统运行的第一个程序,在PendSV中断服务程序中不需要保持上一个任务的运行环境(没有上一个任务),在这里设定PSP为0,是通知PendSV中断服务程序不需要保存上一个任务的运行环境。在PendSV中断服务程序会判断PSP的值,后面会分析。

第三段:设定系统运行标志OSRunning为运行标志

第四段:通过设定中断控制状态寄存器NVIC_INT_CTRL实现触发PendSV中断,然后在PendSV中断服务程序中实现任务切换。在退出中断服务程序时,系统会自动将第一个任务的指针写入PC寄存器,开始运行第一个任务代码

第五段:中断使能,触发PendSV中断

 

五任务级上下切换

OSCtxSw是任务级的上下文切换程序段,在os_cpu.h中的OS_TASK_SW()宏封装的即是OSCtxSw程序段

OSCtxSw程序段只是简单的通过设定中断控制状态寄存器实现触发PendSV中断,然后返回。OSCtxSw中设定触发PendSv中断时,PendSV中断服务程序不会立即运行,因为在调用OS_TASK_SW()时,中断为关闭状态,当使能中断后,PendSV中断服务程序才会有机会运行。

  一般是OS_Sched函数调用OS_TASK_SW()宏

六中断级上下切换

OSIntCtxSw是中断级的上下文切换程序段,当一个中断服务程序结束后,OSIntExit()查看是否还有优先级更高的程序就绪,若有则调用OSIntCtxSw程序段进行任务切换,OSIntCtxSw也只是简单的通过设定中断控制状态寄存器实现触发PendSV中断,然后返回。

 OSIntCtxSw与OSCtxSw程序代码一样,但意义不一样。OSCtxSw是任务级切换(如任务A因为等待某个资源或是做延时切换到任务B),OSIntCtxSw是中断级切换(中断退出时,由中断状态切换到另一个任务),此时,被中断打断的任务运行环境已经缓存至堆栈中,因此在从中断切换到新的任务时,无需重新将被中断打断的任务的运行环境入堆栈

 七 PendSV中断

 

中断服务程序由汇编语言实现上下任务切换。CM3架构处理器在响应中断时自动将xPSR,PC,LR,R12,R3,R2,R1,R0这8个寄存器压入堆栈,在退出中断时,自动将这8个寄存器弹出堆栈,其余R4-R11寄存器就要程序压入堆栈。因此PendSV中断服务程序需要将R4-R11寄存器压入堆栈以及弹出堆栈

方便理解,看一下PendSV_Handler的伪代码

首先判断PSP堆栈指针是否为NULL,若PSP为0,说明将运行系统第一个任务,无需缓存R4-R11寄存器,否则要缓存R4-R11寄存器(即前一个运行任务的运行环境),前一运行任务的xPSR,PC,LR,R12,R3,R2,R1,R0已结在响应PendSV中断时自动压入堆栈,将前一任务的堆栈指针存入任务控制块中。

然后调用任务切换钩子函数OSTsakSwHook()

再然后设定OSPrioCur为当前就绪任务中优先级最高的任务优先级,设定OSTCBCur指向当前就绪任务中优先级最高的任务的控制块。设定PSP指向当前就绪任务中优先级最高的任务的堆栈

然后从当前就绪任务中优先级最高的任务的堆栈中弹出R4-R11寄存器,即恢复运行缓存。退出中断,处理器自动将xPSR,PC,LR,R12,R3,R2,R1,R0从当前就绪任务中优先级最高的任务堆栈中弹出,即实现任务上下文切换

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值