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.

TMS320F280049: Missing ISR when CPU bandwidth is high

Part Number: TMS320F280049
Other Parts Discussed in Thread: C2000WARE

Hi expert,

My customer is triggering ADC ISR in contineous mode with EPWM carrier and ADC ISR is triggered in 2.5us interval.

When they adding code in ISR we can find ISR will miss (skip one the other) when ISR ececution time get close to 2.3us.

In our recongnition, we will still have 0.2us left for entering and exiting ISR, but why we started to miss ISR at this load?

We are bench marking CPU execution with GPIO toggling, enter ISR - GPIO to high, exit ISR GPIO to low. In this case we can get to know how long does it takes to run the ISR and if it is missing. (in normal conditon of high ISR/CPU load, we should see narrow low voltage on GPIO)

Could you help us a bit more on whether it is normal or not? Why?

Thanks

Sheldon

  • Hi Sheldon,

    From what you are saying, it sounds like the ISR is being skipped due to one of the following:

    1. PIEIFR being cleared by software. If you clear PIEIFR in software, you can lose an interrupt, which is why it should never be cleared in software. You should instead let the CPU take the interrupt to let it clear. Please check through all instances where PIEIFR is being modified in your code and ensure that there is no place where it is cleared manually by software.

    2. Unintended nested interrupts. By default, you cannot nest interrupts. If the global priority or group priority are changed in an ISR, you may be unintentionally nesting interrupts. For a bit more on what I am describing by nested interrupts, please view within C2000Ware the interrupt nesting document:  <C2000Ware>\docs\c28x_interrupt_nesting\html\index.html.

    3. Some strange behavior can happen if you are stepping through the code in CCS one line at a time. If using the debugger, please remove all breakpoints and let the program run freely and see if this removes the problem as well.

    These are the main ones I can think of that could cause the interrupt being missed completely, so please let me know once you have checked these if this fixes your issue! The only real other possibility would be that the trigger itself is not occurring, but as long as you have configured the ADC/EPWM ISRs and their settings correctly, then this issue should likely not occur.

    Regards,

    Vince

  • Hi Vince,

    As for point 2 and 3 are not our case, regarding point 1, we have below settings in our code.

        AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
        PieCtrlRegs.PIEACK.bit.ACK1 = 1;

    Did we do the things right?

    Thanks

    Sheldon

  • Hi Sheldon,

    If this code is located at the end of the ISR, then that looks correct.

    I will mention as well, that assuming you have only 0.2us remaining for an ISR to complete, and you are using a 20 MHz clock frequency, then there is one thing that can come into play regarding the time needed for the ISR:

    The interrupt delay for only the internal portion of the interrupt can technically go up to 32 cycles (taking into account the minimum 14 cycles + 8 fixed-point pushes + 10 floating-point pushes). At 20 MHz frequency, this would be ~1.6 us. This means a higher-priority interrupt could potentially pre-empt during this time. And the number of cycles could technically be higher if you account for items like RPT instructions, multiple-cycle instructions, code execution in wait-stated memory, etc.

    To check if this is the case, are you sure that the interrupt is not occurring after another interrupt? Meaning do you see the ISR just happen late? Or is it really completely being skipped?

    There is nothing else that I know of that could cause a missed interrupt like you are describing, so could you provide a bit more details?

    1. To make sure, nested interrupts are used, or are not used?
    2. Could you provide context for where the PIEACK.bit.ACK1 line is occurring in the code?
    3. Can you provide a bit more detail on the EPWM and ADC setup you have? From what I understand, you are using EPWM to trigger an ADC SOC every 2.5us. And if I am understanding correctly, the ADC conversion itself is taking up to 2.3us. You are saying that if the conversion goes above 2.2us (approaches 2.3us), then the EPWM 
    4. Could you clarify what you mean by the following quote? Is the EPWM is changing to 2.3us? Why is the ISR execution time changing?
      When they adding code in ISR we can find ISR will miss (skip one the other) when ISR ececution time get close to 2.3us.
    5. Any other info and details you can provide would be great as well, to help debug this further!

    Regards,

    Vince

  • Hi Vince,

    Thanks for your very detailed explanation here!

    We have seen ISRs completely skipped instead of just being late.

    We are not using nested ISR here, there is only one ISR in our code and configuration is shown below:

        EALLOW;
        PieVectTable.ADCA1_INT  = &adca_isr;
        EDIS;
    
        EALLOW;
        PieCtrlRegs.PIECTRL.bit.ENPIE = 1;  // Enable the PIE block
        PieCtrlRegs.PIEIER1.bit.INTx1 = 1;  // Enable ADCA1 interrupt
        //PieCtrlRegs.PIEIER1.bit.INTx3 = 1;  // Enable ADCC1 interrupt
        EDIS;
    
        IER |= M_INT1;
        EINT;
        ERTM;

    Regarding the context of ACK1, please see below, the ISR:

    // ADC interrupt service routine, triggered by ADC EOC
    #pragma CODE_SECTION(adca_isr, ".TI.ramfunc")
    __interrupt void adca_isr(void)
    {
        GpioDataRegs.GPASET.bit.GPIO7 = 1;
    
    //    AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
    //    PieCtrlRegs.PIEACK.bit.ACK1 = 1;
        // ADC input range = 0 to 4096
        // With offset subtraction, range will be -2048 to + 2047
        InputADC = (int16_t) (AdcaResultRegs.ADCRESULT0 - adc_offset);                   // offset
    
    //    temp_val[0] = (Uint16)(single_turn >> 16);
    //    temp_val[1] = (Uint16)single_turn;
        // force CLA task
    //    Cla1ForceTask1();
    
    #ifdef Undersampling
            switch(demo_i){
                case 0:
                        DualFilter.InFilter1 = 0;
                        DualFilter.InFilter2 = InputADC;
                        demo_i = 1;
                        break;
                case 1:
                        DualFilter.InFilter1 = InputADC;
                        DualFilter.InFilter2 = 0;
                        demo_i = 2;
                        break;
                case 2:
                        DualFilter.InFilter1 = 0;
                        DualFilter.InFilter2 = -InputADC;
                        demo_i = 3;
                        break;
                case 3:
                        DualFilter.InFilter1 = -InputADC;
                        DualFilter.InFilter2 = 0;
                        demo_i = 0;
                        break;
            }
    #else
        DualFilter.InFilter1 = (int16_t) ((InputADC * sinVal[demo_i]) >> 19);
        DualFilter.InFilter2 = (int16_t) ((InputADC * cosVal[demo_i]) >> 19);
    
    
        demo_i++;
        demo_i = (demo_i <= 7)? demo_i : 0;
    #endif
    
        DualFIRCalc_V1(DualFilter);
    
    #ifndef Algorithm2
    //    OutFilter1_CPU_to_CLA = DualFilter.OutFilter1;
    //    OutFilter2_CPU_to_CLA = DualFilter.OutFilter2;
        theta = __atan2puf32((float) DualFilter.OutFilter1, (float) DualFilter.OutFilter2);
    
        single = (single & 0x1E0000) + theta * 131071 + 5535;
    
        if(pre_theta > 98304 && tmp_theta < 32767)
            single = single + 131072;
        else{
             if(tmp_theta > 98304 && pre_theta < 32767)
                 single = single - 131072;
            }
    
          pre_theta = tmp_theta;
    
    
    #else
        switch(demo_i2){
            case 0:
                    DualFilter2.InFilter1 = 0;
                    DualFilter2.InFilter2 = 0;
                    demo_i2 = 1;
                    break;
            case 1:
                    DualFilter2.InFilter1 = DualFilter.OutFilter1;
                    DualFilter2.InFilter2 = DualFilter.OutFilter2;
                    demo_i2 = 2;
                    break;
            case 2:
                    DualFilter2.InFilter1 = 0;
                    DualFilter2.InFilter2 = 0;
                    demo_i2 = 3;
                    break;
            case 3:
                    DualFilter2.InFilter1 = -DualFilter.OutFilter1;
                    DualFilter2.InFilter2 = -DualFilter.OutFilter2;
                    demo_i2 = 0;
                    break;
        }
    
        DualFIRCalc_V1(DualFilter2);
    
        OutFilter1_CPU_to_CLA = (float)DualFilter2.OutFilter1;
        OutFilter2_CPU_to_CLA = (float)DualFilter2.OutFilter2;
    #endif
    
        temp_val[0] = (Uint16)(single >> 16);
        temp_val[1] = (Uint16)single;
    
        // re-initialize interrupt
        AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
        PieCtrlRegs.PIEACK.bit.ACK1 = 1;
        GpioDataRegs.GPACLEAR.bit.GPIO7 = 1;
    }

    To be continue...

  • Yes, I am triggering ADC SOC with EPWM at every 2.5us.

    EPWM set up is:

    // EPWM1 A/B and EPWM2 A/B doing SVPWM output
    // EPWM3 SOCA trigger ADC S + H
    void ConfigureEPWM(void)
    {
        // PWM clock set to 100MHZ, stupid reset value
        EPwm1Regs.TBCTL.bit.CLKDIV = 0;
        EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0;
        EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW; // Shadow mode is a must
        EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
    
        EPwm2Regs.TBCTL.bit.CLKDIV = 0;
        EPwm2Regs.TBCTL.bit.HSPCLKDIV = 0;
        EPwm2Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW; // Shadow mode is a must
        EPwm2Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
    
        EPwm3Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW; // Shadow mode is a must
        EPwm3Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
        EPwm3Regs.TBCTL.bit.CLKDIV = 0;
        EPwm3Regs.TBCTL.bit.HSPCLKDIV = 0;
    
        EPwm4Regs.TBCTL.bit.CLKDIV = 7;                 // Set /128
        EPwm4Regs.TBCTL.bit.HSPCLKDIV = 5;              // Set /10
        EPwm4Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;     // Shadow mode is a must
        EPwm4Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
    
        // Stop the ePWM clock
        EALLOW;
        CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0;
        EDIS;
    
        EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;
        EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;       // counter up down mode
    
        EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;           // Master: Output sync signal when CTR = 0
        EPwm1Regs.TBPRD = PERIOD_50K_UD;                     // carrier period
        EPwm1Regs.CMPA.bit.CMPA = PERIOD_50K_UD / 2;
        EPwm1Regs.CMPB.bit.CMPB = PERIOD_50K_UD / 2;
    
        EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
        EPwm1Regs.AQCTLA.bit.CBD = AQ_CLEAR;
        EPwm1Regs.AQCTLB.bit.CAU = AQ_CLEAR;
        EPwm1Regs.AQCTLB.bit.CBD = AQ_SET;
    
        EPwm2Regs.TBCTL.bit.PHSEN = TB_DISABLE;
        EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // counter up down mode
    
        EPwm2Regs.TBPRD = PERIOD_50K_UD;      // carrier period
        EPwm2Regs.CMPA.bit.CMPA = PERIOD_50K_UD / 2;
        EPwm2Regs.CMPB.bit.CMPB = PERIOD_50K_UD / 2;
    
        EPwm2Regs.AQCTLA.bit.ZRO = AQ_CLEAR;
        EPwm2Regs.AQCTLA.bit.PRD = AQ_SET;
        EPwm2Regs.AQCTLB.bit.ZRO = AQ_SET;
        EPwm2Regs.AQCTLB.bit.PRD = AQ_CLEAR;
    
        EPwm3Regs.TBCTL.bit.PHSEN = TB_DISABLE;
    
        EPwm3Regs.ETSEL.bit.SOCAEN = 1;                   // Enable EPWMxSOCA pulse
        EPwm3Regs.ETSEL.bit.SOCASEL = ET_CTR_ZERO;        // Select SOC on up-count
        EPwm3Regs.ETPS.bit.SOCAPRD = ET_1ST;              // Generate pulse on 1st even
        EPwm3Regs.ETCNTINITCTL.bit.SOCAINITEN = 1;
    
        EPwm3Regs.TBPRD = Sampling_rate_400K;             // Set sampling rate
    
        EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;        // counter up mode
        EPwm3Regs.CMPA.bit.CMPA = 10;
    //    EPwm3Regs.AQCTLA.bit.ZRO = AQ_SET;
    //    EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR;
    
        EPwm4Regs.ETSEL.bit.SOCAEN =  1;    // Enable EPWMxSOCA pulse
        EPwm4Regs.ETSEL.bit.SOCASEL = 1;    // Select SOC on up-count
        EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;    // counter up mode
        EPwm4Regs.ETPS.bit.SOCAPRD = 1;     // Generate pulse on 1st event
        EPwm4Regs.TBPRD = 39061;            // 2HZ for SOC
    
    }

    ADC configurations:

    void ConfigureADC(void)
    {
        // ADC
        SetVREF(ADC_ADCA, ADC_EXTERNAL, ADC_VREF2P5);
        EALLOW;
        AdcaRegs.ADCCTL2.bit.PRESCALE = 6;      // divided by four
        AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;   // EOC interrupt pulse
        AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;      // Powered up
        DELAY_US(1000);                         // Wait for initialization
    
        AdcaRegs.ADCSOC0CTL.bit.CHSEL = 2;      // SOC0 will convert pin A2
        AdcaRegs.ADCSOC0CTL.bit.ACQPS = 39;     // sample window is 40 SYSCLK cycles
        AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 9;    // trigger on ePWM3 SOCA
        AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 0;  // EOC0 will set INT1 flag
        AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1;    // enable INT1 flag
        AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;  // make sure INT1 flag is cleared
        EDIS;
    
        // ADC measure the temperature
        SetVREF(ADC_ADCC, ADC_EXTERNAL, ADC_VREF2P5);
        EALLOW;
        AdccRegs.ADCCTL2.bit.PRESCALE = 6;      // divided by four
        AdccRegs.ADCCTL1.bit.INTPULSEPOS = 1;   // EOC interrupt pulse
        AdccRegs.ADCCTL1.bit.ADCPWDNZ = 1;      // Powered up
        DELAY_US(1000);                         // Wait for initialization
    
        AdccRegs.ADCSOC1CTL.bit.CHSEL = 0;      // SOC1 will convert pin C0
        AdccRegs.ADCSOC1CTL.bit.ACQPS = 99;     // sample window is 100 SYSCLK cycles
        AdccRegs.ADCSOC1CTL.bit.TRIGSEL = 11;   // trigger on ePWM4 SOCA
        AdccRegs.ADCINTSEL1N2.bit.INT1SEL = 0;  // EOC1 will set INT1 flag
        AdccRegs.ADCINTSEL1N2.bit.INT1E = 0;    // enable INT1 flag
        AdccRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;  // make sure INT1 flag is cleared
        EDIS;
    }

    To answer question three, our EPWM trigger is always fixed and the variable is how much thing we want to do in ISR or how much code we want to run in ISR. ISR exection time changes with what we are adding in each build. In the same build, ISR execution time is fixed.

    I'd like to clarify that ADC conversion itself it quite short, that is the code running in ADC ISR consums much time.

    BTW, we are running at 100M system clock, the interrupt enter time could be as long as 0.32us. Is my understanding correct?

    Thanks

    Sheldon

  • Hi Sheldon,

    Thanks for the detailed follow-up! Thanks for providing the ISR code as well, as this now clarifies that perhaps the interrupt enter time + ISR time could be the cause of this. I am discussing this internally and will get back to you with a response by Tuesday.

    Regards,

    Vince

  • Hi Sheldon,

    After discussing, it looks like it is possible that you could be overflowing the ADC interrupt buffer by writing to it while the other interrupt is still being processed (if I am understanding your question properly). In other words, if your interrupt request (req2) comes in while the first request (req1) is processing, then at the end of the interrupt both flags get cleared.

    EDIT: Also, I would like to point that the number of cycles mentioned previously (32) could theoretically be even higher, if there are pipeline delays due to wait-stated memory, RPT instructions, multiple-cycle instructions, etc. So it is definitely possible that the interrupt itself (from request to finished executing) could be taking up longer than 2.5us. For example, if it is the minimum 32 cycles like mentioned previously, then 0.32us would be for entry, plus the 2.3us of the ISR process itself, which would be longer than the 2.5us cycle. It will be important to be cognizant of these items.

    Let me know if this provides a bit more clarity.

    Regards,

    Vince

  • Hi Vince,

    Thanks for your detailed explanation and follow up. I think I am clear on this now.

    Sheldon