Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805510
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2017-09-04 13:59:04

任务切换函数:根据不同的CPU,这个函数一般都是用汇编语言写,一般思想都是:
1、将CPU的通用寄存器压入堆栈(每一个任务都有自己的堆 栈);
2、将堆栈指针SP赋值给任务控制块中的OSTCBStkPtr中;
3、获得最高优先级的任务块;
4、将最高任务快中的OSTCBStkPtr赋值 给堆栈指针SP;
5、将新的任务块中的值出栈给CPU通用寄存器;
6、中断返回IRET(RETI),使PC指向待运行任务。

    普通任务切换函数OSCtxSw()示意性代码:

    OSCtxSw(void)

    {

        压入堆栈;

        OSTCBCur->OSTCBStkPtr=SP;

        OSTCBCur=OSTCBHighRdy;

        OSPrioCur=OSPrioHighRdy;

        SP=OSTCBHighRdy->OSTCBStkPtr;

        出栈;

        IRET;

    }

中断级任务切换函数OSIntCtxSw()示意代码:

    OSIntCtxSw(void)

    {

         OSTCBCur=OSTCBHighRdy;

        OSPrioCur=OSPrioHighRdy;

        SP=OSTCBHighRdy->OSTCBStkPtr;

        出栈;

        RETI;

    }

    OSIntCtxSw(void)在调用之前,在进入中断的时候已经将任务的通用寄存器压入堆栈里面了,所以任务切换函数后面是相同的;

    IRET、RETI指令都是将在将堆栈中保存的地址取出,送给PC;唯一的区别就是RETI指令除了中断返回的作用之外还有将“优先级生效”触发器清零的功能;



    在Coxtex-M3系列的芯片中,任务级和中断级的任务切换都是利用cpu的PendSV来实现的,所以统一的利用在中断进行任务的切换。
    具体的代码见:


;********************************************************************************************************
;                               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中断来实现。具体的pendsv代码如下:
    ;********************************************************************************************************
;                                         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).
;********************************************************************************************************

PendSV_Handler
    CPSID   I                                                           ; Prevent interruption during context switch
    MRS     R0, PSP                                                  ; PSP is process stack pointer
    CBZ     R0, PendSV_Handler_nosave                     ; Skip register save the first time (第一次切换的时候,不用保存中断现场,因为之前并没有任务执行过)

    // 保存当前任务的中断现场

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}   ;将cpu的寄存器R4-R11保存到任务堆栈中
   

STMS的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。

    STMFD      SP!,   {R0}

同样的,该指令也可理解为:  STMFD      [SP]!,   {R0}

意思是:把R0保存到堆栈(sp指向的地址)中。



    LDR     R1, =OSTCBCur                 ; OSTCBCur->OSTCBStkPtr = SP; 
                                                      ; 伪指令,取变量OSTCBCur的地址,就是相当于该结构体的首地址,也就是变量            
                                                      ; OSTCBCur->OSTCBStkPtr
    LDR     R1, [R1]
    STR     R0, [R1]                          ; R0 is SP of process being switched out
                                                    ; R0就是堆栈的栈顶,保存起来,而后面cpu寄存器R0,R1,..., XPSR都是中断时,CPU自动入栈的。
                                                    ; 出栈的时候也是一样
                            ; At this point, entire context of process has been saved


// 获取最高任务优先级,恢复该任务堆栈的数据到CPU各个寄存器,跳转到该任务中执行。
PendSV_Handler_nosave
    PUSH    {R14}                 ; Save LR exc_return value
                                     ; 调用C函数之前,保存链接寄存器,也即保存返回地址,因为调用C函数会自动使用LR寄存器来保存返回地址

    LDR     R0, =OSTaskSwHook ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

B: 跳转。

B   label  满足条件立即跳转到Lable指定的地址执行

BL: 带链接的跳转。 首先将当前指令的下一条指令地址保存在LR寄存器,然后跳转的lable。通常用于调用子程序,可通过在子程序的尾部添加mov  pc, lr 返回。

BX: 带状态切换的跳转。最低位为1时,切换到Thumb指令执行,为0时,解释为ARM指令执行。

BLX:  带链接和状态切换的跳转。结合了BX与BL功能,

操作:

  • BL 和 BLX 指令可将下一个指令的地址复制到 lr(r14,链接寄存器)中。

  • BX 和 BLX 指令可将处理器的状态从 ARM 更改为 Thumb,或从 Thumb 更改为 ARM。

    BLX label 无论何种情况,始终会更改处理器的状态。

    BX Rm 和 BLX Rm 可从 Rm 的位 [0] 推算出目标状态:

    • 如果 Rm 的位 [0] 为 0,则处理器的状态会更改为(或保持在)ARM 状态

    • 如果 Rm 的位 [0] 为 1,则处理器的状态会更改为(或保持在)Thumb 状态。



    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]                                     

STR指令的格式为:
STR{条件}  源寄存器,<存储器地址>
STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中

                                                                       ; OS_TCB结构体的定义非常有讲究啊,它的第一个元素就是指向任务堆栈的指针 OSTCBStkPtr
                                                                       ; 所以 OSTCBHighRdy 的前面四个字节的内容就是 任务堆栈 的指针啊!!!!

    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
    ORR     LR, LR, #0x04                      ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                             ; Exception return will restore remaining context
    NOP

    END


阅读(3572) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

erwrwe2019-06-11 16:26:19

你好,请问一下。 PendSV_Handler_nosave 函数里的ADDS    R0, R0, #0x20 你是如何理解的