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

Enable Reentrant interrupt handlers in kinetis K70

I have a TWR-K70F120M: kinetis k70 120MHz Tower System Module.

I have connected a elettric signal to PORTB and i have connected e isr handler to signal transition.

Electric signal

     _____          ______          ______

    |         |         |          |         |          |

__|          |_____|          |_____|          |_____  ------> PORTB  

  ( 1 )             ( 2 )                ( 3 )

     

Interrupt handler is called every transition of the elettric signal ( in the figure is 1,2,3 ).

The interrupt handler is the follow code:

#define EN_ERR_SCHEDULER_INT_TOO_LONG (10)

static bool wbFlag = false; /* control the reentrant function */

__ isr void isr_handler( void )

{

  /* l'interrupt precedente è durato troppo ? */

  if ( wbFlag != true )

  {

    /* flag reentrant function  */

    wbFlag = TRUE;  

  }

  else

  {  

    vDebugPrint( EN_ERR_SCHEDULER_INT_TOO_LONG );

  }

   while(1);

   wbFlag = false;

}

at the step ( 1 ) the program jump to the handler but not in the 2nd e third impulse. Is it possible to enable a reentrand interrupt in kinetis k70?

 

the setup is follow ( setup is call one time at the startup of the system ):

sllRq = 88; /*Port control module Pin Detect (Port B) */

void vEnableIrq (SLONG slIRQ)

{

  SLONG slDiv;

 

  /* Make sure that the IRQ is an allowable number. Right now up to 91 is

  * used.

  */

  if ( slIRQ > 91 )

  {

    vDebugPrint("\nERR! Invalid IRQ value passed to enable irq function!\n");

  }

 

  /* Determine which of the NVICISERs corresponds to the irq */

  slDiv = ( slIRQ / 32 );

 

  switch ( slDiv )

  {

    case 0x0:

      NVICICPR0 = 1 << ( slIRQ % 32 );

      NVICISER0 = 1 << ( slIRQ % 32 );

    break;

    case 0x1:

      NVICICPR1 = 1 << ( slIRQ % 32 );

      NVICISER1 = 1 << ( slIRQ % 32 );

    break;

    case 0x2: /* program execute this case !!!!! */

      NVICISER2 = 1 << ( slIRQ % 32 );

      NVICICPR2 = 1 << ( slIRQ % 32 );

      PORTB_PCR18 |= PORT_PCR_ISF(0) | PORT_PCR_IRQC(9) |  PORT_PCR_MUX(1);

      NVICIP88 = 0x00;

    break;

  }             

}

I need to control the reentrant interrupt because this application must check the excessive duration of the code in isr handler.

