I’m trying to adapt FreeRTOS-Kernel/portable/GCC/ARM_CM0/port.c
for Raspberry Pi Pico. The Pico has a hardware divider whose state must be saved for context switches. The state consists of 4 32-bit words. (See hw_divider_save_state
and hw_divider_restore_state
in pico-sdk/divider.S at master · raspberrypi/pico-sdk · GitHub.
Here is my attempt:
void xPortPendSVHandler( void )
{
/* This is a naked function. */
// Save area:
//psp-> | 0
// | -4 r11
// | -8 r10
// | -12 r9
//pxTopOfStack + 32 | -16 r8
// | -20 r7
// | -24 r6
// | -28 r5
//psp - 32 | -32 r4
// | -36 SIO_DIV_QUOTIENT
// | -40 SIO_DIV_REMAINDER
// | -44 SIO_DIV_UDIVISOR
//pxTopOfStack-> | -48 SIO_DIV_UDIVIDEND
__asm volatile
(
" .syntax unified \n"
" mrs r0, psp \n"
" \n"
" ldr r3, pxCurrentTCBConst \n"/* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" subs r0, r0, #32 \n"/* Make space for the remaining low registers. */
" stm r0!, {r4-r7} \n"/* Store the low registers that are not saved automatically. */
" mov r4, r8 \n"/* Store the high registers. */
" mov r5, r9 \n"
" mov r6, r10 \n"
" mov r7, r11 \n"
" stm r0!, {r4-r7} \n"
" \n"
" subs r0, r0, #48 \n"/* Make space for divider state. */
" str r0, [r2] \n"/* Save the new top of stack. */
" \n"
" ldr r2, =SIO_BASE \n"/* hw_divider_save_state */
" ldr r1, [r2, #0x00000078] \n"/* SIO_DIV_CSR_OFFSET (sio.h) */
/* wait for results as we can't save signed-ness of operation */
"MY1: \n"
" lsrs r1, 1 \n"/* #SIO_DIV_CSR_READY_SHIFT_FOR_CARRY */
" bcc MY1 \n"
" ldr r4, [r2, #0x00000060] \n"/* SIO_DIV_UDIVIDEND_OFFSET */
" ldr r5, [r2, #0x00000064] \n"/* SIO_DIV_UDIVISOR_OFFSET */
" ldr r6, [r2, #0x00000074] \n"/* SIO_DIV_REMAINDER_OFFSET */
" ldr r7, [r2, #0x00000070] \n"/* SIO_DIV_QUOTIENT_OFFSET */
" stm r0!, {r4-r7} \n"/* Save HW divider state */
" \n"
" push {r3, r14} \n"
" cpsid i \n"
" bl vTaskSwitchContext \n"
" cpsie i \n"
" pop {r2, r3} \n"/* lr goes in r3. r2 now holds tcb pointer. */
" \n"
" ldr r1, [r2] \n"
" ldr r0, [r1] \n"/* The first item in pxCurrentTCB is the task top of stack. */
" \n"
" ldr r2, =SIO_BASE \n"/* hw_divider_restore_state */
" ldm r0!, {r4-r7} \n"
" str r4, [r2, #0x00000060] \n"/* SIO_DIV_UDIVIDEND_OFFSET */
" str r5, [r2, #0x00000064] \n"/* SIO_DIV_UDIVISOR_OFFSET */
" str r6, [r2, #0x00000074] \n"/* SIO_DIV_REMAINDER_OFFSET */
" str r7, [r2, #0x00000070] \n"/* SIO_DIV_QUOTIENT_OFFSET */
" \n"
" adds r0, r0, #16 \n"/* Move to the high registers. */
" ldm r0!, {r4-r7} \n"/* Pop the high registers. */
" mov r8, r4 \n"
" mov r9, r5 \n"
" mov r10, r6 \n"
" mov r11, r7 \n"
" \n"
" msr psp, r0 \n"/* Remember the new top of stack for the task. */
" \n"
" subs r0, r0, #32 \n"/* Go back for the low registers that are not automatically restored. */
" ldm r0!, {r4-r7} \n"/* Pop low registers. */
" \n"
" bx r3 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
"SIO_BASE: .word SIO_BASE"
);
}
I also added
pxTopOfStack -= 4; /* Divider state */
to pxPortInitialiseStack
. Alas, it doesn’t work. Gets stuck in a loop, I think (but past the bcc MY1 loop). Is there anything else I need to change? Have I made a dumb mistake in the assembly code? (I am a beginner at ARM assembly.)
My starting point was FreeRTOS-Kernel/port.c at main · FreeRTOS/FreeRTOS-Kernel · GitHub. I’ve attached my modified version.
port.c (24.9 KB)