ThreadX源码:Cortex-A7的tx_thread_context_save.S(线程上下文保存)汇编代码分析

27 篇文章 0 订阅
4 篇文章 0 订阅

0 参考资料

Cortex M3权威指南(中文).pdf(可以参考ARM指令集用法)

1 前言

tx_thread_context_save.S是用来实现Cortex-A7下线程上下文保存的函数所在汇编文件。

2 源码分析

2.1 概述

_tx_thread_context_save函数用于在线程被中断打断后保存上下文,根据打断点所处的位置分为2种情况处理:
(1)非嵌套中断(首次触发中断,也就是从线程切换到IRQ模式)
(2)嵌套中断
其中,非嵌套中断中又分为2种情况:
(1)打断点处于线程调度执行期间
(2)打断点不处于线程调度执行期间

2.2 源码逐行分析

源码如下:

1.    .global _tx_thread_context_save
2.    .type   _tx_thread_context_save,function
3._tx_thread_context_save:

4.    /* Upon entry to this routine, it is assumed that IRQ interrupts are locked
5.       out, we are in IRQ mode, and all registers are intact.  */

6.    /* Check for a nested interrupt condition.  */

7.    PUSH    {r0-r3}                         // Save some working registers
8.#ifdef TX_ENABLE_FIQ_SUPPORT
9.    CPSID   if                              // Disable FIQ interrupts
10.#endif
11.    LDR     r3, =_tx_thread_system_state    // Pickup address of system state variable
12.    LDR     r2, [r3]                        // Pickup system state
13.    CMP     r2, #0                          // Is this the first interrupt?
14.    BEQ     __tx_thread_not_nested_save     // Yes, not a nested context save

15.    /* Nested interrupt condition.  */

16.    ADD     r2, #1                          // Increment the interrupt counter
17.    STR     r2, [r3]                        // Store it back in the variable

18.   /* Save the rest of the scratch registers on the stack and return to the
19.      calling ISR.  */

20.    MRS     r0, SPSR                        // Pickup saved SPSR
21.    SUB     lr, #4                          // Adjust point of interrupt
22.    PUSH    {r0, r10, r12, lr}              // Store other registers

23.    /* Return to the ISR.  */

24.    MOV     r10, #0                         // Clear stack limit

25.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
26.    /* Call the ISR enter function to indicate an ISR is executing.  */
27.    PUSH    {lr}                            // Save ISR lr
28.    BL      _tx_execution_isr_enter         // Call the ISR enter function
29.    POP     {lr}                            // Recover ISR lr
30.#endif

31.    B       __tx_irq_processing_return      // Continue IRQ processing

32.__tx_thread_not_nested_save:

33.    /* Otherwise, not nested, check to see if a thread was running.  */
34.    ADD     r2, #1                          // Increment the interrupt counter
35.    STR     r2, [r3]                        // Store it back in the variable
36.    LDR     r1, =_tx_thread_current_ptr     // Pickup address of current thread ptr
37.    LDR     r0, [r1]                        // Pickup current thread pointer
38.    CMP     r0, #0                          // Is it NULL?
39.    BEQ     __tx_thread_idle_system_save    // If so, interrupt occurred in
40.                                            //   scheduling loop - nothing needs saving!

41.    /* Save minimal context of interrupted thread.  */

42.    MRS     r2, SPSR                        // Pickup saved SPSR
43.    SUB     lr, #4                          // Adjust point of interrupt
44.    PUSH    {r2, r10, r12, lr}              // Store other registers

45.    MOV     r10, #0                         // Clear stack limit

46.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
47.    /* Call the ISR enter function to indicate an ISR is executing.  */
48.    PUSH    {lr}                            // Save ISR lr
49.    BL      _tx_execution_isr_enter         // Call the ISR enter function
50.    POP     {lr}                            // Recover ISR lr
51.#endif

52.    B       __tx_irq_processing_return      // Continue IRQ processing

53.__tx_thread_idle_system_save:

54.    /* Interrupt occurred in the scheduling loop.  */

55.    /* Not much to do here, just adjust the stack pointer, and return to IRQ
56.       processing.  */

57.    MOV     r10, #0                         // Clear stack limit

