琢磨ucos内核进行中++>>>>>>>
先上cortex-M3 与本部分相关的汇编指令
相关的汇编指令
至于相关伪指令,参见:-----ARM汇编伪指令----
NVIC---Nested Vectored Interrupt controller--嵌套向量中断控制器
NVIC 空间还用来实现系统控制寄存器。NVIC 空间分成以下部分:
- 0xE000E000 - 0xE000E00F. 中断类型寄存器
- 0xE000E010 - 0xE000E0FF. 系统定时器
- 0xE000E100 - 0xE000ECFF. NVIC
- 0xE000ED00 -0xE000ED8F. 系统控制模块,包括:CPUID;系统控制、配置和状态;故障报告。
- 0xE000EF00 - 0xE000EF0F. 软件触发异常寄存器
- 0xE000EFD0 -0xE000EFFF. ID空间
中断控制状态寄存器 :
- 设置一个挂起(pending)NMI
- 设置或清除一个挂起 SVC
- 设置或清除一个挂起 SysTick
- 查找挂起异常
- 查找最高优先级挂起异常的向量号
- 查找激活异常的向量号
系统处理器优先级寄存器 :
- 存储器管理
- 总线故障
- 使用故障
- 调试监控
- SVC
- SysTick
- PendSV
这里我们关注 PRI_11、PRI15、PRI_14==>SVCall异常、SysCall异常、PendSV异常。。。
以上只为更好的读懂、理解 OS_CPU_A.ASM ,,,
OS_CPU_A.ASM解析:
环境:
STM32 cortex-M3内核的片子 + ucos + IAR
@自己:coretex A8、A9都是armv7a 架构;coretex M3、M4是armv7m架构;前者是内核,后者是指令集的架构。
变量、函数声明 及 相关宏定义:有点像.h文件干的事。
;********************************************************************************************************
EXTERN OSRunning ; External references 全局变量声明,非本地定义
EXTERN OSPrioCur
EXTERN OSPrioHighRdy
EXTERN OSTCBCur
EXTERN OSTCBHighRdy
EXTERN OSIntNesting
EXTERN OSIntExit
EXTERN OSTaskSwHook
EXTERN OSRdyGrp
EXTERN OSRdyTbl
EXTERN OSPrioHighRdy
PUBLIC OS_CPU_SR_Save ; Functions declared in this file 公共函数声明
PUBLIC OS_CPU_SR_Restore
PUBLIC OSStartHighRdy
PUBLIC OSCtxSw
PUBLIC OSIntCtxSw
PUBLIC OS_CPU_PendSVHandler
;********************************************************************************************************
; EQUATES;宏定义
;********************************************************************************************************
NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register.
NVIC_SYSPRI14 EQU 0xE000ED22 ; System priority register (priority 14).
NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (lowest).
NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception.
然后是--RSEG CODE:CODE:NOROOT(2)
RSEG CODE:CODE:NOROOT(2)
+
已经注释的OS_SchedNew 实现..
.................
原谅我看不太懂,,提供线索如下,
处理关键代码段时开关中断:
CRITICAL SECTION METHOD 3 FUNCTIONS:关键代码段方法3函数
Description:通过保存中断的状态来禁用/启用中断。一般来说我们将中断禁用标志的状态存储在本地变量“cpu_sr”中然后禁用中断。我们能够恢复中断禁用状态通过复制回“cpu_sr”到CPU状态寄存器中。
;********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
; Prototypes : OS_CPU_SR OS_CPU_SR_Save(void);
; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
;
;
; Note(s) : 1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
; OS_CPU_SR cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL(); /* cpu_sr = OS_CPU_SaveSR(); */
; :
; :
; OS_EXIT_CRITICAL(); /* OS_CPU_RestoreSR(cpu_sr); */
; :
; :
; }
;********************************************************************************************************
OS_CPU_SR_Save
MRS R0, PRIMASK ; 读回PRIMASK寄存器内容
CPSID I
BX LR
OS_CPU_SR_Restore
MSR PRIMASK, R0
BX LR
PRIMASK寄存器: 除能所有的中断——当然了,不可屏蔽中断(NMI)不甩它。
OSStartHighRdy:
当OS初始化完毕后,执行OSStart,OSStart最后调用OSStartHighRdy函数,注意在此之前的线程模式和异常模式的堆栈都是MSP,在此之后线程模式的堆栈是PSP,异常模式的堆栈仍是MSP。
;********************************************************************************************************
; START MULTITASKING 开始任务
; void OSStartHighRdy(void)
;
; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause
; the first task to start.函数触发PendSV异常(从本质上讲,是导致上下文切换)导致第一个任务开始
;
; 2) OSStartHighRdy() MUST: 关键流程
; a) Setup PendSV exception priority to lowest;
; 设置PendSV异常优先级到最低
; b) Set initial PSP to 0, to tell context switcher this is first run;
; 设置初始进程堆栈到0位置,目的是告诉上下文/任务切换器这个函数第一个执行
; c) Set OSRunning to TRUE;
; 设置OSRunning标志为TRUE
; d) Trigger PendSV exception;
; 切换PendSV异常
; e) Enable interrupts (tasks will run with interrupts enabled).
; 使能中断(任务将通过启用中断运行)
;********************************************************************************************************
OSStartHighRdy
LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0];R1寄存器中的内容存放/写入到以R0内容作为地址的内存中
MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
MSR PSP, R0
LDR R0, =OSRunning ; OSRunning = TRUE
MOVS R1, #1
STRB R1, [R0]
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
CPSIE I ; Enable interrupts at processor level
OSStartHang
B OSStartHang ; Should never get here正常情况下,不应运行到这
任务级的上下文切换:
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level) 从任务级执行一个上下文切换
; void OSCtxSw(void)
;
; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function
; triggers the PendSV exception which is where the real work is done.
;********************************************************************************************************
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
中断级上下文切换:
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From interrupt level)从中断级执行一个上下文切换
; void OSIntCtxSw(void)
;
; Notes: 1) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as
; the result of an interrupt. This function simply triggers a PendSV exception which will
; be handled when there are no more interrupts active and interrupts are enabled.
;********************************************************************************************************
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
这两个显然没看出有啥子不一样的.....啥子目的这是? 待续~
PendSV异常处理程序:
描述:
1、PendSV用于引起上下文切换。这是Cortex-M3执行上下文切换的推荐方法。因为Cortex-M3对任何异常自动保存和恢复 处理器上下文的一半。所以,只有R4-R11需要被用于固定堆栈指针。这样子使用PendSV异常意味着保存和恢复是相同的,无论是从一个线程开始或发生中断或异常。
2、Pseudo-code is:
- 获取进程指针SP,如果为0则跳过(转到D)储蓄部分(第一上下文切换);
- 保存剩余的寄存器 r4-r11 到进程/任务堆栈;
- 保存进程指针PSP到它的TCB(任务控制块),OSTCBCur->OSTCBStkPtr = SP;
- 调用 OSTaskSwHook();
- 获取当前的高优先级,OSPrioCur = OSPrioHighRdy;
- 获取当前准备好的,即ready状态的线程TCB,OSTCBCur = OSTCBHighRdy;
- 从TCB获取新的进程指针SP(PSP),SP = OSTCBHighRdy->OSTCBStkPtr;
- 从新的进程堆栈恢复R4--R11;
- 执行异常返回 将恢复剩下的上下文;
- 下边的寄存器已经被处理器保存到进程堆栈上啦:xPSR, PC, LR, R12, R0-R3;
- 处理器模式被切换到Handler模式(从线程/Thread模式切换过来的);
- 堆栈是主堆栈MSP(从PSP切换过来的)
- OSTCBCur points to the OS_TCB of the task to suspend OSTCBHighRdy points to the OS_TCB of the task to resume;
;********************************************************************************************************
; HANDLE PendSV EXCEPTION
; void OS_CPU_PendSVHandler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
; context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the
; processor context on any exception, and restores same on return from exception. So only
; saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception
; this way means that context saving and restoring is identical whether it is initiated from
; a thread or occurs due to an interrupt or exception.
;
; 2) Pseudo-code is:
; a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch);
; b) Save remaining regs r4-r11 on process stack;
; c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP;
; d) Call OSTaskSwHook();
; e) Get current high priority, OSPrioCur = OSPrioHighRdy;
; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy;
; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr;
; h) Restore R4-R11 from new process stack;
; i) Perform exception return which will restore remaining context.
;
; 3) On entry into PendSV handler:
; a) The following have been saved on the process stack (by processor):
; xPSR, PC, LR, R12, R0-R3
; b) Processor mode is switched to Handler mode (from Thread mode)
; c) Stack is Main stack (switched from Process stack)
; d) OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we
; know that it will only be run when no other exception or interrupt is active, and
; therefore safe to assume that context being switched out was using the process stack (PSP).
;********************************************************************************************************
OS_CPU_PendSVHandler
CPSID I ; 上下文切换期间防止中断-关中断
MRS R0, PSP ; 获得PSP
CBZ R0, OS_CPU_PendSVHandler_nosave ;PSP为0跳到OS_CPU_PendSVHandler_nosave,即不保存上文,直接进入下文。
;问什么呢,因为首次调用,是没有上文的。否则,全部执行
;接下来是保存上文
SUBS R0, R0, #0x20 ;因为寄存器是32位的,4字节对齐,自动压栈的寄存器有8个,所以偏移为8*0x04=0x20
STM R0, {R4-R11} ;除去自动压栈的寄存器外,需手动将R4-R11压栈
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;保存上文的SP指针 OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out
; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave ;切换下文
PUSH {R14} ; Save LR exc_return value, LR压栈,下面要调用C函数
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
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 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11} ; Restore r4-11 from new process stack
ADDS R0, R0, #0x20
MSR PSP, R0 ; Load PSP with new process SP ,PSP = 新任务SP
ORR LR, LR, #0x04 ; Ensure exception return uses process stack,确保异常返回后使用PSP
CPSIE I
BX LR ; Exception return will restore remaining context,
;退出异常,从PSP弹出xPSR,PC,LR,R0-R3,进入新任务运行
END
以上便是OS_CPU_A.ASM源文件的全部,注释即函数段的相关说明仅供参考。
总结:
1、设置PendSV异常优先级到最低的实现,
NVIC_SYSPRI14 EQU 0xE000ED22 ; System priority register (priority 14).
NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (lowest).
0xE000ED22为PRI_14的地址,8位==》应该这么说,PendSV异常的优先级设置寄存器对应PRI_14---->见上文---系统处理器优先级寄存器。
2、实现PendSV异常的触发
NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register.
NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception.
0xE000ED04是中断控制状态寄存器的地址,0x10000000就是要给 中断控制状态寄存器 设置的值,该寄存器的28bit 负责PendSV的挂起状态设置还是不设置。高有效。
其他的,待续。。