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.

TMS320F280049C: Interrupt nesting modifying multiple PIEIERs within in interrupt

Part Number: TMS320F280049C


I've read this:

C28x Interrrupt nesting

and it says:

"Do not modify PIEIER registers outside of an ISR for that group. For example, PIEIER1 should only be modified within an ISR from group 1. Likewise PIEIER2 should only be modified within a group 2 ISR."

Now, I have a TIMER0 task that needs to run periodically at 10msec and which lasts about 3-4 msec. At the same time I need to service an ADC interrupt at 50 kHz and three BLDC motor HALL interrupts at 1000 Hz.
 I'm using ADCB1, XINT1,XINT2 and XINT3. 

Basically I need my TIMER0 interrupt to be interruptible by everything else.

I create this small test:

__interrupt void timer0_ISR() {
    volatile uint32_t d;

    uint16_t TempPIEIER;
    TempPIEIER = PieCtrlRegs.PIEIER1.all; 
    IER |= 0x801;                         
    IER &= 0x801;
    PieCtrlRegs.PIEIER1.all &= 0x001A;    
    PieCtrlRegs.PIEIER12.all &= 0x0001;    
    PieCtrlRegs.PIEACK.all = 0xFFFF;      
    asm("       NOP");
    // Wait one cycle
    EINT;
    // Clear INTM to enable interrupts

    for (d = 0; d < 6666; d++) // waste some time just to prove that interrupts are nested
        d = d;

    g_timer0_tick++;

    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);

    DINT;
    PieCtrlRegs.PIEIER1.all = TempPIEIER;
}

It seems to work, but it is not heeding the warning I quoted above.

I have not observed any adverse signs.

So is this ok, or what is the problem?

If this is not acceptable, then how can I make TIMER0 interrupt so that basically any other interrupt can be serviced while it is running?

