This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Changing interrupt priority to prevent nesting

Hi, I'm working on M0+, but the question applies to M3 or M4 too.

I have the following situation: 2 interrupt sources inject events on a state machine that must handle each event separately (not nested execution)

The state machine is implementing a protocol and is using the UART interrupt and the SysTick for timeouts.

A UART interrupt causes the state machine execution to evolve based on related UART event.

The same routine, executed inside the SysTick interrupt service routine, handles timeout event.

It happens the 2 interrupts, SysTick and UART have different priorities.

I want to be sure that when the state machine is processing one event (pretend it is UART), the timeout event is postponed (i.e. the interrupt is served when the previous is completed)

I wrote the following code to protect state machine execution from self-nesting

extern void commStack_uart_v0_rx( void )

{

  uInt8 timer_priority;

  uInt8 my_priority;

  my_priority = NVIC_GetPriority( UART0_IRQn );

  timer_priority = NVIC_GetPriority( SysTick_IRQn );

  if (timer_priority < my_priority) NVIC_SetPriority( SysTick_IRQn, my_priority );

  state_machine(); // Execute the state machine to process UART event

  NVIC_SetPriority( SysTick_IRQn, timer_priority );

}

Before executing state_machine, I change the priority of SysTick to match the UART priority if lower number. In other words I change the SysTick priority so that it cannot enter over the UART.

Before leaving I restore the priority.

Is it the right way to proceed? Since it is not possible to postpone the service of a SysTick interrupt with a trick like disable/enable.

The SysTick can't be temporary stopped.

I'm looking for a solution that can be used to solve problems of this category.

Thanks.

Parents
  • In ARMv6-M architecture (Cortex-M0, M0+, M1), dynamic changing of priority level in an enabled interrupt is not supported.

    You can change priority level dynamically in ARMv7-M (Cortex-M3, M4), and there is an additional special register called BASEPRI which you can use to mask interrupts/exception for certain priority or below.

    In Cortex-M0/M0+, since dynamic priority level change for enabled interupt is not supported (typo correction 9-Dec-13),

    you should disable it first, then change the priority level, and then reenable it.

    However, for SysTick timer interrupt, it you "disable" the interrupt generation, you could lost a timer event.

    Ideally you can set the two interrupts at same priority level, but assume that this is not possible, you could use the following method, by change the way your SysTick ISR work:

    // Declare a global volatile variable

    volatile int running_uart_task = 0;

    // Set the variable to 1 when the UART task is running.

    extern void commStack_uart_v0_rx( void )

    { ...

    running_uart_task = 1;

      state_machine(); // Execute the state machine to process UART event

    running_uart_task = 0;

    ...

    }

    In your SysTick handle:

    void SysTick_Handler(void)

    {

      if (running_uart_task == 0) {

        // Do work

       } else {

      // Set PendSV pending status

      SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;

      }

      return;

    }

    Create a PendSV handler, and program it to be same level as UART or lowest priority.

    void PendSV_Handler(void)

    {

      SCB->ICSR |=SCB_ICSR_PENDSTSET_Msk; // Set SysTick pending status

      return;

    }

    So when the SysTick get triggered during your UART task,

    1) the SysTIck ISR will execute, finding out the running_uart_task is set, so it set PendSV pending status

    2) Return to UART ISR, finishing the UART task

    3) PendSV ISR execute after UART task, this set the SysTick pending status

    4) SysTick handler executed.

    Hope this helps.

Reply
  • In ARMv6-M architecture (Cortex-M0, M0+, M1), dynamic changing of priority level in an enabled interrupt is not supported.

    You can change priority level dynamically in ARMv7-M (Cortex-M3, M4), and there is an additional special register called BASEPRI which you can use to mask interrupts/exception for certain priority or below.

    In Cortex-M0/M0+, since dynamic priority level change for enabled interupt is not supported (typo correction 9-Dec-13),

    you should disable it first, then change the priority level, and then reenable it.

    However, for SysTick timer interrupt, it you "disable" the interrupt generation, you could lost a timer event.

    Ideally you can set the two interrupts at same priority level, but assume that this is not possible, you could use the following method, by change the way your SysTick ISR work:

    // Declare a global volatile variable

    volatile int running_uart_task = 0;

    // Set the variable to 1 when the UART task is running.

    extern void commStack_uart_v0_rx( void )

    { ...

    running_uart_task = 1;

      state_machine(); // Execute the state machine to process UART event

    running_uart_task = 0;

    ...

    }

    In your SysTick handle:

    void SysTick_Handler(void)

    {

      if (running_uart_task == 0) {

        // Do work

       } else {

      // Set PendSV pending status

      SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;

      }

      return;

    }

    Create a PendSV handler, and program it to be same level as UART or lowest priority.

    void PendSV_Handler(void)

    {

      SCB->ICSR |=SCB_ICSR_PENDSTSET_Msk; // Set SysTick pending status

      return;

    }

    So when the SysTick get triggered during your UART task,

    1) the SysTIck ISR will execute, finding out the running_uart_task is set, so it set PendSV pending status

    2) Return to UART ISR, finishing the UART task

    3) PendSV ISR execute after UART task, this set the SysTick pending status

    4) SysTick handler executed.

    Hope this helps.

Children