Skip to content

Commit 88ba97f

Browse files
Bradley Bolenstephanosio
authored andcommitted
arch: arm: aarch32: cortex_a_r: Add shared FPU support
This adds lazy floating point context switching. On svc/irq entrance, the VFP is disabled and a pointer to the exception stack frame is saved away. If the esf pointer is still valid on exception exit, then no other context used the VFP so the context is still valid and nothing needs to be restored. If the esf pointer is NULL on exception exit, then some other context used the VFP and the floating point context is restored from the esf. The undefined instruction handler is responsible for saving away the floating point context if needed. If the handler is in the first irq/svc context and the current thread uses the VFP, then the float context needs to be saved. Also, if the handler is in a nested context and the previous context was using the FVP, save the float context. Signed-off-by: Bradley Bolen <[email protected]>
1 parent 80bd814 commit 88ba97f

File tree

9 files changed

+363
-10
lines changed

9 files changed

+363
-10
lines changed

arch/arm/core/aarch32/cortex_a_r/exc.S

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030

3131
_ASM_FILE_PROLOGUE
3232

33+
#if defined(CONFIG_FPU_SHARING)
34+
GTEXT(z_arm_fault_undef_instruction_fp)
35+
#endif
3336
GTEXT(z_arm_fault_undef_instruction)
3437
GTEXT(z_arm_fault_prefetch)
3538
GTEXT(z_arm_fault_data)
@@ -47,6 +50,19 @@ GTEXT(z_arm_data_abort)
4750
stmfd sp, {r0-r3, r12, lr}^
4851
sub sp, #24
4952

53+
#if defined(CONFIG_FPU_SHARING)
54+
sub sp, #___fpu_t_SIZEOF
55+
56+
vmrs r1, fpexc
57+
mov r0, #FPEXC_EN
58+
vmsr fpexc, r0
59+
vmrs r0, fpscr
60+
61+
mov r2, sp
62+
vstmia r2!, {s0-s15}
63+
stm r2, {r0, r1}
64+
#endif
65+
5066
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
5167
/* Pointer to extra esf info */
5268
sub sp, #___extra_esf_info_t_SIZEOF
@@ -100,7 +116,56 @@ SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_undef_instruction)
100116
subne lr, #2 /* Thumb (T_BIT) */
101117
pop {r0}
102118

