os_cpu_a.asm

??定位到源码的uCOS-II/Ports/ARM-Cortex-M3/Generic/IAR/os_cpu_a.asm,这是.asm文件,也就是汇编文件。

1. 声明外部变量和导出符号

??EXTERN声明的是变量,该变量来自外部(IMPORT声明的则是函数,表示该函数来自外部)。
??EXPORT表示将该符号从本文件中导出,可供外部调用。

2. 内核异常相关寄存器地址定义

3. 堆栈分段对齐相关

4. 开关中断源函数实现

??OS_CPU_SR_Save和OS_CPU_SR_Restore在前面的os_cpu.h文件中出现过:

用于实现临界区的保护。

5. 任务的切换

??任务切换是uCOS-II的核心之一。所谓的任务切换,是指从原来的任务中离开,转去执行新的任务。而切换的实现核心是:保存上下文,恢复将要去执行的任务的上下文件,跳转到新的任务中去执行。问题的关键在于,os不能够简单粗暴的跳转到新任务中去执行,所以Cortex-M3内核中PendSV机制来实现任务的切换。

??要了解什么是PendSV,先要了解SVC。SVC和PendSV都属于内核异常,所以参考资料《Cortex-M3权威指南.pdf》。

??SVC即系统服务调用,也称系统调用;PendSV为可悬起的系统调用,它们用于操作系统之上的软件开发中。

??操作系统不可让用户程序(APP)直接操作硬件,所以当APP要访问硬件的时候,os会产生一个SVC异常,然后os提供的SVC异常服务程序得到执行,它再调用相关的操作系统函数,以完成APP的请求。

??这样方式很方便灵活,具有的优点有:
??(1) APP不用直接控制硬件,而是交由OS赋值具体的硬件操作,APP开发得到简化,编译APP的移植
??(2) OS代码经过充分的测试,使得系统更加稳健可靠
??(3) APP可以在无需特权级执行,APP不用担心误操作而时OS崩溃

??SVC异常通过执行”SVC”指令产生,该指令需要一个立即数充当系统调用代号。SVC异常服务函数会根据此代号解释本次SVC调用的具体要求,再调用对应的服务函数:

??在学习ARM9裸板的时候,有一个软中断指令SWI,其实SVC的地位和SWI是相同的,其机器码也相同。然而在CM3内核中,因为异常处理模型已经更换,所以该指令也被重命名,以强调SVC是在新生的系统中使用的。

??与之相关的异常是PendSV。SVC异常要求必须立即得到响应,若因优先级比当前正处理的中断服务函数低等原因使之无法立即得到响应,将造成硬FAULT。PendSV则不同,它可以像普通中断一样被悬挂起。OS可以利用PendSV的”缓期执行”机制去缓期执行一个异常,”缓”到什么时候然是缓到当前重要的任务完成。

??使用PendSV的方法是:手工往NVIC的PendSV悬起寄存器中写1,悬起后,若优先级不够高,则缓期执行。

??假设系统中有两个就绪任务,正通过systick异常启动任务切换:

这里写图片描述

??任务切换时没遇到中断请求的场景:
??(1) 任务A呼叫SVC来请求任务切换
??(2) OS接收到SVC请求后,不立即进行任务切换,而只是做任务切换的准备,并且Pend一个PendSV异常
??(3) 当CPU退出SVC服务函数后,立即进入PendSV服务函数,执行上下文切换
??(4) PendSV服务函数执行完毕后,回到任务B

??任务切换时遇到中断请求的场景:
??(1) 发生了一个中断,CPU在执行该中断的中断服务函数(ISR)
??(2) 在执行ISR的同时,发生systick异常,任务B呼叫SVC来请求任务切换
??(3) CPU暂时放弃ISR继续执行,而是执行一些必要操作,在SVC服务函数中Pend一个PendSV异常
??(4) CPU退出SVC服务函数后,回到ISR继续执行
??(5) ISR执行完毕后PendSV服务函数得到执行,在函数中实现上下文切换
??(6) 切换完毕,回到任务A