58.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
59.    /* Call the ISR enter function to indicate an ISR is executing.  */
60.    PUSH    {lr}                            // Save ISR lr
61.    BL      _tx_execution_isr_enter         // Call the ISR enter function
62.    POP     {lr}                            // Recover ISR lr
63.#endif

64.    ADD     sp, #16                         // Recover saved registers
65.    B       __tx_irq_processing_return      // Continue IRQ processing

2.1 _tx_thread_context_save函数主干

逐行分析:

1.    .global _tx_thread_context_save

说明:

.global 用于定义全局符号,以便于被其他文件引用;.local 用于定义局部符号, 仅在当前文件使用。

2.    .type   _tx_thread_context_save,function

说明:

.type用于设置符号的type属性,可选值为function或object(函数或对象(如全局变量))

3._tx_thread_context_save:

指示_tx_thread_context_save函数入口。

7.    PUSH    {r0-r3}                         // Save some working registers

功能:
将一些_tx_thread_context_save函数会使用到的寄存器入栈。

8.#ifdef TX_ENABLE_FIQ_SUPPORT
9.    CPSID   if                              // Disable FIQ interrupts
10.#endif

功能:
如果使能了FIQ中断,则在进入IRQ之后同时将FIQ失能,这个语句是将IRQ和FIQ全部失能(进入IRQ硬件会自动失能IRQ)。
CPSID用法如下:
在这里插入图片描述

11.    LDR     r3, =_tx_thread_system_state    // Pickup address of system state variable
12.    LDR     r2, [r3]                        // Pickup system state
13.    CMP     r2, #0                          // Is this the first interrupt?
14.    BEQ     __tx_thread_not_nested_save     // Yes, not a nested context save

操作如下:
(1)将_tx_thread_system_state变量的内存地址写入r3
(2)将r3存储的内存地址对应的内存数据写入r2
(3)判断r2的值是否为0
(4)如果(3)中结果为真则跳转到__tx_thread_not_nested_save处执行
功能如下:
判断_tx_thread_system_state变量的值是否为0,如果为0则执行__tx_thread_not_nested_save函数,也就是非嵌套中断的上下文保存函数。

16.    ADD     r2, #1                          // Increment the interrupt counter
17.    STR     r2, [r3]                        // Store it back in the variable

操作如下:
(1)将r2的值+1
(2)将r2的值写入r3存储的内存地址对应的内存
功能如下:
将_tx_thread_system_state的值+1。

20.    MRS     r0, SPSR                        // Pickup saved SPSR
21.    SUB     lr, #4                          // Adjust point of interrupt
22.    PUSH    {r0, r10, r12, lr}              // Store other registers

操作如下:
(1)将SPSR(程序状态保存寄存器)的值写入r0
(2)将lr的值-4
(3)将lr、r12、r10、r0依次入栈
功能如下:
保存上下文,lr寄存器保存的是当前中断打断位置的下一条指令,SPSR寄存器在发生嵌套中断时会被破坏,因此也需要入栈。

24.    MOV     r10, #0                         // Clear stack limit

操作如下:
(1)将r10的值设置为0
功能如下:
将栈极限设置为0(ThreadX构建了一个中断帧,r10表示栈极限)。

25.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
26.    /* Call the ISR enter function to indicate an ISR is executing.  */
27.    PUSH    {lr}                            // Save ISR lr
28.    BL      _tx_execution_isr_enter         // Call the ISR enter function
29.    POP     {lr}                            // Recover ISR lr
30.#endif

操作如下:
(1)将lr入栈
(2)跳转到_tx_execution_isr_enter函数,告知进入了中断服务函数
(3)将lr出栈
功能如下:
如果定义了TX_ENABLE_EXECUTION_CHANGE_NOTIFY或TX_EXECUTION_PROFILE_ENABLE宏,则会通过_tx_execution_isr_enter函数告知ThreadX内核现在进入了中断服务函数。

31.    B       __tx_irq_processing_return

操作/功能如下:
(1)跳转到__tx_irq_processing_return函数,结束上下文保存。

2.2 _tx_thread_context_save函数分支__tx_thread_not_nested_save非嵌套上下文保存

32.__tx_thread_not_nested_save:

