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; * 2024-08-13     Evlers       allows rewrite to interrupt enable/disable api to support independent interrupts management
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     0xFFFF0000               ; PendSV and SysTick priority value (lowest)
25NVIC_PENDSVSET  EQU     0x10000000               ; value to trigger PendSV exception
26
27    AREA |.text|, CODE, READONLY, ALIGN=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; */
39rt_hw_interrupt_disable    PROC
40    EXPORT  rt_hw_interrupt_disable [WEAK]
41    MRS     r0, PRIMASK
42    CPSID   I
43    BX      LR
44    ENDP
45
46;/*
47; * void rt_hw_interrupt_enable(rt_base_t level);
48; */
49rt_hw_interrupt_enable    PROC
50    EXPORT  rt_hw_interrupt_enable [WEAK]
51    MSR     PRIMASK, r0
52    BX      LR
53    ENDP
54
55;/*
56; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
57; * r0 --> from
58; * r1 --> to
59; */
60rt_hw_context_switch_interrupt
61    EXPORT rt_hw_context_switch_interrupt
62rt_hw_context_switch    PROC
63    EXPORT rt_hw_context_switch
64
65    ; set rt_thread_switch_interrupt_flag to 1
66    LDR     r2, =rt_thread_switch_interrupt_flag
67    LDR     r3, [r2]
68    CMP     r3, #1
69    BEQ     _reswitch
70    MOV     r3, #1
71    STR     r3, [r2]
72
73    LDR     r2, =rt_interrupt_from_thread   ; set rt_interrupt_from_thread
74    STR     r0, [r2]
75
76_reswitch
77    LDR     r2, =rt_interrupt_to_thread     ; set rt_interrupt_to_thread
78    STR     r1, [r2]
79
80    LDR     r0, =NVIC_INT_CTRL              ; trigger the PendSV exception (causes context switch)
81    LDR     r1, =NVIC_PENDSVSET
82    STR     r1, [r0]
83    BX      LR
84    ENDP
85
86; r0 --> switch from thread stack
87; r1 --> switch to thread stack
88; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
89PendSV_Handler   PROC
90    EXPORT PendSV_Handler
91
92    ; disable interrupt to protect context switch
93    MRS     r2, PRIMASK
94    CPSID   I
95
96    ; get rt_thread_switch_interrupt_flag
97    LDR     r0, =rt_thread_switch_interrupt_flag
98    LDR     r1, [r0]
99    CBZ     r1, pendsv_exit         ; pendsv already handled
100
101    ; clear rt_thread_switch_interrupt_flag to 0
102    MOV     r1, #0x00
103    STR     r1, [r0]
104
105    LDR     r0, =rt_interrupt_from_thread
106    LDR     r1, [r0]
107    CBZ     r1, switch_to_thread    ; skip register save at the first time
108
109    MRS     r1, psp                 ; get from thread stack pointer
110
111    IF      {FPU} != "SoftVFP"
112    TST     lr, #0x10               ; if(!EXC_RETURN[4])
113    VSTMFDEQ  r1!, {d8 - d15}       ; push FPU register s16~s31
114    ENDIF
115
116    STMFD   r1!, {r4 - r11}         ; push r4 - r11 register
117
118    IF      {FPU} != "SoftVFP"
119    MOV     r4, #0x00               ; flag = 0
120
121    TST     lr, #0x10               ; if(!EXC_RETURN[4])
122    MOVEQ   r4, #0x01               ; flag = 1
123
124    STMFD   r1!, {r4}               ; push flag
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      {FPU} != "SoftVFP"
136    LDMFD   r1!, {r3}               ; pop flag
137    ENDIF
138
139    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register
140
141    IF      {FPU} != "SoftVFP"
142    CMP     r3,  #0                 ; if(flag_r3 != 0)
143    VLDMFDNE  r1!, {d8 - d15}       ; pop FPU register s16~s31
144    ENDIF
145
146    MSR     psp, r1                 ; update stack pointer
147
148    IF      {FPU} != "SoftVFP"
149    ORR     lr, lr, #0x10           ; lr |=  (1 << 4), clean FPCA.
150    CMP     r3,  #0                 ; if(flag_r3 != 0)
151    BICNE   lr, lr, #0x10           ; lr &= ~(1 << 4), set FPCA.
152    ENDIF
153
154pendsv_exit
155    ; restore interrupt
156    MSR     PRIMASK, r2
157
158    ORR     lr, lr, #0x04
159    BX      lr
160    ENDP
161
162;/*
163; * void rt_hw_context_switch_to(rt_uint32 to);
164; * r0 --> to
165; * this fucntion is used to perform the first thread switch
166; */
167rt_hw_context_switch_to    PROC
168    EXPORT rt_hw_context_switch_to
169    ; set to thread
170    LDR     r1, =rt_interrupt_to_thread
171    STR     r0, [r1]
172
173    IF      {FPU} != "SoftVFP"
174    ; CLEAR CONTROL.FPCA
175    MRS     r2, CONTROL             ; read
176    BIC     r2, #0x04               ; modify
177    MSR     CONTROL, r2             ; write-back
178    ENDIF
179
180    ; set from thread to 0
181    LDR     r1, =rt_interrupt_from_thread
182    MOV     r0, #0x0
183    STR     r0, [r1]
184
185    ; set interrupt flag to 1
186    LDR     r1, =rt_thread_switch_interrupt_flag
187    MOV     r0, #1
188    STR     r0, [r1]
189
190    ; set the PendSV and SysTick exception priority
191    LDR     r0, =NVIC_SYSPRI2
192    LDR     r1, =NVIC_PENDSV_PRI
193    LDR.W   r2, [r0,#0x00]       ; read
194    ORR     r1,r1,r2             ; modify
195    STR     r1, [r0]             ; write-back
196
197    ; trigger the PendSV exception (causes context switch)
198    LDR     r0, =NVIC_INT_CTRL
199    LDR     r1, =NVIC_PENDSVSET
200    STR     r1, [r0]
201
202    ; restore MSP
203    LDR     r0, =SCB_VTOR
204    LDR     r0, [r0]
205    LDR     r0, [r0]
206    MSR     msp, r0
207
208    ; enable interrupts at processor level
209    CPSIE   F
210    CPSIE   I
211
212    ; clear the BASEPRI register to disable masking priority
213    MOV     r0, #0x00
214    MSR     BASEPRI, r0
215
216    ; ensure PendSV exception taken place before subsequent operation
217    DSB
218    ISB
219
220    ; never reach here!
221    ENDP
222
223; compatible with old version
224rt_hw_interrupt_thread_switch PROC
225    EXPORT rt_hw_interrupt_thread_switch
226    BX      lr
227    ENDP
228
229    IMPORT rt_hw_hard_fault_exception
230    EXPORT HardFault_Handler
231HardFault_Handler    PROC
232
233    ; get current context
234    TST     lr, #0x04               ; if(!EXC_RETURN[2])
235    ITE     EQ
236    MRSEQ   r0, msp                 ; [2]=0 ==> Z=1, get fault context from handler.
237    MRSNE   r0, psp                 ; [2]=1 ==> Z=0, get fault context from thread.
238
239    IF      {FPU} != "SoftVFP"
240    TST     lr, #0x10               ; if(!EXC_RETURN[4])
241    VSTMFDEQ r0!, {d8 - d15}        ; push FPU register s16~s31
242    ENDIF
243
244    STMFD   r0!, {r4 - r11}         ; push r4 - r11 register
245
246    IF      {FPU} != "SoftVFP"
247    MOV     r4, #0x00               ; flag = 0
248
249    TST     lr, #0x10               ; if(!EXC_RETURN[4])
250    MOVEQ   r4, #0x01               ; flag = 1
251    STMFD   r0!, {r4}               ; push flag
252    ENDIF
253
254    STMFD   r0!, {lr}               ; push exec_return register
255
256    TST     lr, #0x04               ; if(!EXC_RETURN[2])
257    ITE     EQ
258    MSREQ   msp, r0                 ; [2]=0 ==> Z=1, update stack pointer to MSP.
259    MSRNE   psp, r0                 ; [2]=1 ==> Z=0, update stack pointer to PSP.
260
261    PUSH    {lr}
262    BL      rt_hw_hard_fault_exception
263    POP     {lr}
264
265    ORR     lr, lr, #0x04
266    BX      lr
267    ENDP
268
269    ALIGN   4
270
271    END
272