??要实现这个任务切换机制,我们需要手动将PendSV设置为最低优先级的异常。为什么/p>

??因为只有PendSV比当前正在响应的中断源,或者在切换任务的同时发生的中断的中断优先级低时,PendSV的中断服务才会被挂起。设置为最低优先级才保证PendSV中断服务能被悬起。

??了解了这么多后,接着往下看代码。

??OSStartHighRdy用于启动最高优先级的任务,它被OSStart()函数调用,调用前系统中必须至少有一个用户任务,否则系统发生崩溃。

??有了前面任务切换的认识后,上面的代码很容易理解,只是将PSP设置为0这个有点费解。

??那么PSP是什么PSP相关联还还有一个MSP。
??(1) MSP是主堆栈指针,或写成SP_main,这是缺省的堆栈指针,由OS内核、异常服务函数以及具有特权的APP代码使用。MSP在任务切换时是不需要保存的。
??(2) PSP是进程堆栈指针,或写成SP_process,用于常规的APP代码(不处于异常服务函数中)

??如何知道当前使用的是PSP还是MSPM3的设计是,LR寄存器的BIT2指示了当前使用的是PSP还是MSP(1表示PSP,0表示MSP),LR本身用户保存函数的返回地址的,但是STM32是32BIT单片机,地址是32BIT的,四字节对齐,所以最后两位恒为0,所以最后两位可以拿来复用。

??这里将PSP设置为0,说明系统刚刚运行,方便后面的判断OS从初始化过来的。

6. os自己使用的使能悬起PendSVd的函数

??这个函数是给os自己使用的切换任务,实质只是使能pendSV悬起(在pendSV服务中切换),可以发现这个使能操作跟在OSStartHighRdy的实现是一样的。在os_core.c的OS_Sched()中调用,(被宏定义为OS_TASK_SW()),OS_Sched()用于启动任务调度功能。

7. os中的在中断处理程序中实现任务切换调用的函数

??os中的在中断处理程序中调用任务切换,但是在中断处理程序中是不可以随便调用其他有可能会被调度的东西的(实时性),但是在中断退出后可以,所以此函数在OSIntExit()调用。其实质还是触发Pendsv。

8. PendSV服务函数

??终于到PendSV服务函数了,通过上面学习知道该函数主要是实现执行上下文切换。

??(1) CPU自动保存xPSR、PC、LR、R12和R0-E3寄存器到任务的堆栈中
??(2) CPU的栈指针切换到主堆栈指针MSP,我们只需要判断PSP指针是否为NULL就知道此时是否是因为OS进行任务切换而进入本函数的,
因此当os第一次启动任务时,即在OSStartHighRdy()中把PSP设置为NULL,以避免os以为进行任务切换
??(3) 手动保存R4-R11
??(4) 将旧任务的PSP指针保存(到该任务的控制块OSTCBStkPtr),以便下次继续任务运行时继续使用原来的栈
??(5) 调用任务切换的钩子函数(钩子函数在下一篇文章讲解)
??(6) 在就绪的众多任务中找出优先级最高的,获取其任务控制块,进而得到该任务的PSP指针
??(7) 恢复新任务的栈的R4-R11,其他的寄存器CPU会自动帮我们恢复
??(8) 函数返回,新任务得到执行

??需要注意的是,ARM的硬件特性,进行上下文切换时,CPU 会自动保存xPSR, PC, LR, R12, R0-R3,R4-R11需要使用程序保存。当是系统刚开始运行时候,R4-R11存放是没有用的数据,所以不需要报错。判断是系统刚开始运行第一次进行上下文切换还是非第一次进行上下文切换的依据是看PSP是否等于0。

来源:mybright_

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2017年7月9日
下一篇 2017年7月9日

相关推荐