fans-rt 任务调度-堆栈切换篇(4)tiny模型详细分析

来源:互联网 发布:python urllib2 post 编辑:程序博客网 时间:2024/06/05 21:12

优化后的Tiny模型代码:

;;    Copyright(C) 2013-2015, Fans-rt development team.;;    All rights reserved.;;    This is open source software.;    Learning and research can be unrestricted to  modification, use and dissemination.;    If you need for commercial purposes, you should get the author's permission.;;    Configuration:;     System global core stack                  NO;     The local core stack of general task      YES;     The loacl core stack of kernel task       YES;     The local user stack of general task      NO;     Hardware supported task switch IRQ        NO;     Hardware supported double stack           NO;;    date           author          notes;    2015-06-25     JiangYong       new file;    2015-07-07     JiangYong       rename to kboard_interrupt.s;    2015-07-11     JiangYong       code optimization;        INCLUDE kirq_define_enum.inc    EXPORT  UsageFault_Handler    EXPORT  BusFault_Handler    EXPORT  MemManage_Handler    EXPORT  HardFault_Handler    EXPORT  SysTick_Handler    EXPORT  PendSV_Handler    EXPORT  SVC_Handler    EXPORT  CORE_Switch2UserMode    IMPORT  CORE_EnterIRQ    IMPORT  CORE_LeaveIRQ    IMPORT  CORE_TickHandler    IMPORT  CORE_TaskScheduling    IMPORT  CORE_HandlerLPC    IMPORT  CORE_SwitchTask    IMPORT  CORE_SetTaskStackPosition    IMPORT  CORE_GetTaskStackPosition    IMPORT  CORE_GetCoreStackPosition    IMPORT  CORE_CheckMustbeSchedule    PRESERVE8    AREA    |.text|, CODE, READONLY    ALIGN 4    THUMBCORE_Switch2UserMode   PROC    MOV     R0,     #0                  ;    MSR     PRIMASK, R0                 ;  Enable IRQ    BX      LR                          ;  return    ENDPPendSV_Handler  PROC    BX      LR                          ;  Not support PendSV IRQ    ENDPSVC_Handler     PROC    CPSID   I                           ;  Why to disable IRQ ? Guess !<span style="color:#ff0000;">    MOV     R0,     SP                  ;  R0 = Offset of {R0 - R3}    PUSH    {LR, R12}                   ;  Why to push 12? Guess !    MOV     R12,    R0                  ;  R12 = Offset of {R0 - R3}</span>    BL      CORE_EnterIRQ               ;  Set current interrupt nest layer    CPSIE   I                           ;  Enable IRQ<span style="color:#ff0000;">    LDMFD   R12,    {R0-R3}             ;  Resume R0 - R3 to call service</span>    BL      CORE_HandlerLPC             ;  Call system service<span style="color:#ff0000;">    B       ST_L1                       ;  The next step same as system tick handler</span>    ENDPSysTick_Handler PROC    CPSID   I                           ;  Why to disable IRQ ? Guess !<span style="color:#ff0000;">    PUSH    {R12, LR}                   ;  Why to push R12?</span>    BL      CORE_EnterIRQ               ;  Set current interrupt nest layer    BL      CORE_TickHandler            ;  Inc the system tick    CPSIE   I                           ;  Enable IRQ    BL      CORE_TaskScheduling         ;  Find the new task will be scheduling<span style="color:#ff0000;">ST_L1</span>    CPSID   I                           ;  Disable IRQ    BL      CORE_LeaveIRQ               ;  Set and get current interrupt nest layer    CBNZ    R0,     ST_LE               ;  Nest layer != 0 then leave this interrupt    BL      CORE_CheckMustbeSchedule    ;  Check need schedule    CBZ     R0,     ST_LE               ;  Must schedule = FALSE then leave this interrupt<span style="color:#ff0000;">    PUSH    {R4 - R11}                  ;  nest layer = 0 and Must schedule = TRUE then scheduling</span>    MRS     R0,     MSP                 ;  R0 = Core stack for old task    MRS     R1,     PSP                 ;  R1 = User stack for old task    BL      CORE_SwitchTask             ;  CORE_SwitchTask(CoreStack, UserStack);                                        ;  No need check the task permssion    BL      CORE_GetTaskStackPosition   ;  R0 = User stack for new task(no need)    MOV     R1,     R0                  ;  R1 = User stack for new task(no need)    BL      CORE_GetCoreStackPosition   ;  R0 = Core stack for new task    MSR     PSP,    R1                  ;  Update user stack(no need)    MSR     MSP,    R0                  ;  Update core stack<span style="color:#ff0000;">    POP     {R4 - R11}                  ;  Restore new task registers</span>ST_LE    POP     {R12, LR}                   ;  Resume break point    CPSIE   I                           ;  Enable IRQ    BX      LR                          ;  Return to task break point    ENDPHardFault_Handler   PROC    B       .    ENDPMemManage_Handler   PROC    B       .    ENDPBusFault_Handler    PROC    B       .    ENDPUsageFault_Handler  PROC    B       .    ENDPALIGN            END