Parents
  • Hi,

    The NVIC in Cortex-M processors are not designed for handling reentrant interrupt requests. The NVIC design automatically block all interrupts of the same or lower priority when it is taken, so in normal use cases you wont be able to have reentrant interrupt handling.

    There is a software trick that enable some interrupts handlers to support reentrant, but this trick wont work if the new interrupt request arrived very shortly after the first one (it need some execution time in the ISR to switch the processor back into thread). This software trick is documented in "the Definitive Guide to the ARM Cortex-M3 and Cortex-M4 Processors, 3rd edition" (The example codes can be downloaded from http://booksite.elsevier.com/9780124080829/, but you might need to update the example in \ch_23_eg_3_reentrant_handler , see Errors in the Definitive Guide for ARM Cortex-M book series ).

    I guess the reason why you want reentrant ISR is to make sure each pulse in port B will trigger an ISR execution.

    In general, there is no way to guarantee the three ISRs are executed if these three pulses are very close to each other. E.g. If the first pulse occurred when the processor is executing an ISR, hence delaying the execution of the port B ISRs, the pending requests could merged together and hence you might just get one ISR execution.  To improve the solution slightly better, you can reduce the length of the port B ISR to minimum length, make it the highest priority in the system and do the actual processing in another ISR like PendSV. e.g.

    volatile int req_counter = 0; // request counter (global variable)

    volatile int ack_counter = 0; // acknowledge counter (global variable)

    void Port_B_Handler(void) // High priority

    {

      req_counter++;

      SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; // Set PendSV pending status

      return;

    }

    void PendSV_Handler(void) // Low priority

    {

      while (req_counter != ack_counter) {

        // do ISR processing ...

        ack_counter ++;

        }

    }

    But even this will not guarantee you will get one ISR per pulse if the pulses get too close to each other of if another ISR caused delay to the Port B ISR processing.

    regards,

    Joseph

Reply
  • Hi,

    The NVIC in Cortex-M processors are not designed for handling reentrant interrupt requests. The NVIC design automatically block all interrupts of the same or lower priority when it is taken, so in normal use cases you wont be able to have reentrant interrupt handling.

    There is a software trick that enable some interrupts handlers to support reentrant, but this trick wont work if the new interrupt request arrived very shortly after the first one (it need some execution time in the ISR to switch the processor back into thread). This software trick is documented in "the Definitive Guide to the ARM Cortex-M3 and Cortex-M4 Processors, 3rd edition" (The example codes can be downloaded from http://booksite.elsevier.com/9780124080829/, but you might need to update the example in \ch_23_eg_3_reentrant_handler , see Errors in the Definitive Guide for ARM Cortex-M book series ).

    I guess the reason why you want reentrant ISR is to make sure each pulse in port B will trigger an ISR execution.

    In general, there is no way to guarantee the three ISRs are executed if these three pulses are very close to each other. E.g. If the first pulse occurred when the processor is executing an ISR, hence delaying the execution of the port B ISRs, the pending requests could merged together and hence you might just get one ISR execution.  To improve the solution slightly better, you can reduce the length of the port B ISR to minimum length, make it the highest priority in the system and do the actual processing in another ISR like PendSV. e.g.

    volatile int req_counter = 0; // request counter (global variable)

    volatile int ack_counter = 0; // acknowledge counter (global variable)

    void Port_B_Handler(void) // High priority

    {

      req_counter++;

      SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; // Set PendSV pending status

      return;

    }

    void PendSV_Handler(void) // Low priority

    {

      while (req_counter != ack_counter) {

        // do ISR processing ...

        ack_counter ++;

        }

    }

    But even this will not guarantee you will get one ISR per pulse if the pulses get too close to each other of if another ISR caused delay to the Port B ISR processing.

    regards,

    Joseph

Children
  • That re-entrant interrupt handler in your book works fabulously for me. Really brilliant code.

    I was trying to write similar handler (I have it documented here https://sites.google.com/site/sippeyfunlabs/how-to-run-re-entrant-task-scheduler-on-arm-cortex-m4 ). It does work for simple test case but start to fail in real program. It causes stack related hard fault occasionally. However, I can confirm my code handles stack correctly with debugging info. The rest of program can not be wrong either, they are mostly pure float point math calculation in C. I turned off the lazy stacking and it still behaves the same.

    Even if your code already solve my problem, I am still willing to figure out why. Comparing your code with mine, I found that you are using SVC to recover to the normal program. Since all my code runs in privileged mode always, is it also ok to manually recover stack and avoid using SVC? Existence of fp stack frame can be figured from LR and the existence of aligner is bit 9(reserved) of psr. Is that correct? Moreover, if psr is finally recovered from stack, why it is necessary to save it at the beginning? Is that to keep 8 bytes alignment when pushing lr?

    Sorry to bring up so many stuff that seems not really related to OP's question. I did not find PM feature in this place.

  • Hi,

    The biggest problem about returning to normal program is the need to restore the IT/ICI bits in EPSR.

    The only way to do it is to use an exception return, that's why I used an SVC to handle this.

    (You cannot write to EPSR using MSR, see

    Cortex-M4 Devices Generic User Guide: 2.1.3. Core registers)

    Otherwise, you lose the IT/ICI status if the interrupt was taking place during a LDM/STM/PUSH/POP instruction or an IT sequence,

    regards,

    Joseph

  • Thank you for your timely reply. You are exactly right. That solves my confusion about why my code works abnormally only for certain programs but not others. If at the time of interrupt, the core is executing a PUSH, POP, it will do that twice and mess up the stack? Debugging my code seems confirmed the stack has duplicated data when hard fault happens. Those may be data that got pushed twice. Since there are no way to write to EPSR as you said, SVC seems the only option.

    Thank you for the clarification and the code that save my day.

    Peng

  • Hi Peng,

    The losing of ICI bit is less likely to be a issue. The PUSH/POP will just restart from the first transfer, from the same address. The bigger problem is the losing of the IT status. Losting the condition execution information mean the wrong instructions could get executed, or instruction that should have been skipped is executed.

    Also, as you are using my Cortex-M3/M4 book, I think it worth mentioning that the errors in my books are documented in

    Errors in the Definitive Guide for ARM Cortex-M book series

    regards,

    Joseph