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(线程调度期间已经做了这部分工作)。