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

优化后的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
    THUMB

CORE_Switch2UserMode   PROC
    MOV     R0,     #0                  ;
    MSR     PRIMASK, R0                 ;  Enable IRQ
    BX      LR                          ;  return
    ENDP


PendSV_Handler  PROC
    BX      LR                          ;  Not support PendSV IRQ
    ENDP

SVC_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>
    ENDP

SysTick_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
    ENDP


HardFault_Handler   PROC
    B       .
    ENDP

MemManage_Handler   PROC
    B       .
    ENDP
BusFault_Handler    PROC
    B       .
    ENDP
UsageFault_Handler  PROC
    B       .
    ENDP
	
ALIGN
            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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值