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 and interrupt priorities

Part Number: TMS320F280049C
Other Parts Discussed in Thread: C2000WARE, SYSBIOS

Hi TI Team,

I have an application where the ADCC1 conversion complete drives DMA1 for data transfer.  When DMA 1 transfer is complete it fires the DMA CH1 interrupt to perform timing critical processing.  We are running this interrupt outside the OS as an NMI to make sure it does not get preempted or delayed.

I am now trying to add a system protection Interrupt attached to XINT1, and want this one to run at the highest priority.  I configured this one inside the OS.  Since XINT1 is supposed to be of higher priority than DMA and SCI I would have expected this interrupt would preempt those of lower priority and run first.  But this was not the case.

I suspected that because I have the DMA interrupt as NMI that it cannot be preempted.  Therefore, I remove the NMI setting and see an extra processing time hit I assume due to OS overhead, but to make things worst, if I start communicating to the system over my serial port on SCI-B (using interrupts) then the frequency and durations of my critical ISR become unstable.

My first thought was to move the DMA interrupt inside the OS, assuming that the OS would then control who goes next based on the hardware priority with the enable nesting flag set.  But this did not work.

A few other postings just point to this 2020 writeup on the topic: C28x Interrupt Nesting.  However, this is not very clear, and do not use driverlib functions.

The posting suggests that the ISR functions need to be modified so that they are polite and yield the processor to other interrupts, but no matter what I tried I still get the same odd results.

In a nutshell the posting above mentions this sequence:

    uint16_dtc TempPIEIER;
    TempPIEIER = PieCtrlRegs.PIEIER2.all; // Save PIEIER register for later
    IER |= 0x0002;                        // Set global priority by adjusting IER
    IER &= 0x0002;
    PieCtrlRegs.PIEIER9.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

    // Here do my interrupt code
    ...

    DINT;
    PieCtrlRegs.PIEIER2.all = TempPIEIER;

Can you guys provide an example using driverlib to achieve the desired behavior?

XINT1 should be top priority

DMA1 interrupt should be second

The rest should follow (SCI and I2C)

I added the code below into my SCI-B RX and TX ISRs following the comments on the sample code, but still get the same results.  My DMA 1 interrupt inside the OS (not NMI) is still been preempted by SCI-B communications.

    Interrupt_enableInCPU(INTERRUPT_CPU_INT7);
    Interrupt_disableInCPU(INTERRUPT_CPU_INT9);
    Interrupt_enablePIE();
    Interrupt_enableMaster();