33.    /* Otherwise, not nested, check to see if a thread was running.  */
34.    ADD     r2, #1                          // Increment the interrupt counter
35.    STR     r2, [r3]                        // Store it back in the variable
36.    LDR     r1, =_tx_thread_current_ptr     // Pickup address of current thread ptr
37.    LDR     r0, [r1]                        // Pickup current thread pointer
38.    CMP     r0, #0                          // Is it NULL?
39.    BEQ     __tx_thread_idle_system_save    // If so, interrupt occurred in
40.                                            //   scheduling loop - nothing needs saving!

操作如下:
(1)将r2的值+1
(2)将r2的值写入r3存储的内存地址对应的内存
(3)将_tx_thread_current_ptr变量的内存地址写入到r1
(4)将r1存储的内存地址对应的内存数据写入r0
(5)判断r0的值是否为0
(6)如果(5)中结果为真则跳转到__tx_thread_idle_system_save处执行
功能说明:
如果IRQ非嵌套中断,判断_tx_thread_current_ptr的值是否为空,如果发生在线程调度期间,则不需要保存任何数据(线程调度在非IRQ模式下执行,进入前线程栈已经保存)。

42.    MRS     r2, SPSR                        // Pickup saved SPSR
43.    SUB     lr, #4                          // Adjust point of interrupt
44.    PUSH    {r2, r10, r12, lr}              // Store other registers

操作如下:
(1)将SPSR(程序状态保存寄存器)值写入r2
(2)将lr的值-4
(3)依次将lr、r12、r10、r2入栈
功能如下:
保存上下文,lr寄存器保存的是当前中断打断位置的下一条指令,SPSR寄存器在发生嵌套中断时会被破坏,因此也需要入栈。

45.    MOV     r10, #0                         // Clear stack limit

(1)将r10的值设置为0
功能如下:
将栈极限设置为0(ThreadX线程构建了一个中断帧,r10表示栈极限)。

46.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
47.    /* Call the ISR enter function to indicate an ISR is executing.  */
48.    PUSH    {lr}                            // Save ISR lr
49.    BL      _tx_execution_isr_enter         // Call the ISR enter function
50.    POP     {lr}                            // Recover ISR lr
51.#endif

操作如下:
(1)将lr入栈
(2)跳转到_tx_execution_isr_enter函数,告知进入了中断服务函数
(3)将lr出栈
功能如下:
如果定义了TX_ENABLE_EXECUTION_CHANGE_NOTIFY或TX_EXECUTION_PROFILE_ENABLE宏,则会通过_tx_execution_isr_enter函数告知ThreadX内核现在进入了中断服务函数。

52.    B       __tx_irq_processing_return      // Continue IRQ processing

操作/功能如下:
(1)跳转到__tx_irq_processing_return函数,结束上下文保存。

2.3 _tx_thread_context_save函数分支__tx_thread_idle_system_save

53.__tx_thread_idle_system_save:

54.    /* Interrupt occurred in the scheduling loop.  */

55.    /* Not much to do here, just adjust the stack pointer, and return to IRQ
56.       processing.  */

57.    MOV     r10, #0                         // Clear stack limit

58.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
59.    /* Call the ISR enter function to indicate an ISR is executing.  */
60.    PUSH    {lr}                            // Save ISR lr
61.    BL      _tx_execution_isr_enter         // Call the ISR enter function
62.    POP     {lr}                            // Recover ISR lr
63.#endif

64.    ADD     sp, #16                         // Recover saved registers
65.    B       __tx_irq_processing_return      // Continue IRQ processing

操作/功能如下:
(1)将r10的值设置为0
(2)将lr入栈
(3)跳转到_tx_execution_isr_enter函数,告知进入了中断服务函数
(4)将lr出栈
(5)将sp的值+16(之前入栈的4个寄存器无需出栈,直接将栈指针+16空出栈空间即可)
(6)跳转到__tx_irq_processing_return函数,结束上下文保存

3 总结

_tx_thread_context_save函数主要功能就是实现上下文保存。上下文的保存又分为嵌套中断上下文保存和非嵌套中断上下文保存,如果是嵌套中断上下文保存则只需要入栈lr和SPSR即可,如果发生了非嵌套中断且不是发生在线程调度期间,要保存lr和SPSR,如果发生在线程调度期间则无需保存lr和SPSR(线程调度期间已经做了这部分工作)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NW嵌入式开发

感谢您的支持,让我们一起进步!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值