This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NVIC does not tail-chain to PendSV ISR

I'm trying to implement context switching from handler mode to thread mode using PendSV. I set the PendSV pending bit inside an ISR with SCB->ICSR = SCB->ICSR | SCB_ICSR_PENDSVSET_Msk. After setting the PendSV pending bit, I can see that the PendSV exception is pending: SCB->ICSR = 0x10000813 (I think the ISRPREEMPT bit should be set as well but it's not).

NVIC should tail-chain to my PendSV handler before unstacking. Instead ISR unstacking occurs, a return to exception is executed, and a few thread-level instructions are executed, before the PendSV handler is called.

The SoftDevice is present but has not been enabled.

Any ideas on what might be causing this? Thanks.

  • Thanks again, Aryan! I'm still having problems completing my context switch without hard-faulting, but it does look like tail-chaining is fine and my problems lie somewhere else.

    Still interested in hearing about how you were able to confirm tail-chaining on your end.

  • yes, one more time , mislead by the debugger's inability to show us exactly what is happening under the hood.

  • This is how i verified it.

       void PendSV_Handler(void)
        {
              volatile register uint32_t r0 __asm("r0");
              volatile register uint32_t r1 __asm("r1");
              printf("PendSV %x\n\r",r0);
              printf("PendSV %x\n\r",r1);
        }
    
    void TIMER1_IRQHandler(void)
    {
         volatile register uint32_t r0 __asm("r0");
         volatile register uint32_t r1 __asm("r1");
        
        printf("Timer1 %x\n\r",r0);
        printf("Timer1 %x\n\r",r1);
        
        
        NRF_TIMER1->INTENSET = 0;
        NVIC_DisableIRQ(TIMER1_IRQn);
        NVIC_ClearPendingIRQ(TIMER1_IRQn);
    
        SCB->ICSR = SCB->ICSR | SCB_ICSR_PENDSVSET_Msk;
    
        int i = 0xFFFF;
        while(i--);  
    
        __asm("mov r0, #0xbeef");
        __asm("mov r1, #0xdead");
    }
    
    
    
    /**
     * @brief Function for main application entry.
     */
    int main(void)
    {
        uint32_t err_code;
    
        simple_uart_config(0, TX_PIN_NUMBER, 0, RTS_PIN_NUMBER, false); // Hardware flow control not used in this example.
          
        printf("\n\rStart: \n\r");
    
        NVIC_SetPriority(TIMER1_IRQn, 2);
        NVIC_EnableIRQ(TIMER1_IRQn);
        NVIC_SetPriority(PendSV_IRQn, 0xFF);
        NVIC_EnableIRQ(PendSV_IRQn);
        NVIC_SetPendingIRQ(TIMER1_IRQn);
          
        while(1);
    }
    

    I set 0xdeadbeef in R0 and R1 in the end of Timer1 ISR and i see that they retain on entry to PendSV, this can only happen with tail chaining

    OUTPUT

    Start:
    Timer1 0
    Timer1 e000e200
    PendSV beef
    PendSV dead
    
Related