代码中,红色部分为第一个版本与优化后的主要差异点,为什么这么优化呢?在我们分析完代码执行过程中的堆栈环境就明白了。Tiny模型所有任务只有一个堆栈,本文将所有任务堆栈按照内核堆栈进行分析。

首先,进入中断时的堆栈状态:

;               |-----------------------|;               |        ........       |;               |-----------------------|;               |          xPSR         | <<=  SP + 0x1C;               |-----------------------|;               |           PC          | <<=  SP + 0x18;               |-----------------------|;               |           LR          | <<=  SP + 0x14;               |-----------------------|;               |           RC          | <<=  SP + 0x10;               |-----------------------|;               |           R3          | <<=  SP + 0x0C;               |-----------------------|;               |           R2          | <<=  SP + 0x08;               |-----------------------|;               |           R1          | <<=  SP + 0x04;               |-----------------------|;               |           R0          | <<=  SP + 0x00;               |-----------------------|;               |        ........       |;               |-----------------------|;           中断入口的堆栈映像,由CPU自动保存

从堆栈映像图可以看出,SP 刚好指向保存 R0 的位置, 所以 SVC_Handler 中断入口在执行

    <span style="color:#ff0000;">MOV     R0,     SP                  ;  R0 = Offset of {R0 - R3}</span>    PUSH    {LR, R12}                   ;  Why to push 12? Guess !    <span style="color:#ff0000;">MOV     R12,    R0                  ;  R12 = Offset of {R0 - R3}</span>

后, R12就指向堆栈中保存R0-R3寄存器的首地址,在调用 CORE_HandlerLPC之前,通过

<span style="color:#ff0000;">    LDMFD   R12,    {R0-R3}             ;  Resume R0 - R3 to call service</span>    BL      CORE_HandlerLPC             ;  Call system service
即可恢复R0-R3寄存器,以便作为参数传递给 CORE_HandlerLPC。


当代码执行完 LR 和 R12 寄存器的入栈操作后堆栈如下:

;               |-----------------------|;               |        ........       |;               |-----------------------|;               |          xPSR         | <<=  SP + 0x24;               |-----------------------|;               |           PC          | <<=  SP + 0x20;               |-----------------------|;               |           LR          | <<=  SP + 0x1C;               |-----------------------|;               |           RC          | <<=  SP + 0x18;               |-----------------------|;               |           R3          | <<=  SP + 0x14;               |-----------------------|;               |           R2          | <<=  SP + 0x10;               |-----------------------|;               |           R1          | <<=  SP + 0x0C;               |-----------------------|;               |           R0          | <<=  SP + 0x08;               |-----------------------|;               |           LR          | <<=  SP + 0x04;               |-----------------------|;               |           RC          | <<=  SP + 0x00;               |-----------------------|;               |        ........       |;               |-----------------------|;                    LR 和 R12 入栈后
修改前的代码SVC_Handler和SysTick_Handler分别对R4和R0入栈,并未对R12入栈。为什么修改为对R12入栈呢?因为任务切换时需要构造断点堆栈映像如下:

;               |-----------------------|;               |        ........       |;               |-----------------------|;               |          xPSR         | <<=  SP + 0x54;               |-----------------------|;               |           PC          | <<=  SP + 0x50;               |-----------------------|;               |           LR          | <<=  SP + 0x4C;               |-----------------------|;               |           RC          | <<=  SP + 0x48;               |-----------------------|;               |           R3          | <<=  SP + 0x44;               |-----------------------|;               |           R2          | <<=  SP + 0x40;               |-----------------------|;               |           R1          | <<=  SP + 0x3C;               |-----------------------|;               |           R0          | <<=  SP + 0x38;               |-----------------------|;               |           LR          | <<=  SP + 0x34;               |-----------------------|;               |           RC          | <<=  SP + 0x30;               |-----------------------|;               |           RB          | <<=  SP + 0x2C;               |-----------------------|;               |           RA          | <<=  SP + 0x28;               |-----------------------|;               |           R9          | <<=  SP + 0x24;               |-----------------------|;               |        ........       |;               |-----------------------|;               |           R0          | <<=  SP + 0x00  (It's the old task break point);               |-----------------------|;               |        ........       |;               |-----------------------|;                     断点堆栈映像
在确认需要调度后,会对 R4-R11进行入栈以构造断点堆栈映像,而已入栈的R12刚好在断点堆栈映像中正确的位置。所以,相比修改前,节省了R4/R0的入栈和出栈动作。

优化点:

   1.中断入口的入栈由R4/R0修改为R12,减少R0/R4的入栈和出栈消耗

   2.在 SVC_Handler 中,对R0-R3的入栈和出栈修改为由R12寻址的LDMFD指令,通过两次寄存器访问来减少对内存的4次访问

   3.减少冗余代码,SVC_Handler和SysTick_Handler共用中断下半部代码。



0 0
原创粉丝点击