xref: /nrf52832-nimble/rt-thread/libcpu/arm/cortex-m4/context_rvds.S (revision 042d53a763ad75cb1465103098bb88c245d95138)
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; * 2012-01-01     aozima       support context switch load/store FPU register.
10; * 2013-06-18     aozima       add restore MSP feature.
11; * 2013-06-23     aozima       support lazy stack optimized.
12; * 2018-07-24     aozima       enhancement hard fault exception handler.
13; */
14
15;/**
16; * @addtogroup cortex-m4
17; */
18;/*@{*/
19
20SCB_VTOR        EQU     0xE000ED08               ; Vector Table Offset Register
21NVIC_INT_CTRL   EQU     0xE000ED04               ; interrupt control state register
22NVIC_SYSPRI2    EQU     0xE000ED20               ; system priority register (2)
23NVIC_PENDSV_PRI EQU     0x00FF0000               ; PendSV priority value (lowest)
24NVIC_PENDSVSET  EQU     0x10000000               ; value to trigger PendSV exception
25
26    AREA |.text|, CODE, READONLY, ALIGN=2
27    THUMB
28    REQUIRE8
29    PRESERVE8
30
31    IMPORT rt_thread_switch_interrupt_flag
32    IMPORT rt_interrupt_from_thread
33    IMPORT rt_interrupt_to_thread
34
35;/*
36; * rt_base_t rt_hw_interrupt_disable();
37; */
38rt_hw_interrupt_disable    PROC
39    EXPORT  rt_hw_interrupt_disable
40    MRS     r0, PRIMASK
41    CPSID   I
42    BX      LR
43    ENDP
44
45;/*
46; * void rt_hw_interrupt_enable(rt_base_t level);
47; */
48rt_hw_interrupt_enable    PROC
49    EXPORT  rt_hw_interrupt_enable
50    MSR     PRIMASK, r0
51    BX      LR
52    ENDP
53
54;/*
55; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
56; * r0 --> from
57; * r1 --> to
58; */
59rt_hw_context_switch_interrupt
60    EXPORT rt_hw_context_switch_interrupt
61rt_hw_context_switch    PROC
62    EXPORT rt_hw_context_switch
63
64    ; set rt_thread_switch_interrupt_flag to 1
65    LDR     r2, =rt_thread_switch_interrupt_flag
66    LDR     r3, [r2]
67    CMP     r3, #1
68    BEQ     _reswitch
69    MOV     r3, #1
70    STR     r3, [r2]
71
72    LDR     r2, =rt_interrupt_from_thread   ; set rt_interrupt_from_thread
73    STR     r0, [r2]
74
75_reswitch
76    LDR     r2, =rt_interrupt_to_thread     ; set rt_interrupt_to_thread
77    STR     r1, [r2]
78
79    LDR     r0, =NVIC_INT_CTRL              ; trigger the PendSV exception (causes context switch)
80    LDR     r1, =NVIC_PENDSVSET
81    STR     r1, [r0]
82    BX      LR
83    ENDP
84
85; r0 --> switch from thread stack
86; r1 --> switch to thread stack
87; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
88PendSV_Handler   PROC
89    EXPORT PendSV_Handler
90
91    ; disable interrupt to protect context switch
92    MRS     r2, PRIMASK
93    CPSID   I
94
95    ; get rt_thread_switch_interrupt_flag
96    LDR     r0, =rt_thread_switch_interrupt_flag
97    LDR     r1, [r0]
98    CBZ     r1, pendsv_exit         ; pendsv already handled
99
100    ; clear rt_thread_switch_interrupt_flag to 0
101    MOV     r1, #0x00
102    STR     r1, [r0]
103
104    LDR     r0, =rt_interrupt_from_thread
105    LDR     r1, [r0]
106    CBZ     r1, switch_to_thread    ; skip register save at the first time
107
108    MRS     r1, psp                 ; get from thread stack pointer
109
110    IF      {FPU} != "SoftVFP"
111    TST     lr, #0x10               ; if(!EXC_RETURN[4])
112    VSTMFDEQ  r1!, {d8 - d15}       ; push FPU register s16~s31
113    ENDIF
114
115    STMFD   r1!, {r4 - r11}         ; push r4 - r11 register
116
117    IF      {FPU} != "SoftVFP"
118    MOV     r4, #0x00               ; flag = 0
119
120    TST     lr, #0x10               ; if(!EXC_RETURN[4])
121    MOVEQ   r4, #0x01               ; flag = 1
122
123    STMFD   r1!, {r4}               ; push flag
124    ENDIF
125
126    LDR     r0, [r0]
127    STR     r1, [r0]                ; update from thread stack pointer
128
129switch_to_thread
130    LDR     r1, =rt_interrupt_to_thread
131    LDR     r1, [r1]
132    LDR     r1, [r1]                ; load thread stack pointer
133
134    IF      {FPU} != "SoftVFP"
135    LDMFD   r1!, {r3}               ; pop flag
136    ENDIF
137
138    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register
139
140    IF      {FPU} != "SoftVFP"
141    CMP     r3,  #0                 ; if(flag_r3 != 0)
142    VLDMFDNE  r1!, {d8 - d15}       ; pop FPU register s16~s31
143    ENDIF
144
145    MSR     psp, r1                 ; update stack pointer
146
147    IF      {FPU} != "SoftVFP"
148    ORR     lr, lr, #0x10           ; lr |=  (1 << 4), clean FPCA.
149    CMP     r3,  #0                 ; if(flag_r3 != 0)
150    BICNE   lr, lr, #0x10           ; lr &= ~(1 << 4), set FPCA.
151    ENDIF
152
153pendsv_exit
154    ; restore interrupt
155    MSR     PRIMASK, r2
156
157    ORR     lr, lr, #0x04
158    BX      lr
159    ENDP
160
161;/*
162; * void rt_hw_context_switch_to(rt_uint32 to);
163; * r0 --> to
164; * this fucntion is used to perform the first thread switch
165; */
166rt_hw_context_switch_to    PROC
167    EXPORT rt_hw_context_switch_to
168    ; set to thread
169    LDR     r1, =rt_interrupt_to_thread
170    STR     r0, [r1]
171
172    IF      {FPU} != "SoftVFP"
173    ; CLEAR CONTROL.FPCA
174    MRS     r2, CONTROL             ; read
175    BIC     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    ; trigger the PendSV exception (causes context switch)
197    LDR     r0, =NVIC_INT_CTRL
198    LDR     r1, =NVIC_PENDSVSET
199    STR     r1, [r0]
200
201    ; restore MSP
202    LDR     r0, =SCB_VTOR
203    LDR     r0, [r0]
204    LDR     r0, [r0]
205    MSR     msp, r0
206
207    ; enable interrupts at processor level
208    CPSIE   F
209    CPSIE   I
210
211    ; never reach here!
212    ENDP
213
214; compatible with old version
215rt_hw_interrupt_thread_switch PROC
216    EXPORT rt_hw_interrupt_thread_switch
217    BX      lr
218    ENDP
219
220    IMPORT rt_hw_hard_fault_exception
221    EXPORT HardFault_Handler
222HardFault_Handler    PROC
223
224    ; get current context
225    TST     lr, #0x04               ; if(!EXC_RETURN[2])
226    ITE     EQ
227    MRSEQ   r0, msp                 ; [2]=0 ==> Z=1, get fault context from handler.
228    MRSNE   r0, psp                 ; [2]=1 ==> Z=0, get fault context from thread.
229
230    STMFD   r0!, {r4 - r11}         ; push r4 - r11 register
231    IF      {FPU} != "SoftVFP"
232    STMFD   r0!, {lr}               ; push dummy for flag
233    ENDIF
234    STMFD   r0!, {lr}               ; push exec_return register
235
236    TST     lr, #0x04               ; if(!EXC_RETURN[2])
237    ITE     EQ
238    MSREQ   msp, r0                 ; [2]=0 ==> Z=1, update stack pointer to MSP.
239    MSRNE   psp, r0                 ; [2]=1 ==> Z=0, update stack pointer to PSP.
240
241    PUSH    {lr}
242    BL      rt_hw_hard_fault_exception
243    POP     {lr}
244
245    ORR     lr, lr, #0x04
246    BX      lr
247    ENDP
248
249    ALIGN   4
250
251    END
252