103-
exception_entry MODE_UND
119+
/*
120+
* Store r0-r3, r12, lr, lr_und and spsr_und into the stack to
121+
* construct an exception stack frame.
122+
*/
123+
srsdb sp!, #MODE_UND
124+
stmfd sp, {r0-r3, r12, lr}^
125+
sub sp, #24
126+
127+
/* Increment exception nesting count */
128+
ldr r2, =_kernel
129+
ldr r1, [r2, #_kernel_offset_to_nested]
130+
add r1, r1, #1
131+
str r1, [r2, #_kernel_offset_to_nested]
132+
133+
#if defined(CONFIG_FPU_SHARING)
134+
sub sp, #___fpu_t_SIZEOF
135+
136+
bl z_arm_fault_undef_instruction_fp
137+
cmp r0, #0
138+
beq z_arm_exc_exit
139+
140+
vmrs r1, fpexc
141+
mov r0, #FPEXC_EN
142+
vmsr fpexc, r0
143+
vmrs r0, fpscr
144+
145+
mov r2, sp
146+
vstmia r2!, {s0-s15}
147+
stm r2, {r0, r1}
148+
#endif
149+
150+
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
151+
/* Pointer to extra esf info */
152+
sub sp, #___extra_esf_info_t_SIZEOF
153+
mov r0, #0
154+
str r0, [sp, #4]
155+
str r0, [sp, #8]
156+
157+
sub r1, sp, #___callee_saved_t_SIZEOF
158+
str r1, [sp]
159+
cps #MODE_SYS
160+
stm r1, {r4-r11, sp}
161+
cps #MODE_UND
162+
163+
mov r0, sp
164+
mov sp, r1
165+
#else
166+
mov r0, sp
167+
#endif
168+
104169
bl z_arm_fault_undef_instruction
105170
exception_exit
106171

@@ -125,6 +190,12 @@ SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_prefetch_abort)
125190

126191
b z_arm_exc_exit
127192

193+
#if defined(CONFIG_FPU_SHARING)
194+
#define FPU_SF_SIZE ___fpu_t_SIZEOF
195+
#else
196+
#define FPU_SF_SIZE 0
197+
#endif
198+
128199
/**
129200
* @brief Data abort exception handler
130201
*
@@ -148,10 +219,10 @@ SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_data_abort)
148219
* the true esf from the one passed to z_arm_fault_data.
149220
*/
150221
cmp r0, #0
151-
ldreq r1, [sp, #24]
222+
ldreq r1, [sp, #24 + FPU_SF_SIZE]
152223

153224
exception_exit
154225

155-
streq r1, [sp, #24]
226+
streq r1, [sp, #24 + FPU_SF_SIZE]
156227

157228
b z_arm_exc_exit

arch/arm/core/aarch32/cortex_a_r/exc_exit.S

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,45 @@ system_thread_exit\@:
6262
#endif
6363
.endm
6464

65+
.macro fpu_exc_exit
66+
#if defined(CONFIG_FPU_SHARING)
67+
/*
68+
* If the floating point context pointer is null, then a context was
69+
* saved so restore the float context from the exception stack frame.
70+
*/
71+
ldr r2, =_kernel
72+
ldr r1, [r2, #_kernel_offset_to_fp_ctx]
73+
cmp r1, #0
74+
beq vfp_restore\@
75+
76+
/*
77+
* If leaving the last interrupt context, remove the floating point
78+
* context pointer.
79+
*/
80+
cmp r0, #0
81+
moveq r1, #0
82+
streq r1, [r2, #_kernel_offset_to_fp_ctx]
83+
b vfp_exit\@
84+
85+
vfp_restore\@:
86+
add r3, sp, #___fpu_sf_t_fpscr_OFFSET
87+
ldm r3, {r1, r2}
88+
tst r2, #FPEXC_EN
89+
beq vfp_exit\@
90+
91+
vmsr fpexc, r2
92+
vmsr fpscr, r1
93+
vldmia sp, {s0-s15}
94+
95+
vfp_exit\@:
96+
/* Leave the VFP disabled when leaving */
97+
mov r1, #0
98+
vmsr fpexc, r1
99+
100+
add sp, sp, #___fpu_t_SIZEOF
101+
#endif
102+
.endm
103+
65104
/**
66105
* @brief Kernel housekeeping when exiting interrupt handler installed directly
67106
* in the vector table
@@ -133,6 +172,11 @@ __EXIT_INT:
133172
* out or they are the args to _new_thread for a new thread.
134173
*/
135174
cps #MODE_SYS
175+
176+
#if defined(CONFIG_FPU_SHARING)
177+
fpu_exc_exit
178+
#endif
179+
136180
pop {r0-r3, r12, lr}
137181
userspace_exc_exit
138182
rfeia sp!
@@ -173,6 +217,9 @@ SECTION_SUBSEC_FUNC(TEXT, _HandlerModeExit, z_arm_exc_exit)
173217
*/
174218

175219
/* Clean up exception stack frame */
220+
#if defined(CONFIG_FPU_SHARING)
221+
add sp, sp, #___fpu_t_SIZEOF
222+
#endif
176223
add sp, #32
177224

178225
/*
@@ -193,6 +240,9 @@ SECTION_SUBSEC_FUNC(TEXT, _HandlerModeExit, z_arm_exc_exit)
193240

194241
/* Return to the switched thread */
195242
cps #MODE_SYS
243+
#if defined(CONFIG_FPU_SHARING)
244+
fpu_exc_exit
245+
#endif
196246
pop {r0-r3, r12, lr}
197247
userspace_exc_exit
198248
rfeia sp!
@@ -203,6 +253,9 @@ __EXIT_EXC:
203253
sub r0, r0, #1
204254
str r0, [r3, #_kernel_offset_to_nested]
205255

256+
#if defined(CONFIG_FPU_SHARING)
257+
add sp, sp, #___fpu_t_SIZEOF
258+
#endif
206259
/*
207260
* Restore r0-r3, r12, lr, lr_und and spsr_und from the exception stack
208261
* and return to the current thread.

arch/arm/core/aarch32/cortex_a_r/fault.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,101 @@ static void dump_fault(uint32_t status, uint32_t addr)
8686
}
8787
#endif
8888

89+
#if defined(CONFIG_FPU_SHARING)
90+
/**
91+
* @brief FPU undefined instruction fault handler
92+
*
93+
* @return Returns true if the FPU is already enabled
94+
* implying a true undefined instruction
95+
* Returns false if the FPU was disabled
96+
*/
97+
bool z_arm_fault_undef_instruction_fp(void)
98+
{
99+
/*
100+
* Assume this is a floating point instruction that faulted because
101+
* the FP unit was disabled. Enable the FP unit and try again. If
102+
* the FP was already enabled then this was an actual undefined
103+
* instruction.
104+
*/
105+
if (__get_FPEXC() & FPEXC_EN)
106+
return true;
107+
108+
__set_FPEXC(FPEXC_EN);
109+
110+
if (_kernel.cpus[0].nested > 1) {
111+
/*
112+
* If the nested count is greater than 1, the undefined
113+
* instruction exception came from an irq/svc context. (The
114+
* irq/svc handler would have the nested count at 1 and then
115+
* the undef exception would increment it to 2).
116+
*/
117+
struct __fpu_sf *spill_esf =
118+
(struct __fpu_sf *)_kernel.cpus[0].fp_ctx;
119+
120+
if (spill_esf == NULL)
121+
return false;
122+
123+
_kernel.cpus[0].fp_ctx = NULL;
124+
125+
/*
126+
* If the nested count is 2 and the current thread has used the
127+
* VFP (whether or not it was actually using the VFP before the
128+
* current exception) OR if the nested count is greater than 2
129+
* and the VFP was enabled on the irq/svc entrance for the
130+
* saved exception stack frame, then save the floating point
131+
* context because it is about to be overwritten.
132+
*/
133+
if (((_kernel.cpus[0].nested == 2)
134+
&& (_current->base.user_options & K_FP_REGS))
135+
|| ((_kernel.cpus[0].nested > 2)
136+
&& (spill_esf->undefined & FPEXC_EN))) {
137+
/*
138+
* Spill VFP registers to specified exception stack
139+
* frame
140+
*/
141+
spill_esf->undefined |= FPEXC_EN;
142+
spill_esf->fpscr = __get_FPSCR();
143+
__asm__ volatile (
144+
"vstmia %0, {s0-s15};\n"
145+
: : "r" (&spill_esf->s[0])
146+
: "memory"
147+
);
148+
}
149+
} else {
150+
/*
151+
* If the nested count is one, a thread was the faulting
152+
* context. Just flag that this thread uses the VFP. This
153+
* means that a thread that uses the VFP does not have to,
154+
* but should, set K_FP_REGS on thread creation.
155+
*/
156+
_current->base.user_options |= K_FP_REGS;
157+
}
158+
159+
return false;
160+
}
161+
#endif
162+
89163
/**
90164
* @brief Undefined instruction fault handler
91165
*
92166
* @return Returns true if the fault is fatal
93167
*/
94168
bool z_arm_fault_undef_instruction(z_arch_esf_t *esf)
95169
{
170+
#if defined(CONFIG_FPU_SHARING)
171+
/*
172+
* This is a true undefined instruction and we will be crashing
173+
* so save away the VFP registers.
174+
*/
175+
esf->fpu.undefined = __get_FPEXC();
176+
esf->fpu.fpscr = __get_FPSCR();
177+
__asm__ volatile (
178+
"vstmia %0, {s0-s15};\n"
179+
: : "r" (&esf->fpu.s[0])
180+
: "memory"
181+
);
182+
#endif
183+
96184
/* Print fault information */
97185
LOG_ERR("***** UNDEFINED INSTRUCTION ABORT *****");
98186

arch/arm/core/aarch32/isr_wrapper.S

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,31 @@ isr_system_thread:
8888
cps #MODE_SYS
8989
push {r0-r3, r12, lr}
9090

91+
#if defined(CONFIG_FPU_SHARING)
92+
sub sp, sp, #___fpu_t_SIZEOF
93+
94+
/*
95+
* Note that this handler was entered with the VFP unit enabled.
96+
* The undefined instruction handler uses this to know that it
97+
* needs to save the current floating context.
98+
*/
99+
vmrs r0, fpexc
100+
str r0, [sp, #___fpu_t_SIZEOF - 4]
101+
102+
/* Disable VFP */
103+
mov r0, #0
104+
vmsr fpexc, r0
105+
106+
/*
107+
* Mark where to store the floating context for the undefined
108+
* instruction handler
109+
*/
110+
ldr r2, =_kernel
111+
ldr r0, [r2, #_kernel_offset_to_fp_ctx]
112+
cmp r0, #0
113+
streq sp, [r2, #_kernel_offset_to_fp_ctx]
114+
#endif /* CONFIG_FPU_SHARING */
115+
91116
/*
92117
* Use SVC mode stack for predictable interrupt behaviour; running ISRs
93118
* in the SYS/USR mode stack (i.e. interrupted thread stack) leaves the

0 commit comments

Comments
 (0)