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.

Nested Interrupts Causing Spurious Interrupt Firing

Other Parts Discussed in Thread: CONTROLSUITE

This post may be a solution to questions such as these:

  • Seeing interrupt even though not enabling that interrupt
  • Random high priority interrupt firing unintentionally
  • How to nest interrupts of the same group
  • How to nest interrupts of different groups
  • Modify PIEIER to disable/enable interrupts in program

 

RULE:  Do not modify PIEIER registers outside of an ISR for that group.

 PIEIERx should only be modified within a group x interrupt before re-enabling interrupts.  The common use case for this is when using nested interrupts.   For example, PIEIER1 should only be modified within an ISR from group 1. Likewise PIEIER2 should only be modified within a group 2 ISR.

This modification should be done while the PIEACK bit for the group is still set and therefore no interrupts will be sent to the CPU while the modification is being done.

If this rule is violated, then spurious INTx.1 interrupts can be triggered.  

When this happens: The PIEIERx register is used to determine which vector will be used for branch to an interrupt service routine.  Because of this you must follow the proper procedure when clearing the bits within a PIEIERx register as described in the PIE section of the Technical Reference Manual for a particular device.  If an interrupt from another group is sent during the ISR it is flagged at the CPU level, but is not serviced yet.  If then the PIEIER is changed while the ISR is pending then when the CPU comes back and ask for the vector it may not find an interrupt that is both enabled and flagged.  If this is the case it will fetch the first vector in the group.

Properly Handle Nested Interrupts

It is important to note that when you enter an ISR all the interrupts are disabled by the complier via INTM bit.  If you want to nest interrupts of the same group then you should enable higher priority interrupts within a lower priority interrupt that is part of the same group.  This is done by masking the IER appropriately, adjusting the PIEIER (of the same interrupt group), and then enabling interrupts.

You may nest a group interrupt inside another group’s ISR by re-enabling the interrupts for the other group (CPU IER register).  What you cannot do is change the PIEIER register for a group inside a different group ISR.

For example within a group 6 ISR the user can re-enable interrupts for group 1.  What the user cannot do is change the group 1 PIEIER1 register inside group 6 ISR.   The user would have to be inside the group 1 ISR in order to modify the group 1 PIEIER1 register. 

(But, if you must change the PIEIER of group 1 within a group 6 interrupt, there is a procedure for doing so in the PIE section of the Technical Reference Manual for a particular device)

Procedure for software-prioritizing (nesting) interrupts.

  1. Use the CPU IER register as a global priority and the individual PIEIER registers for group priorities. In this case the PIEIER register is only modified within an interrupt. In addition, only the PIEIER for the same group as the interrupt being serviced is modified. This modification is done while the PIEACK bit holds additional interrupts back from the CPU.

  • Global Priority:   This priority can be managed by manipulating the CPU IER register. This register controls the 16 maskable CPU interrupts (INT1 - INT16).  These are the different interrupt groups.
  • Group Priority:   This can be managed by manipulating the PIE block interrupt enable registers (PIEIERx). There is one PIEIERx per group and each control the 8-interrupts multiplexed within that group.  It is very important that only the PIEIERx register for the same group be changed.

        2. Never disable a PIEIER bit for a group when servicing an interrupt from an unrelated group.

  

This is shown in assembly in the TMS320C28x FPU Primer, or you can follow the steps below to implement nested interrupts:

 The steps required to nest interrupts are:

 Step 1: Set the global priority:

  • Modify the IER register to allow CPU interrupts with a higher user priority to be serviced.
  • Note: at this time IER has already been saved on the stack.

Step 2: Set the group priority: (optional)

  • Modify the appropriate PIEIERx register to allow group interrupts with a higher user set priority to be serviced.
  • Do NOT clear PIEIER register bits from another group other than that being serviced by this ISR. Doing so can cause erroneous interrupts to occur.

 Step 3: Enable interrupts:

  • There are three steps to do this:
      1. Clear the PIEACK bits
      2. Wait at least one cycle
      3. Clear the INTM bit. Use the assembly statement asm(" CLRC INTM"); or TI examples use #define EINT asm(" CLRC INTM")

