1/* 2 * Copyright (c) 2006-2024, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2024-03-01 Wangyuqiang first version 9 */ 10 11.syntax unified 12.text 13 14.globl rt_thread_switch_interrupt_flag 15.globl rt_interrupt_from_thread 16.globl rt_interrupt_to_thread 17.globl rt_interrupt_enter 18.globl rt_interrupt_leave 19.globl rt_hw_trap_irq 20 21/* 22 * rt_base_t rt_hw_interrupt_disable(); 23 */ 24.globl rt_hw_interrupt_disable 25rt_hw_interrupt_disable: 26 mrs r0, cpsr 27 cpsid i 28 bx lr 29 30/* 31 * void rt_hw_interrupt_enable(rt_base_t level); 32 */ 33.globl rt_hw_interrupt_enable 34rt_hw_interrupt_enable: 35 msr cpsr, r0 36 bx lr 37 38/* 39 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to) 40 * r0 --> from 41 * r1 --> to 42 */ 43.globl rt_hw_context_switch 44rt_hw_context_switch: 45 clrex 46 stmfd sp!, {lr} @ push pc (lr should be pushed in place of PC) 47 stmfd sp!, {r0-r12, lr} @ push lr & register file 48 49 mrs r4, cpsr 50 tst lr, #0x01 51 orrne r4, r4, #0x20 @ it's thumb code 52 53 stmfd sp!, {r4} @ push cpsr 54 55#ifdef RT_USING_FPU 56 /* fpu context */ 57 vmrs r6, fpexc 58 tst r6, #(1<<30) 59 beq __no_vfp_frame1 60 vstmdb sp!, {d0-d15} 61 vstmdb sp!, {d16-d31} 62 vmrs r5, fpscr 63 stmfd sp!, {r5} 64__no_vfp_frame1: 65 stmfd sp!, {r6} 66#endif 67 str sp, [r0] @ store sp in preempted tasks TCB 68 ldr sp, [r1] @ get new task stack pointer 69 70#ifdef RT_USING_FPU 71 /* fpu context */ 72 ldmfd sp!, {r6} 73 vmsr fpexc, r6 74 tst r6, #(1<<30) 75 beq __no_vfp_frame2 76 ldmfd sp!, {r5} 77 vmsr fpscr, r5 78 vldmia sp!, {d16-d31} 79 vldmia sp!, {d0-d15} 80__no_vfp_frame2: 81#endif 82 83 ldmfd sp!, {r1} 84 msr spsr_cxsf, r1 /* original mode */ 85 86 ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */ 87 88/* 89 * void rt_hw_context_switch_to(rt_uint32 to) 90 * r0 --> to 91 */ 92.globl rt_hw_context_switch_to 93rt_hw_context_switch_to: 94 LDR sp, [r0] @ get new task stack pointer 95 96#ifdef RT_USING_FPU 97 ldmfd sp!, {r6} 98 vmsr fpexc, r6 99 tst r6, #(1<<30) 100 beq __no_vfp_frame_to 101 ldmfd sp!, {r5} 102 vmsr fpscr, r5 103 vldmia sp!, {d0-d15} 104__no_vfp_frame_to: 105#endif 106 107 LDMIA sp!, {r4} @ pop new task cpsr to spsr 108 MSR spsr_cxsf, r4 109 110 ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */ 111 112/* 113 * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to)@ 114 */ 115 116.globl rt_hw_context_switch_interrupt 117rt_hw_context_switch_interrupt: 118 LDR r2, =rt_thread_switch_interrupt_flag 119 LDR r3, [r2] 120 CMP r3, #1 121 BEQ _reswitch 122 MOV r3, #1 @ set rt_thread_switch_interrupt_flag to 1 123 STR r3, [r2] 124 LDR r2, =rt_interrupt_from_thread @ set rt_interrupt_from_thread 125 126 STR r0, [r2] 127_reswitch: 128 LDR r2, =rt_interrupt_to_thread @ set rt_interrupt_to_thread 129 STR r1, [r2] 130 BX lr 131 132.globl IRQ_Handler 133IRQ_Handler: 134 STMDB sp!, {r0-r12,lr} 135 136#ifdef RT_USING_FPU 137 VMRS r0, fpexc 138 TST r0, #0x40000000 139 BEQ __no_vfp_frame_str_irq 140 VSTMDB sp!, {d0-d15} 141 VMRS r1, fpscr 142 @ TODO: add support for Common VFPv3. 143 @ Save registers like FPINST, FPINST2 144 STMDB sp!, {r1} 145__no_vfp_frame_str_irq: 146 STMDB sp!, {r0} 147#endif 148 149 BL rt_interrupt_enter 150 BL rt_hw_trap_irq 151 BL rt_interrupt_leave 152 153 @ if rt_thread_switch_interrupt_flag set, jump to 154 @ rt_hw_context_switch_interrupt_do and don't return 155 LDR r0, =rt_thread_switch_interrupt_flag 156 LDR r1, [r0] 157 CMP r1, #1 158 BEQ rt_hw_context_switch_interrupt_do 159 160#ifdef RT_USING_FPU 161 LDMIA sp!, {r0} @ get fpexc 162 VMSR fpexc, r0 163 TST r0, #0x40000000 164 BEQ __no_vfp_frame_ldr_irq 165 LDMIA sp!, {r1} @ get fpscr 166 VMSR fpscr, r1 167 VLDMIA sp!, {d0-d15} 168__no_vfp_frame_ldr_irq: 169#endif 170 171 LDMIA sp!, {r0-r12,lr} 172 SUBS pc, lr, #4 173 174/* 175 * void rt_hw_context_switch_interrupt_do(rt_base_t flag) 176 */ 177.globl rt_hw_context_switch_interrupt_do 178rt_hw_context_switch_interrupt_do: 179 MOV r1, #0 @ clear flag 180 STR r1, [r0] 181 182#ifdef RT_USING_FPU 183 LDMIA sp!, {r0} @ get fpexc 184 VMSR fpexc, r0 185 TST r0, #0x40000000 186 BEQ __no_vfp_frame_do1 187 LDMIA sp!, {r1} @ get fpscr 188 VMSR fpscr, r1 189 VLDMIA sp!, {d0-d15} 190__no_vfp_frame_do1: 191#endif 192 193 LDMIA sp!, {r0-r12,lr} @ reload saved registers 194 STMDB sp, {r0-r3} @ save r0-r3. We will restore r0-r3 in the SVC 195 @ mode so there is no need to update SP. 196 SUB r1, sp, #16 @ save the right SP value in r1, so we could restore r0-r3. 197 SUB r2, lr, #4 @ save old task's pc to r2 198 199 MRS r3, spsr @ get cpsr of interrupt thread 200 201 @ switch to SVC mode and no interrupt 202 CPSID IF, #0x13 203 204 STMDB sp!, {r2} @ push old task's pc 205 STMDB sp!, {r4-r12,lr} @ push old task's lr,r12-r4 206 LDMIA r1!, {r4-r7} @ restore r0-r3 of the interrupted thread 207 STMDB sp!, {r4-r7} @ push old task's r3-r0. We don't need to push/pop them to 208 @ r0-r3 because we just want to transfer the data and don't 209 @ use them here. 210 STMDB sp!, {r3} @ push old task's cpsr 211 212#ifdef RT_USING_FPU 213 VMRS r0, fpexc 214 TST r0, #0x40000000 215 BEQ __no_vfp_frame_do2 216 VSTMDB sp!, {d0-d15} 217 VMRS r1, fpscr 218 @ TODO: add support for Common VFPv3. 219 @ Save registers like FPINST, FPINST2 220 STMDB sp!, {r1} 221__no_vfp_frame_do2: 222 STMDB sp!, {r0} 223#endif 224 225 LDR r4, =rt_interrupt_from_thread 226 LDR r5, [r4] 227 STR sp, [r5] @ store sp in preempted tasks's TCB 228 229 LDR r6, =rt_interrupt_to_thread 230 LDR r6, [r6] 231 LDR sp, [r6] @ get new task's stack pointer 232 233#ifdef RT_USING_FPU 234 ldmfd sp!, {r6} 235 vmsr fpexc, r6 236 tst r6, #(1<<30) 237 beq __no_vfp_frame_do3 238 ldmfd sp!, {r5} 239 vmsr fpscr, r5 240 vldmia sp!, {d0-d15} 241 242__no_vfp_frame_do3: 243#endif 244 245 LDMIA sp!, {r4} @ pop new task's cpsr to spsr 246 MSR spsr_cxsf, r4 247 248 ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */ 249 250