In addition to above I will also need to service XINT4 and SPIB_RX interrupts.







  • Hi,

    Thanks for your question! The issue you will see by modifying PIEIERx bits outside of the current ISR's group is that you will have spurious INTx.1 interrupts firing. So in the case of the code you provided, you would see spurious interrupts from XINT3 firing each time you modify PIEIER12. This is a problem for obvious reasons.

    To avoid this, you can see the steps outlined in the "Disabling Interrupts" section of the TRM, section 3.5.4.3 (section numbers valid as of the date of this post).

    Regards,

    Vince

  • Thanks, does following look it is correct, seems to work and I'm not observing any signs of spurious interrupts, then again I did not see them before so maybe just dumb luck.

    __interrupt void timer0_ISR() {
        //
        // Allow interrupts for ADCB1,XINT1,XINT2,XINT3,XIINT4 and SPIB_RX to interrupt this TIMER0 interrupt so we can ran long computations here peridically
        //
        volatile uint32_t d;
        volatile uint16_t savePIEIER1 = PieCtrlRegs.PIEIER1.all;
        volatile uint16_t savePIEIER6 = PieCtrlRegs.PIEIER6.all;
        volatile uint16_t savePIEIER12 = PieCtrlRegs.PIEIER12.all;
        GPIO_writePin(34, 1); // LED on
        IER |= 0x821;                         // Enable interrupt groups 1,6 and 12
        IER &= 0x821;                          // Disable everything else
        PieCtrlRegs.PIEIER1.all &= 0x001A;     // Enable ADCB1,XINT1,XINT2 interrupts (bits 1,3,4)
        PieCtrlRegs.PIEIER6.all &= 0x0004;     // Enable SPIB_RX interrupt (bit 2)
        PieCtrlRegs.PIEIER12.all &= 0x0003;    // Enable XINT3 and XINT4 interrupts (bits 0,1)
        asm("       NOP");      // Although we disable interrupts, some maybe already propagating in hw
        asm("       NOP");      // ... so we wait 5 cycles with NOPs...
        asm("       NOP");
        asm("       NOP");
        asm("       NOP");
        IFR &= ~0x821 ;          // ... and clear the possible spurious IFR flags for groups 1,6 and 12 
                                 // (Is it ok that we clear GROUP1 flag also?)
        PieCtrlRegs.PIEACK.all = 0xFFFF;       // Enable PIE interrupts
        asm("       NOP");
        // Wait one cycle
        EINT;
        // Clear INTM to enable interrupts
    
        //
        // Now we have allowed interrupt nesting, lets get on with actual periodic timer task
    
        for (d = 0; d < 6666; d++) // waste some time just to prove that interrupts are nested
            d = d;
    
        g_timer0_tick++;
    
        //
        // Finally we need to restore the interrupt enables to the way they were
        //
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
    
        DINT;
    
        // Although we are writing PIEIER6 and PIER12 outside their interrupts this should be safe
        // AFAIU cause we are NOT DISABLING any interrupts so any interrupts we will get are real and not spurious
        PieCtrlRegs.PIEIER1.all = savePIEIER1;
        PieCtrlRegs.PIEIER6.all = savePIEIER6;
        PieCtrlRegs.PIEIER12.all = savePIEIER12;
        GPIO_writePin(34, 0); // LED off
    }
    

  • Hi Kustaa,

    Nice work on the changes, code is almost where it needs to be! Fixes needed are described below:

    • DINT before modifying the PIE/IER/IFR bits (immediately after GPIO_writePin and before IER |= 0x821)
    • Answering your question in the code, you should (likely) not need to clear the IFR manually. The IFR already gets cleared upon entry into the ISR (this is protected from interrupts). When you clear PIEACK for the group, any other enabled PIEIER1.y in group 1 that have their flag PIEIFR1.y set will propagate and set the group's IFR latch again.

    To really make sure there are no issues, I would highly recommend you add an ISR for the first channel in the other groups you are modifying to make sure they are not randomly firing. For example, since you are modifying PIEIER12, add an ISR for INT12.1 (which is XINT3), have it toggle a GPIO, and monitor with a scope/logic analyzer. If it toggles, then the spurious interrupt is still occurring.

    Just a bit of background to help better visualize why it is needed to disable interrupts when we change the PIEIER (for you and others who stumble upon this thread):

    Let's say there is an interrupt (call it interrupt 2.5) that gets pre-empted by another interrupt with higher priority (interrupt 1.5). But, when we were in interrupt 2.5, we changed PIEIER1, and now PIEIER1.5 is not cleared.

    The problem is that the interrupt 1.5 already propagated (that's why we switched over to it and stopped interrupt 2.5). The PIE then looks for an interrupt that is both flagged AND enabled. Interrupt 1.5 is flagged, but it is not enabled. So what does the PIE choose to return by default? It chooses the first channel of the group, interrupt 1.1, and returns that vector.

    This is obviously a problem, particularly if you have an ISR for interrupt 1.1, and you were not expecting it to fire at that time.

    Regards,

    Vince

  • Thanks Vince!

    Why the "DINT before modifying the PIE/IER/IFR"  suggestion?

    I copied that part from the example here:



    https://software-dl.ti.com/C2000/docs/c28x_interrupt_nesting/html/

    A
    nd thanks for the tip to check for the spurious calls by adding ISR for channels 1 on those groups I modify the PIEIER.

    I will try that and report back here for posterity if I find a problem.

    And many thanks for the explanation on the underlaying issue.

    wbr Kusti

  • Hi Kusti,

    For the DINT suggestion, this is from the "Disabling Interrupts" section of the TRM, it is only needed because we are modifying the PIEIER bits of another group.

    Now for the reasoning behind it, when we are modifying all of these PIE and IER/IFR bits, we don't want anything to propagate to the CPU until we are ready and have everything set. When we set DINT, we are opening the very last connection from the interrupt to the CPU, preventing anything else from interrupting our changes. It is in a way making the other changes we make atomic so they don't get interrupted, say, between "PieCtrlRegs.PIEIER12.all &= 0x0003;" and "asm("       NOP");". It is essentially making the nesting configuration un-interruptible. If we allowed interrupts during the nesting configuration, we could potentially not finish the nesting setup, and then we'll have incorrect prioritization.

    I hope that clears up the DINT a bit, thanks again for your questions!

    Regards,

    Vince

  • Thanks Vince,

    Great explanations.

    And great support!

    Having said that let me take this opportunity to give some feedback for TI.

    The hardware is awesome, but documentation not so great everywhere.

    There is so much documentation and while it looks good I often find it lacking in the essential information or the essential information is swamped by the verbiage.

    No-one has time to read, let alone learn it all in todays hectic development environment,

    I don't have the answer but terse and to the point would seem to be a good start. 

    A single diagram how e.g. the interrupt hw works tells me much more than ten pages of descriptions and 'fancy' concepts like these interrupt 'groups' and 'channels'. I still don't understand what makes each individual interrupt into a 'channel'. AFAIU each 'channel' is just an interrupt plain and simple.

    Abstractions are fine when they bring something to the table but when they only obfuscate they are just a hindrance. 

    Incidentally I find this same 'problem' in some of the TI lower level sw code. Looks nice and pro but when I scratch the surface I don't like it so much.

    I continuously find myself looking into the inside of the lower level TI code to see what it actually does and how it relates to the hardware/hardware documentation.

    I don't think that is a good sign.

    I know, nothing can be done to any of this, software has been evolving like the documentation for these processors and TI is a huge and diverge (in every aspect) organisation but I felt I just had to get this off my chest.

    Thanks again for the great support.

    wbr Kusti

    PS doesn't this latest explanation from you make the example in the doc about interrupt nesting I sited above and to which the TRM refers to wrong or lacking...