If I move both XINT1 and DMA1 outside the OS as NMI will their priorities be respected?

  • Hello Rafael,

    Our expert will get back to you by tomorrow.

    Best regards,

    Omer Amir

  • Hi Rafael,

    Can you take a quick look at the below example from driverlib.

    ..\C2000Ware_4_03_00_00\driverlib\f28004x\examples\interrupt\interrupt_ex3_sw_prioritization

    regards 

    Aswin

  • Ok, so the answer points to an example which I have already looked at.  This example creates a bunch of macros defined based on settings on a .h file to customize interrupt priorities via software.

    But my biggest concern is why when I create all interrupts inside SYSBIOS (aka configured via .cfg file), and I turn on "enable interrupt nesting" the hardware interrupt priority is not respected?

    My DMA interrupt is higher than SCI interrupt per Hardware Priority, but when I tested this with SCI-B communications then the DMA interrupt frequency/duration becomes unstable.  Why?

    I would expect that if my interrupts are inside the SYSBIOS that then enabling nesting should be all I need to do.  Do I need to modify my ISRs with specific masks and priority settings?  If so then what is the sysbios config "enable interrupt nesting" doing for me? 

  • When you say that you are running the DMA interrupt as an NMI, what do you mean? Are you just saying its what SYS/BIOS calls a "zero latency" interrupt? Or does this have something to do with the actual device NMI?

    Do you mind sharing your Hwi settings from your cfg file? In particular, I'm most interested in your zeroLatencyIERMask, dispatcherAutoNestingSupport, disableMask, restoreMask settings. Are you using Hwi_plug in your code for your non-OS interrupts?

    In case you haven't seen it already, there's some additional info on these Hwi features in this thread:

    https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/953170/faq-are-there-any-c28-device-specific-items-with-ti-rtos-sys-bios

    Thanks,
    Whitney

  • Hi Whitney,

    Thank you for looking into my inquiry.

    My baseline product (in production):

    • Timer 0 drives the ADC conversion, end of last channel in the conversion triggers DMA transfer.
    • DMA to transfers ADC results into a RAM data structure used by our highspeed critical control loop (running at several 10s of kHz).
    • Because of the high frequency, and to avoid latency by SYSBIOS managed interrupts we configured this one as what I am calling NMI, or perhaps I should use the term zero latency.  I have the snippet below in our sysbios.cfg file.

     

    /* This is to prevent BIOS from masking out (or temporarily disabling) the high speed 
     * DAC Chan 1 ISR which transfers all ADC Results registers into RAM and initiates the 
     * high speed processing.  DAC Chan 1 interrupt is in PIE Group 7, therefore this mask
     * sets bit 7 to configure this group as zero latency ISR. Notice that this setting affects
     * all interrupts in this group. */
    Hwi.zeroLatencyIERMask = 0x0040;
    

    • Then I hook this interrupt with Hwi_pug as follows:

    /**
     * DMA Settings
     */
    #define DMA_CH1_INTERRUPT_GROUP 7
    #define DMA_CH1_PIE_INTERRUPT_NUMBER 80
    #define DMA_CH1_PIE_INTRRUPT_MASK 0x0001
    #define DMA_INTERRUPT_MASK 0x0040
    
        Hwi_plug(DMA_CH1_PIE_INTERRUPT_NUMBER, (Hwi_PlugFuncPtr)&adcDmaXferComplete);
        Hwi_enablePIEIER(DMA_CH1_INTERRUPT_GROUP, DMA_CH1_PIE_INTRRUPT_MASK);
        Hwi_enableIER(DMA_INTERRUPT_MASK);
    

    New sibling product (initial prototyping/development stages):

    • Core of the functionality is the same.  A few new features to be added soon.
    • Added the use of XINT1 as a fault protection interrupt to disable PWM output with a less than 10usec requirement.
    • 1st approach: I created the interrupt inside the sysbios.cfg (as shown below) and its corresponding ISR code

    var hwi2Params = new Hwi.Params();
    hwi2Params.instance.name = "INVERTER_FAULT_ISR";
    hwi2Params.enableInt = true;
    hwi2Params.enableAck = true;
    Program.global.INVERTER_FAULT_ISR = Hwi.create(35, "&inverterFaultISR", hwi2Params);
    
    
    //-----------------------------------------------------------------------------
    // Inverter Fault ISR - External Interrupt
    // Inverter fault interrupt connected to pin GPIO 24
    //-----------------------------------------------------------------------------
    void inverterFaultISR(void)
    {
        // Inverter fault detected, disable the PWM output
        writeToOutputGPIO(PWM_ENABLE_GPIO_NUM, PIN_LOW);
    
        // Increase the counter that will let the background task schedule reads for this
        // I2C IO Expander
        ++ioxReadNeeded;
    }
    

    • While injecting the fault, we observed that the Fault to PWM disable time was fluctuating between a couple of 2usec to up 33usec.  To me this suggested that this ISR was getting preempted or delayed.
    • 2nd approach: Remove the zero latency setting and HWI plug for the DMA.  Configure them all with Hwi.Params() in the cfg file. Removed the  use of the interrupt keyword form the ISR function and use of HWI Pugs.
    • This seemed ok, until I connected RS-485 on SCI B.  When we started communications on this bus using SCI interrupts, then the frequency and duration of my critical loop (driven by the DMA interrupt) became unstable.  Which I did not expect this at all.  This is where I am having trouble with.
    • I then turned on the enable interrupt nesting in the sysbios.cfg file with this line ti_sysbios_hal_Hwi.dispatcherAutoNestingSupport = true; but this made no difference.

    This comment you made above makes me scratch my head.  Perhaps I am missing something critical in order to make the interrupt nesting work.

    "dispatcherAutoNestingSupport, disableMask, restoreMask settings".

    This is the first time I am dealing with this level of details between interrupts.  My naive thought was that just setting dispatcherAutoNestingSupport to true that then the compiler would add the necessary code to allow this to happen.

    What am I missing?

    Do I need to modify all my ISRs to add proper priority masking, enable and disable instructions for this to work ritht?

    If this is the case, then what is this doing for me?

    ti_sysbios_hal_Hwi.dispatcherAutoNestingSupport = true;

    Maybe to much info. Or maybe too little.  Let me know.

    Thanks again for your time.

    Rafael

  • Forgot to share my ISR for the DMA interrupt.  Of course when I brought this into the OS (aka configured inside .cfg file), I removed the interrupt keyword.

    But that was the extent of my change.  If I was supposed to add priority masks, enable, disable here, then that may explain the issue.

    interrupt void adcDmaXferComplete(void)
    {
        // call the application to process the data
        (*applicationCallbackFunPtr)();
    
        // Clear the ADC Device level interrupt flag and issue ACK so that the device will
        // re-generate this interrupt on the next conversion.
        // Note that this ADC interrupt does not interrupt the application but it is mapped to
        // signal the DMA for data transfer.
        ADC_clearInterruptStatus(devBaseForInterrupt, ADC_INT_NUMBER1);
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
    
        // Clear the DMA Device interrupt flag and issue ACK so that it will trigger
        // again on the next DMA transfer.
        DMA_clearTriggerFlag(DMA_CH1_BASE);
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
    }
    

  • Thanks for the details. Yes, I think the mask configuration stuff is what you're missing. You can take a look at the dispatcher code in <bios install>\packages\ti\sysbios\family\c28\Hwi.c to get an idea of how they're being used. It looks like if you just turn on nesting support, BIOS will re-enable all interrupts within the ISR, but if you want to make it so that no one can interrupt XINT and only XINT can interrupt DMA_CH1, for example, you'll want to configure the disableMask options for those interrupts.

    It's similar to what is being done in that interrupt_ex3_sw_prioritization example, except BIOS only supports the masking at the IER level, not PIEIER. This shouldn't be a problem for you though since it seems all your interrupts are in different groups.

    Whitney