ThreadX源码:Cortex-A7的tx_thread_context_restore.S(线程上下文恢复)汇编代码分析
0 参考资料
Cortex M3权威指南(中文).pdf(可以参考ARM指令集用法)
1 前言
tx_thread_context_restore.S是用来实现Cortex-A7下线程上下文恢复的函数所在汇编文件。
2 源码分析
2.1 概述
_tx_thread_context_restore函数用于在线程被中断打断之后恢复上下文,根据打断点所处的位置分为2种情况:
(1)非嵌套中断(首次触发中断,也就是从线程切换到IRQ模式)
(2)嵌套中断
非嵌套中断又分为几种情况:
(1)如果中断期间未发生诸如发送信号量等需要引起线程调度的操作,则将返回原来的被中断线程处继续执行
(2)如果中断期间发生诸如发送信号量等需要引起线程调度的操作,则跳转/返回到线程调度,以最快的速度保证最高优先级线程得到响应
(3)中断期间线程未运行(正在执行线程调度操作),则无需恢复现场
2.2 源码逐行分析
源码如下:
1. .global _tx_thread_context_restore
2. .type _tx_thread_context_restore,function
3._tx_thread_context_restore:4. /* Lockout interrupts. */5.#ifdef TX_ENABLE_FIQ_SUPPORT
6. CPSID if // Disable IRQ and FIQ interrupts
7.#else
8. CPSID i // Disable IRQ interrupts
9.#endif10.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
11. /* Call the ISR exit function to indicate an ISR is complete. */
12. BL _tx_execution_isr_exit // Call the ISR exit function
13.#endif14. /* Determine if interrupts are nested. */15. LDR r3, =_tx_thread_system_state // Pickup address of system state variable
16. LDR r2, [r3] // Pickup system state
17. SUB r2, r2, #1 // Decrement the counter
18. STR r2, [r3] // Store the counter
19. CMP r2, #0 // Was this the first interrupt?
20. BEQ __tx_thread_not_nested_restore // If so, not a nested restore21. /* Interrupts are nested. */22. /* Just recover the saved registers and return to the point of
23. interrupt. */24. POP {r0, r10, r12, lr} // Recover SPSR, POI, and scratch regs
25. MSR SPSR_cxsf, r0 // Put SPSR back
26. POP {r0-r3} // Recover r0-r3
27. MOVS pc, lr // Return to point of interrupt28.__tx_thread_not_nested_restore:29. /* Determine if a thread was interrupted and no preemption is required. */30. LDR r1, =_tx_thread_current_ptr // Pickup address of current thread ptr
31. LDR r0, [r1] // Pickup actual current thread pointer
32. CMP r0, #0 // Is it NULL?
33. BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted34. LDR r3, =_tx_thread_preempt_disable // Pickup preempt disable address
35. LDR r2, [r3] // Pickup actual preempt disable flag
36. CMP r2, #0 // Is it set?
37. BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread
38. LDR r3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr
39. LDR r2, [r3] // Pickup actual execute thread pointer
40. CMP r0, r2 // Is the same thread highest priority?
41. BNE __tx_thread_preempt_restore // No, preemption needs to happen42.__tx_thread_no_preempt_restore:43. /* Recover the saved context and return to the point of interrupt. */44. /* Pickup the saved stack pointer. */45. /* Recover the saved context and return to the point of interrupt. */
46. POP {r0, r10, r12, lr} // Recover SPSR, POI, and scratch regs
47. MSR SPSR_cxsf, r0 // Put SPSR back
48. POP {r0-r3} // Recover r0-r3
49. MOVS pc, lr // Return to point of interrupt50.__tx_thread_preempt_restore:51. POP {r3, r10, r12, lr} // Recover temporarily saved registers
52. MOV r1, lr // Save lr (point of interrupt)
53. CPS #SVC_MODE // Enter SVC mode
54. STR r1, [sp, #-4]! // Save point of interrupt
55. PUSH {r4-r12, lr} // Save upper half of registers
56. MOV r4, r3 // Save SPSR in r4
57. CPS #IRQ_MODE // Enter IRQ mode
58. POP {r0-r3} // Recover r0-r3
59. CPS #SVC_MODE // Enter SVC mode
60. PUSH {r0-r3} // Save r0-r3 on thread's stack61. LDR r1, =_tx_thread_current_ptr // Pickup address of current thread ptr
62. LDR r0, [r1] // Pickup current thread pointer63.#ifdef TX_ENABLE_VFP_SUPPORT
64. LDR r2, [r0, #144] // Pickup the VFP enabled flag
65. CMP r2, #0 // Is the VFP enabled?
66. BEQ _tx_skip_irq_vfp_save // No, skip VFP IRQ save
67. VMRS r2, FPSCR // Pickup the FPSCR
68. STR r2, [sp, #-4]! // Save FPSCR
69. VSTMDB sp!, {D16-D31} // Save D16-D31
70. VSTMDB sp!, {D0-D15} // Save D0-D15
71._tx_skip_irq_vfp_save:
72.#endif73. MOV r3, #1 // Build interrupt stack type
74. PUSH {r3, r4} // Save interrupt stack type and SPSR
75. STR sp, [r0, #8] // Save stack pointer in thread control
76. // block77. /* Save the remaining time-slice and disable it. */
78. LDR r3, =_tx_timer_time_slice // Pickup time-slice variable address
79. LDR r2, [r3] // Pickup time-slice
80. CMP r2, #0 // Is it active?
81. BEQ __tx_thread_dont_save_ts // No, don't save it
82. STR r2, [r0, #24] // Save thread's time-slice
83. MOV r2, #0 // Clear value
84. STR r2, [r3] // Disable global time-slice flag85.__tx_thread_dont_save_ts:86. /* Clear the current task pointer. */
87. MOV r0, #0 // NULL value
88. STR r0, [r1] // Clear current thread pointer89. /* Return to the scheduler. */
90. B _tx_thread_schedule // Return to scheduler91.__tx_thread_idle_system_restore:92. /* Just return back to the scheduler! */
93. CPS #SVC_MODE // Enter SVC mode
94. B _tx_thread_schedule // Return to scheduler
2.2.1 _tx_thread_context_restore函数主干
1. .global _tx_thread_context_restore
2. .type _tx_thread_context_restore,function
3._tx_thread_context_restore:4. /* Lockout interrupts. */5.#ifdef TX_ENABLE_FIQ_SUPPORT
6. CPSID if // Disable IRQ and FIQ interrupts
7.#else
8. CPSID i // Disable IRQ interrupts
9.#endif10.#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))
11. /* Call the ISR exit function to indicate an ISR is complete. */
12. BL _tx_execution_isr_exit // Call the ISR exit function
13.#endif
功能:
将IRQ失能,如果定义了TX_ENABLE_EXECUTION_CHANGE_NOTIFY或TX_EXECUTION_PROFILE_ENABLE宏则将执行_tx_execution_isr_exit函数,告知内核退出了中断服务函数。
14. /* Determine if interrupts are nested. */15. LDR r3, =_tx_thread_system_state // Pickup address of system state variable
16. LDR r2, [r3] // Pickup system state
17. SUB r2, r2, #1 // Decrement the counter
18. STR r2, [r3] // Store the counter
19. CMP r2, #0 // Was this the first interrupt?
20. BEQ __tx_thread_not_nested_restore // If so, not a nested restore
功能:
根据变量_tx_thread_system_state的值判断当前是否属于非嵌套中断,如果属于非嵌套中断则跳转到__tx_thread_not_nested_restore执行。
21. /* Interrupts are nested. */22. /* Just recover the saved registers and return to the point of
23. interrupt. */24. POP {r0, r10, r12, lr} // Recover SPSR, POI, and scratch regs
25. MSR SPSR_cxsf, r0 // Put SPSR back
26. POP {r0-r3} // Recover r0-r3
27. MOVS pc, lr // Return to point of interrupt
功能:
如果是嵌套中断则会执行上述语句,也就是将中断上下文恢复,跳转到被嵌套中断打断前的位置。
SPSR_cxsf表示影响的位为c、x、s、f,含义如下:
c - control field mask byte(xPSR[7:0])
x - extension field mask byte(xPSR[15:8])
s - status field mask byte(xPSR[23:16)
f - flags field mask byte(xPSR[31:24]).
2.2.1 _tx_thread_context_restore函数分支__tx_thread_not_nested_restore非嵌套中断上下文恢复
28.__tx_thread_not_nested_restore:29. /* Determine if a thread was interrupted and no preemption is required. */30. LDR r1, =_tx_thread_current_ptr // Pickup address of current thread ptr
31. LDR r0, [r1] // Pickup actual current thread pointer
32. CMP r0, #0 // Is it NULL?
33. BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted34. LDR r3, =_tx_thread_preempt_disable // Pickup preempt disable address
35. LDR r2, [r3] // Pickup actual preempt disable flag
36. CMP r2, #0 // Is it set?
37. BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread
38. LDR r3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr
39. LDR r2, [r3] // Pickup actual execute thread pointer
40. CMP r0, r2 // Is the same thread highest priority?
41. BNE __tx_thread_preempt_restore // No, preemption needs to happen
功能:
如果中断打断点属于非嵌套中断,表明需要执行的中断已经尽数处理完,执行最后的工作恢复中断打断前的现场。被中断打断的现场又分为3种情况:
(1)如果中断期间未发生诸如发送信号量等需要引起线程调度的操作,则将返回原来的被中断线程处继续执行,执行函数__tx_thread_no_preempt_restore
(2)如果中断期间发生诸如发送信号量等需要引起线程调度的操作,则跳转/返回到线程调度,以最快的速度保证最高优先级线程得到响应,执行函数__tx_thread_preempt_restore
(3)中断期间线程未运行(正在执行线程调度操作),则无需恢复现场,执行函数__tx_thread_idle_system_restore
2.2.1.1 _tx_thread_context_restore函数分支__tx_thread_not_nested_restore非嵌套中断上下文恢复分支__tx_thread_no_preempt_restore无高优先级线程需要调度
42.__tx_thread_no_preempt_restore:43. /* Recover the saved context and return to the point of interrupt. */44. /* Pickup the saved stack pointer. */45. /* Recover the saved context and return to the point of interrupt. */
46. POP {r0, r10, r12, lr} // Recover SPSR, POI, and scratch regs
47. MSR SPSR_cxsf, r0 // Put SPSR back
48. POP {r0-r3} // Recover r0-r3
49. MOVS pc, lr // Return to point of interrupt
功能:
如果中断之后没有高优先级线程需要调度,则将中断打断前的现场恢复即可。
2.2.1.2 _tx_thread_context_restore函数分支__tx_thread_not_nested_restore非嵌套中断上下文恢复分支__tx_thread_preempt_restore有高优先级线程需要调度
50.__tx_thread_preempt_restore:51. POP {r3, r10, r12, lr} // Recover temporarily saved registers
52. MOV r1, lr // Save lr (point of interrupt)
53. CPS #SVC_MODE // Enter SVC mode
54. STR r1, [sp, #-4]! // Save point of interrupt
55. PUSH {r4-r12, lr} // Save upper half of registers
56. MOV r4, r3 // Save SPSR in r4
57. CPS #IRQ_MODE // Enter IRQ mode
58. POP {r0-r3} // Recover r0-r3
59. CPS #SVC_MODE // Enter SVC mode
60. PUSH {r0-r3} // Save r0-r3 on thread's stack61. LDR r1, =_tx_thread_current_ptr // Pickup address of current thread ptr
62. LDR r0, [r1] // Pickup current thread pointer63.#ifdef TX_ENABLE_VFP_SUPPORT
64. LDR r2, [r0, #144] // Pickup the VFP enabled flag
65. CMP r2, #0 // Is the VFP enabled?
66. BEQ _tx_skip_irq_vfp_save // No, skip VFP IRQ save
67. VMRS r2, FPSCR // Pickup the FPSCR
68. STR r2, [sp, #-4]! // Save FPSCR
69. VSTMDB sp!, {D16-D31} // Save D16-D31
70. VSTMDB sp!, {D0-D15} // Save D0-D15
71._tx_skip_irq_vfp_save:
72.#endif73. MOV r3, #1 // Build interrupt stack type
74. PUSH {r3, r4} // Save interrupt stack type and SPSR
75. STR sp, [r0, #8] // Save stack pointer in thread control
76. // block77. /* Save the remaining time-slice and disable it. */
78. LDR r3, =_tx_timer_time_slice // Pickup time-slice variable address
79. LDR r2, [r3] // Pickup time-slice
80. CMP r2, #0 // Is it active?
81. BEQ __tx_thread_dont_save_ts // No, don't save it
82. STR r2, [r0, #24] // Save thread's time-slice
83. MOV r2, #0 // Clear value
84. STR r2, [r3] // Disable global time-slice flag85.__tx_thread_dont_save_ts:86. /* Clear the current task pointer. */
87. MOV r0, #0 // NULL value
88. STR r0, [r1] // Clear current thread pointer89. /* Return to the scheduler. */
90. B _tx_thread_schedule // Return to scheduler
功能:
如果中断执行之后有高优先级中断需要执行,则需要执行__tx_thread_preempt_restore函数,然后执行_tx_thread_schedule函数,执行线程调度
2.2.1.3 _tx_thread_context_restore函数分支__tx_thread_not_nested_restore非嵌套中断上下文恢复分支__tx_thread_idle_system_restore中断前无线程运行现场恢复
91.__tx_thread_idle_system_restore:92. /* Just return back to the scheduler! */
93. CPS #SVC_MODE // Enter SVC mode
94. B _tx_thread_schedule // Return to scheduler
功能:
如果中断前线程未在运行(如处于线程调度期间),则跳转到_tx_thread_schedule函数,执行线程调度
3 总结
_tx_thread_context_restore函数主要功能就是根据中断打断位置的不同来恢复现场,根据中断打断位置分为非嵌套中断和嵌套中断。非嵌套中断下还涉及到高优先级线程调度问题(如中断期间发送了信号量等操作,需要执行线程调度),嵌套中断下则只需要恢复中断上下文即可。