OS_CPU_A.ASM cortex-M3 解析

琢磨ucos内核进行中++>>>>>>>

先上cortex-M3 与本部分相关的汇编指令

相关的汇编指令


至于相关伪指令,参见:-----ARM汇编伪指令----


NVIC---Nested Vectored Interrupt controller--嵌套向量中断控制器

这里给出NVIC寄存器映射:
NVIC 空间还用来实现系统控制寄存器。NVIC 空间分成以下部分:
  • 0xE000E000 - 0xE000E00F.  中断类型寄存器
  • 0xE000E010 - 0xE000E0FF.  系统定时器
  • 0xE000E100 - 0xE000ECFF.  NVIC 
  • 0xE000ED00  -0xE000ED8F.  系统控制模块,包括:CPUID;系统控制、配置和状态;故障报告。
  • 0xE000EF00 - 0xE000EF0F.  软件触发异常寄存器 
  • 0xE000EFD0  -0xE000EFFF.  ID空间 
详情查看:《cortex-M3技术参考手册》
另,关于Cortex-M3存储器结构和STM32存储器结构,参见 stm32存储器结构
这里关注如下寄存器:

留意标注的就好。

中断控制状态寄存器 :

中断控制状态寄存器用于:
  • 设置一个挂起(pending)NMI
  • 设置或清除一个挂起 SVC
  • 设置或清除一个挂起 SysTick
  • 查找挂起异常
  • 查找最高优先级挂起异常的向量号
  • 查找激活异常的向量号
寄存器地址、访问类型和复位状态:见上表
中断控制状态寄存器的位分配:

说明如下:




系统处理器优先级寄存器 :

通过 3 个处理器优先级寄存器为系统处理器排列优先级:
  • 存储器管理
  • 总线故障
  • 使用故障
  • 调试监控
  • 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;
  • 执行异常返回 将恢复剩下的上下文;
3、进入pendsv处理程序:
  • 下边的寄存器已经被处理器保存到进程堆栈上啦: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;
4、由于PendSV异常被设置为最低优先级的系统(在OSStartHighRdy()中),我们知道 当没有其他的异常或中断时,它才会运行,therefore safe to assume that context being switched out was using the process stack (PSP).
;********************************************************************************************************
;                                         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的挂起状态设置还是不设置。高有效。

其他的,待续。。



阅读更多
换一批

没有更多推荐了,返回首页