This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM263P4: FIQ interrupt has been interrupting code in main loop and not saving the context of the s0-s31 registers

Part Number: AM263P4

Team,

Our customer has an inquiry for our AM263P4 MCU as follows:

"In short, we've found that our FIQ interrupt has been interrupting code in our main loop and not saving the context of the s0-s31 registers. We've set EN_SAVE_RESTORE_FPU_CONTEXT to be defined in kernel/dpl/HwiP.h which enables the following function calls in the FIQ handler:

/* Save FPU context, used in FIQ Handler */

static inline void Hwip_save_fpu_context(void)

{

__asm__ __volatile__ ( "FMRX R0, FPSCR" "\n\t": : : "memory");

__asm__ __volatile__ ( "VPUSH {D0-D15}" "\n\t": : : "memory");

__asm__ __volatile__ ( "PUSH {R0}" "\n\t": : : "memory");

}

/* Restore FPU context, used in FIQ Handler */

static inline void Hwip_restore_fpu_context(void)

{

__asm__ __volatile__ ( "POP {R0}" "\n\t": : : "memory");

__asm__ __volatile__ ( "VPOP {D0-D15}" "\n\t": : : "memory");

__asm__ __volatile__ ( "VMSR FPSCR, R0" "\n\t": : : "memory");

}

However, we've noticed that there's a point in our main loop where we have multiple FPU instructions in a row (some vldr and a vcmp.f32) and the FIQ interrupts the code between some of those instructions. The FIQ then does some float operations and returns without restoring the s0-s31 registers. This causes unexpected behavior back in the main loop. 

We have looked at the VFP registers before and after the interrupt and the d0-d15 registers get restored correctly, but the s0-s31 registers do not. We assumed the s0-s31 registers mapped directly to the high/low 32 bits of d0-d15, so they would get restored as well, but they didn't. Is there a step we are missing to make this work? Do we need to "refresh" the s0-s31 registers after a context restore?

TY,

CY

  • For more context, this is our main loop function:

    static inline bool isHvVoltageSafeForFaultClearing(void)
    {
    volatile float hvPositive = 1.0f;
    volatile float hvNegative = -3.0f;
    volatile float hvOutputVoltage = hvPositive - hvNegative;
    const bool isHvVoltageAboveMinSafe = (hvOutputVoltage >= HV_VOLTAGE_MIN_SAFE_FOR_FAULT_CLEAR);

    FPSCR_MAIN_LOOP = read_fpscr();
    S0_MAIN_LOOP = read_s0();
    S2_MAIN_LOOP = read_s2();
    if (isHvVoltageAboveMinSafe == true) {
    return true;
    }

    const bool isHvVoltageBelowMaxSafe = (hvOutputVoltage <= HV_VOLTAGE_MAX_SAFE_FOR_FAULT_CLEAR);
    const bool isHvVoltageSafe = isHvVoltageAboveMinSafe && isHvVoltageBelowMaxSafe;

    return isHvVoltageSafe;
    }

    I'm putting a breakpoint on the first "return true" (line 12). This passes most of the time (because 4.0f is not greater than 145.0f) but fails when the FIQ hits. If we disable FIQ, it runs fine. 

    Here's the FIQ ISR (with added register saves for debugging):

    static __attribute__((section(".text.tcm"), noinline, flatten)) void pwmIntNoneMode(void *args)
    {
    (void)args;

    FPSCR_FIQ_START = read_fpscr();
    LR_FIQ_START = read_lr();
    S0_FIQ_START = read_s0();
    S2_FIQ_START = read_s2();
    runCommonFunctions();
    FPSCR_FIQ_END = read_fpscr();
    S0_FIQ_END = read_s0();
    S2_FIQ_END = read_s2();
    PwmControl_ackInterrupt(PwmIntPwm.baseAddr);
    }

    When it breaks on "return true" I get the following out of my debugger:

    When I break on the "if" statement when it doesn't get interrupted, I get:

  • Hi, please note that the expert is currently out of office due to holiday. They will get back to you as soon as possible. Thank you.

  • Hi Austin,

    Im looking into this, i'll get back with some feedback after some testing.

    Regards,
    Shaunak

  • We thank you very much for that Shaunak!  

    -Chris 

  • Hi Austin/Chris,

    Based on your description and the SDK code review, we suspect that the compiler-generated prologue and epilogue code for the FIQ handler may be using FPU registers before and after your manual FPU context save/restore functions execute. This would explain why the d0-d15 registers appear to save/restore correctly in isolation, but the s0-s31 registers (which are just different views of the same d0-d15 registers) appear corrupted.

    Here's the suspected sequence of events:
    1. FIQ fires → hardware saves PC/CPSR
    2. Compiler-generated prologue code executes → may use FPU registers as scratch space
    3. Your Hwip_save_fpu_context() saves the already-corrupted FPU state
    4. ISR body executes with float operations
    5. Your Hwip_restore_fpu_context() restores the corrupted state
    6. Compiler-generated epilogue code executes → may further corrupt FPU registers
    7. Return to main loop → sees incorrect FPU register values

    This differs from the IRQ handler, which is implemented entirely in assembly (HwiP_armv7r_handlers_nortos_asm.S:49) and manually saves FPU context before any C code or compiler-generated code can execute.

    But im not sure and would further need some information to verify if the above is true:

    1. Disassembly of the FIQ Handler: Please generate a disassembly listing of the HwiP_fiq_handler function to see the actual compiler-generated code

    2. Compiler Version and Flags: What compiler and version are you using (TI ARM Clang, GCC, etc.)? And are you using any special compiler flags beyond the SDK defaults?

    3. Before modifying the FIQ handler implementation, could you try adding the __attribute__((target("general-regs-only"))) attribute to the FIQ handler function? This tells the compiler to avoid using FPU registers in the generated code.

    Regards,
    Shaunak