Step 4: Run the main part of the ISR

Step 5: Set INTM to disable interrupts. Use asm(" SETC INTM"); or TI examples use #define DINT asm(" SETC INTM")

Step 6: Restore PIEIERx (optional depending on step 2)

 Step 7: Return from ISR

  • This will restore INTM and IER automatically.

 

Example Code

//// C28x ISR Code
//// Enable nested interrupts 

interrupt void EPWM1_TZINT_ISR(void) 
{
    uint16_t TempPIEIER;
    TempPIEIER = PieCtrlRegs.PIEIER2.all; // Save PIEIER register for later
    IER |= 0x002;                         // Set global priority by adjusting IER
    IER &= 0x002;                         
    PieCtrlRegs.PIEIER2.all &= 0x0002;    // Set group priority by adjusting PIEIER2 to allow INT2.2 to interrupt current ISR
    PieCtrlRegs.PIEACK.all = 0xFFFF;      // Enable PIE interrupts
    asm("       NOP");                    // Wait one cycle
    EINT;                                 // Clear INTM to enable interrupts
    //
    // Insert ISR Code here.......
    // for now just insert a delay
    //
    for(i = 1; i <= 10; i++) {}
    //
    // Restore registers saved:
    //
    DINT;
    PieCtrlRegs.PIEIER2.all = TempPIEIER;
}
#endif

 

Advanced Software-Prioritizing with Masks Values Interrupts

For more advanced interrupt prioritization you can use mask values configured at compile time to set the global and group priorities.  For more information on how to use mask values you can go to this wiki page (Interrupt_Nesting_on_C28x) or you can refer to the software prioritization example in the device support for you particular device found in ControlSUITE.

 


 MORE ON THIS TOPIC:

  • In a very odd case, suppose you do want to change the PIEIERx of a group other than the current ISR's group (even a lower priority group) ....

    One reason I want to do this is, I have my CLA control task trigger off ADCINT1 and runs in parallel to C28x CPU.  Once it completes, I need to update PWMs from C28x CPU core as soon as the CLA task is done (CLA1_INT1 will trigger and update PWMs).  This is a group 11 interrupt.  I need it to execute before the next time PWM CMPA/B registers are loaded.

    Would something like this work for nested interrupts that purposefully allow an interrupt of another (lower priority) group (and only that group)?  I realize that I'd have to post this code in every interrupt that I want to allow CLA1_INT1 to interrupt.

    interrupt void ADCINT1_ISR(void)                /* PIE1.1 @ 0x000D40  ADCINT1                                         */
    {
    Uint16 TempPIEIER;
    Uint16 TempIER;
    
        PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;     /* Must acknowledge the PIE group                                     */
        AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;       /* Clear ADCINT1 flag                                                 */
    
        /************************************************************************************ Allow CLA1_INT1 to interrupt*/
        TempPIEIER = PieCtrlRegs.PIEIER11.all;  //CLA1_INT1 group 11
        TempIER = IER;
        IER |= M_INT11;
        IER &= M_INT11;                         // Set "global" priority
        PieCtrlRegs.PIEIER11.all = 0x0001;      // Enable INT1
        PieCtrlRegs.PIEACK.all = 0xFFFF;      // Enable PIE interrupts
        asm("       NOP");                    // Wait one cycle
        EINT;                                 // Clear INTM to enable interrupts
        /************************************************************************************ Allow CLA1_INT1 to interrupt*/
    
        //DO STUFF
    
        /********************************************************************************************************* Restore*/
        IER = TempIER;
        DINT;
        PieCtrlRegs.PIEIER11.all = TempPIEIER;
        /******************************************************************************************************************/
    }

  • I have verified that the above works if one wanted to allow interrupts of a different (higher or lower priority) group to interrupt an active interrupt.  As the OP points out, this is not normally desired and extreme care should be taken if trying this.