关于ARM架构下ucos2任务切换函数OSCtxSw源码分析
来源:互联网 发布:泰牛程序员怎么样 编辑:程序博客网 时间:2024/06/01 10:00
关于ARM架构下ucos2任务切换函数OSCtxSw源码分析
看了很多博文和权威资料,终于搞清楚了ARM的任务切换机制,下面我就引用一些资料来解释 OSCtxSw 函数究竟是如何保护现场和完成任务切换的。
http://www.keil.com/dd/docs/datashts/arm/cortex_m3/r1p1/ddi0337e_cortex_m3_r1p1_trm.pdf
首先,我们看下《Cortex-M3权威指南.pdf》中第38页有关通用寄存器组和特殊功能寄存器组的内容,存储堆栈指针SP的寄存器R13有两个,但是同一时间只能有一个被看到,这也就是所谓的“Banked”寄存器:
以下切换代码取自上面网址提供的ARM手册5-26页 (取材于keil u4的帮助资料)
;Example Context Switch (Assumes Thread is already on PSP)
MRS r12, PSP ; Recover PSP into R12 ①
STMDB r12!, {r4-r11, LR} ; Push non-stack registers ②
LDR r0, =OldPSPValue ; Get pointer to old Thread Control Block ③
STR r12, [r0] ; Store SP into Thread Control Block ④
LDR r0, =NewPSPValue ; Get pointer to new Thread Control Block ⑤
LDR r12, [r0] ; Acquire new Process SP ⑥
LDMIA r12!, {r4-r11, LR} ; Restore non-stacked registers ⑦
MSR PSP, r12 ; Set PSP to R12 ⑧
BX lr ; Return back to Thread ⑨
①显然第一行汇编码,我们是将SP堆栈地址保存到R12寄存器中,即sp地址->R12.
②这里因为R12中已经是sp栈地址,因此我们是将R4-R11, LR,依次按递减4方式压入栈中,
假如此时sp栈地址为4000H,则栈空间如下所示,R12后的“!”号表示存储完成后同时更新R12寄存器,因此,经过递减后,当前R12值为3964H.
栈空间
R12
→
4000H
LR
3996H
R11
3992H
R10
3988H
R9
3984H
R8
3980H
R7
3976H
R6
3972H
R5
3968H
R12’
→
R4
3964H
③然后我们将要保存老的堆栈地址OldPSPValue的寄存器的地址,加载到R0中。
④将此时R12中老的堆栈地址OldPSPValue保存到R0所指的该寄存器中,到此,我们就已经保护现场完毕,接下来我们需要切换新的现场。
⑤继续将保存有新的堆栈地址NewPSPValue 的寄存器地址,加载到R0中。
⑥将新的堆栈地址加载到R12中。
⑦弹栈操作,将原来某个现场的信息弹出到R4-R11, LR中,方式与之前压栈刚好相反,不再赘述。
⑧将此时R12中的新堆栈地址返回给PSP
⑨跳转并返回继续执行。
以上写得比较啰嗦,不知道大家是否明白了,如果有不清楚汇编指令的,请参见我的附件【ARM汇编指令手册.pdf】和《Cortex-M3权威指南.pdf》赚点小分,还请见谅,呵呵。
堆栈的初始化函数需要根据不同的处理器进行设置,关于堆栈函数的处理首先要明白几点:
1.在《Cortex-M3权威指南.pdf》的第35页有如下内容:
接下来我再分析下,为什么ucos-ii中的OSCtxSw代码,似乎没有以上冗长的保护现场的代码,而是一段很简短的代码,以下是STM32平台下,ucos-ii中的源码:
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
NOP
其中NVIC_INT_CTRL的值即0xE000ED04(中断控制及状态器ICSR地址,见权威指南P131),而NVIC_PENDSVSET的值为0x10000000即第28位PENDSVSET写1,悬起PendSV中断,因此这段代码的功能其实就是在不破坏R4,R5内容的前提下,触发PendSV中断,其实就是利用中断来实现保护现场的功能,那么,为什么uCOS保护现场要兜这么大一个圈子呢,后面我会引入权威指南中的相关内容,解析这样做的好处,下面我们先来看看在PendSV中断中我们做了什么,以下仍然是STM32平台下,ucos-ii中的源码:
PendSV_Handler
CPSID I ; 首先我们在任务切换过程中关闭中断
MRS R0, PSP ;保存线程栈PSP内容到R0
CBZ R0, PendSV_Handler_Nosave ; 如果之前没有任务执行那么,我们跳过
;现场保护这个过程
SUBS R0, R0, #0x20 ; 这里有人可能有疑问了,为什么要减32,这是 ;搞什么?其实之前我也提过了进入异常服务例程 ;时,自动压栈了R0-R3,R12,LR,PSR,PC刚好8个寄 ;存器,共占8*4个字节的地址空间。我们将要保存 ;的R4-R11就接在这后面,所以需要偏移。
STM R0, {R4-R11}
LDR R1, =OSTCBCur ; 你会发现OSTCBCur是从C代码中IMPORT引入的,
;这里就是那个指针变量的地址
LDR R1, [R1] ;取得指针OSTCBCur所指的OSTCB结构首地址
STR R0, [R1] ; OSTCB结构的第一个成员就是OSTCBStkPtr , 将 ;保存有我们的R4-R11内容的栈地址保存到 ;OSTCBStkPtr 中
PendSV_Handler_Nosave
PUSH {R14} ; 这段我们只是为了调用一下OSTaskSwHook函数
;这个函数是开放给我们钩取线程切换过程的
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, =OSPrioCur ;这里我们开始切换到新的任务的内容,包含优先 ;级和OSTCB结构的切换
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ;我们将新的栈地址送到R0
LDM R0, {R4-R11} ;将R4-R11从栈弹出
ADDS R0, R0, #0x20 ;加回我们偏移的地址,这样在执行BX跳转指令 ;时,硬件能从正确的栈地址自动弹出 ;R0-R3,R12,LR,PSR,PC的内容
MSR PSP, R0 ; 将R0中的栈地址送到PSP线程栈
ORR LR, LR, #0x04 ; 这句是确保后面执行BX命令时使用的是线程栈 ;PSP,而不是主堆栈MSP
CPSIE I ;开总中断
BX LR ; 执行中断返回,此时R0-R3,R12,LR,PSR,PC自动 ;出栈
这里我要补充说一下关于“ORR LR, LR, #0x04 ”的内容,异常返回(BX跳转)时,硬件会根据LR的内容,POP相应的堆栈,这句保证了从PSP堆栈POP出R0等寄存器,以下引用一篇博文中的内容来说明原因http://blog.chinaunix.net/uid-26817832-id-3162243.html
产生异常时,两个值我们需要,一个是pc,一个是LR,通过LR找到栈
1、 如果LR=0xFFFFFFF9说明产生异常的时候使用的是MSP
2、 如果LR=0xFFFFFFFD明产生异常的时候使用的是PSP
由此我们看出变化的恰是0x04,其实我们并不关心此时LR的值,我们只是做个标记,保证返回时,能从PSP出栈,出栈后LR会自动获取到正确的值,并覆盖我们这个LR值。
好了,说完,以上的内容,现在我将引用《Cortex-M3权威指南.pdf》中P123页给出的为什么这么做的原因。
个人感觉权威指南这几张图说得十分透彻和直观了,我就不啰嗦了,此外当然还有一个原因,就是间接保存PC寄存器的内容了,因为部分ARM不支持直接访问PC寄存器的内容。
希望以上我说了这么多废话,引用这么多资料,有把我的理解解释清楚,因为这些也只是我学习的一些笔记,如果有理解不对的地方,还请大虾门指出,谢谢了。
- 关于ARM架构下ucos2任务切换函数OSCtxSw源码分析
- µC/OS-Ⅱ任务切换函数OSCtxSw()原型
- ucos2的源码架构
- arm架构下的原子操作(atomic)函数源码分析
- ucos2 任务
- ucos2源码阅读之主要函数功能分析
- OSCtxSw()
- arm 体系架构下 linux kernel 的系统调用与返回 源码分析与总结
- ucos2任务管理
- Raw-OS源码分析之同优先级任务切换
- 关于linux下arm的上下文切换之context_switch
- 任务切换分析
- x86、arm、mips架构函数调用实例分析
- arm mmu源码分析
- 进程切换源码分析
- ucos2
- UCOS2下邮箱使用【转】
- UCOS2下邮箱使用【转】
- sumbit text3的设置
- 时间选择器
- 有限背包计数问题 (分类dp)
- VC 如何在一个矩形框中居中显示一个长字符串
- 翻转字符串
- 关于ARM架构下ucos2任务切换函数OSCtxSw源码分析
- 【转载】用“人话”解释不精确线搜索中的Armijo-Goldstein准则及Wolfe-Powell准则
- 数位DP入门后部分题解
- HTTPS原理
- ElasticSearch学习36_Elasticsearch扩展性插件
- HDU 4407 Sum(容斥)
- 杭电2036叉乘法求多边行面积
- 笨办法学Python-习题0 准备工作
- 设计模式