xref: /nrf52832-nimble/rt-thread/libcpu/arm/cortex-m7/context_iar.S (revision 104654410c56c573564690304ae786df310c91fc)
1;/*
2; * Copyright (c) 2006-2018, RT-Thread Development Team
3; *
4; * SPDX-License-Identifier: Apache-2.0
5; *
6; * Change Logs:
7; * Date           Author       Notes
8; * 2009-01-17     Bernard      first version
9; * 2009-09-27     Bernard      add protect when contex switch occurs
10; * 2012-01-01     aozima       support context switch load/store FPU register.
11; * 2013-06-18     aozima       add restore MSP feature.
12; * 2013-06-23     aozima       support lazy stack optimized.
13; * 2018-07-24     aozima       enhancement hard fault exception handler.
14; */
15
16;/**
17; * @addtogroup cortex-m4
18; */
19;/*@{*/
20
21SCB_VTOR        EQU     0xE000ED08               ; Vector Table Offset Register
22NVIC_INT_CTRL   EQU     0xE000ED04               ; interrupt control state register
23NVIC_SYSPRI2    EQU     0xE000ED20               ; system priority register (2)
24NVIC_PENDSV_PRI EQU     0x00FF0000               ; PendSV priority value (lowest)
25NVIC_PENDSVSET  EQU     0x10000000               ; value to trigger PendSV exception
26
27    SECTION    .text:CODE(2)
28    THUMB
29    REQUIRE8
30    PRESERVE8
31
32    IMPORT rt_thread_switch_interrupt_flag
33    IMPORT rt_interrupt_from_thread
34    IMPORT rt_interrupt_to_thread
35
36;/*
37; * rt_base_t rt_hw_interrupt_disable();
38; */
39    EXPORT rt_hw_interrupt_disable
40rt_hw_interrupt_disable:
41    MRS     r0, PRIMASK
42    CPSID   I
43    BX      LR
44
45;/*
46; * void rt_hw_interrupt_enable(rt_base_t level);
47; */
48    EXPORT  rt_hw_interrupt_enable
49rt_hw_interrupt_enable:
50    MSR     PRIMASK, r0
51    BX      LR
52
53;/*
54; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
55; * r0 --> from
56; * r1 --> to
57; */
58    EXPORT rt_hw_context_switch_interrupt
59    EXPORT rt_hw_context_switch
60rt_hw_context_switch_interrupt:
61rt_hw_context_switch:
62    ; set rt_thread_switch_interrupt_flag to 1
63    LDR     r2, =rt_thread_switch_interrupt_flag
64    LDR     r3, [r2]
65    CMP     r3, #1
66    BEQ     _reswitch
67    MOV     r3, #1
68    STR     r3, [r2]
69
70    LDR     r2, =rt_interrupt_from_thread   ; set rt_interrupt_from_thread
71    STR     r0, [r2]
72
73_reswitch
74    LDR     r2, =rt_interrupt_to_thread     ; set rt_interrupt_to_thread
75    STR     r1, [r2]
76
77    LDR     r0, =NVIC_INT_CTRL              ; trigger the PendSV exception (causes context switch)
78    LDR     r1, =NVIC_PENDSVSET
79    STR     r1, [r0]
80    BX      LR
81
82; r0 --> switch from thread stack
83; r1 --> switch to thread stack
84; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
85    EXPORT PendSV_Handler
86PendSV_Handler:
87
88    ; disable interrupt to protect context switch
89    MRS     r2, PRIMASK
90    CPSID   I
91
92    ; get rt_thread_switch_interrupt_flag
93    LDR     r0, =rt_thread_switch_interrupt_flag
94    LDR     r1, [r0]
95    CBZ     r1, pendsv_exit         ; pendsv already handled
96
97    ; clear rt_thread_switch_interrupt_flag to 0
98    MOV     r1, #0x00
99    STR     r1, [r0]
100
101    LDR     r0, =rt_interrupt_from_thread
102    LDR     r1, [r0]
103    CBZ     r1, switch_to_thread    ; skip register save at the first time
104
105    MRS     r1, psp                 ; get from thread stack pointer
106
107#if defined ( __ARMVFP__ )
108    TST     lr, #0x10               ; if(!EXC_RETURN[4])
109    BNE     skip_push_fpu
110    VSTMDB  r1!, {d8 - d15}         ; push FPU register s16~s31
111skip_push_fpu
112#endif
113
114    STMFD   r1!, {r4 - r11}         ; push r4 - r11 register
115
116#if defined ( __ARMVFP__ )
117    MOV     r4, #0x00               ; flag = 0
118    TST     lr, #0x10               ; if(!EXC_RETURN[4])
119    BNE     push_flag
120    MOV     r4, #0x01               ; flag = 1
121push_flag
122    ;STMFD   r1!, {r4}              ; push flag
123    SUB     r1, r1, #0x04
124    STR     r4, [r1]
125#endif
126
127    LDR     r0, [r0]
128    STR     r1, [r0]                ; update from thread stack pointer
129
130switch_to_thread
131    LDR     r1, =rt_interrupt_to_thread
132    LDR     r1, [r1]
133    LDR     r1, [r1]                ; load thread stack pointer
134
135#if defined ( __ARMVFP__ )
136    LDMFD   r1!, {r3}               ; pop flag
137#endif
138
139    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register
140
141#if defined ( __ARMVFP__ )
142    CBZ     r3, skip_pop_fpu
143    VLDMIA  r1!, {d8 - d15}         ; pop FPU register s16~s31
144skip_pop_fpu
145#endif
146
147    MSR     psp, r1                 ; update stack pointer
148
149#if defined ( __ARMVFP__ )
150    ORR     lr, lr, #0x10           ; lr |=  (1 << 4), clean FPCA.
151    CBZ     r3, return_without_fpu  ; if(flag_r3 != 0)
152    BIC     lr, lr, #0x10           ; lr &= ~(1 << 4), set FPCA.
153return_without_fpu
154#endif
155
156pendsv_exit
157    ; restore interrupt
158    MSR     PRIMASK, r2
159
160    ORR     lr, lr, #0x04
161    BX      lr
162
163;/*
164; * void rt_hw_context_switch_to(rt_uint32 to);
165; * r0 --> to
166; */
167    EXPORT rt_hw_context_switch_to
168rt_hw_context_switch_to:
169    LDR     r1, =rt_interrupt_to_thread
170    STR     r0, [r1]
171
172#if defined ( __ARMVFP__ )
173    ; CLEAR CONTROL.FPCA
174    MRS     r2, CONTROL             ; read
175    BIC     r2, r2, #0x04           ; modify
176    MSR     CONTROL, r2             ; write-back
177#endif
178
179    ; set from thread to 0
180    LDR     r1, =rt_interrupt_from_thread
181    MOV     r0, #0x0
182    STR     r0, [r1]
183
184    ; set interrupt flag to 1
185    LDR     r1, =rt_thread_switch_interrupt_flag
186    MOV     r0, #1
187    STR     r0, [r1]
188
189    ; set the PendSV exception priority
190    LDR     r0, =NVIC_SYSPRI2
191    LDR     r1, =NVIC_PENDSV_PRI
192    LDR.W   r2, [r0,#0x00]       ; read
193    ORR     r1,r1,r2             ; modify
194    STR     r1, [r0]             ; write-back
195
196    LDR     r0, =NVIC_INT_CTRL      ; trigger the PendSV exception (causes context switch)
197    LDR     r1, =NVIC_PENDSVSET
198    STR     r1, [r0]
199
200    ; restore MSP
201    LDR     r0, =SCB_VTOR
202    LDR     r0, [r0]
203    LDR     r0, [r0]
204    NOP
205    MSR     msp, r0
206
207    ; enable interrupts at processor level
208    CPSIE   F
209    CPSIE   I
210
211    ; never reach here!
212
213; compatible with old version
214    EXPORT rt_hw_interrupt_thread_switch
215rt_hw_interrupt_thread_switch:
216    BX      lr
217
218    IMPORT rt_hw_hard_fault_exception
219    EXPORT HardFault_Handler
220HardFault_Handler:
221
222    ; get current context
223    MRS     r0, msp                 ; get fault context from handler.
224    TST     lr, #0x04               ; if(!EXC_RETURN[2])
225    BEQ     _get_sp_done
226    MRS     r0, psp                 ; get fault context from thread.
227_get_sp_done
228
229    STMFD   r0!, {r4 - r11}         ; push r4 - r11 register
230    ;STMFD   r0!, {lr}               ; push exec_return register
231#if defined ( __ARMVFP__ )
232    SUB     r0, r0, #0x04           ; push dummy for flag
233    STR     lr, [r0]
234#endif
235    SUB     r0, r0, #0x04
236    STR     lr, [r0]
237
238    TST     lr, #0x04               ; if(!EXC_RETURN[2])
239    BEQ     _update_msp
240    MSR     psp, r0                 ; update stack pointer to PSP.
241    B       _update_done
242_update_msp
243    MSR     msp, r0                 ; update stack pointer to MSP.
244_update_done
245
246    PUSH    {lr}
247    BL      rt_hw_hard_fault_exception
248    POP     {lr}
249
250    ORR     lr, lr, #0x04
251    BX      lr
252
253    END
254