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.

EVMK2G: CIC Interrupt nesting

Part Number: EVMK2G
Other Parts Discussed in Thread: SYSBIOS

Dear experts,

for my work I am developping a bare metal code on the evaluation board EVMK2G that is loaded in the DSP C66x by means of the on-board debugger TI XDS2xxx.

I downloaded the ti-processor-sdk-rtos-k2g-evm-06.03.00.106-Windows-x86-Install.exe and I installed CCS10.2

My application deals with a motor control by means of 2 EPWM signals.

I properly programmed the EPWM1 and EPWM2 modules. Each module triggers the corresponding ISR by means of INTC and CIC modules. 

EPWM1 triggers the ISR1  that controls its period. ISR1  is very short and has the priority 4. It is mapped on CIC_OUT_33.

EPWM2 triggers the ISR2 that controls its period. ISR2 is long and has the priority 5. It is mapped on CIC_OUT_34.

I have problems with the nesting of ISR1 inside ISR2. In particular, when ISR1 nests the ISR2, the period of EPWM2 is wrong.

The problem disappears if I disable the nesting of ISR1 inside ISR2.  

void ISR1void) //ISR triggered by the module EPWM1
{
unsigned int old_csr;
unsigned int old_irp;
unsigned int old_itsr, old_ier;
static int v;

old_irp = IRP ;// Save IRP
old_csr = CSR ;// Save CSR (and thus PGIE)
old_itsr = ITSR; // Save ITSR
old_ier = IER; // Save IER
IER=0x0;
for(v = 0; v < 4; v++) {IER |= (1 << v);};
CSR = old_csr | 1 ; // Enable interrupts

/* period_regulation code */

HW_WR_REG32(CSL_CIC_0_REGS + CSL_CPINTC_STATUS_CLR_INDEX_REG, (uint32_t)CSL_CIC_EPWM_1_INT); // Clear the status of the CIC system interrupt number 201 (EPWM_1_INT)
HW_WR_REG16(CSL_PWM_1_CFG_REGS + PWMSS_EPWM_ETCLR, 0x1); // Clear the ETFLG register of the module EPWM1

CSR = CSR & -2 ; // Disable interrupts
CSR = old_csr ; // Restore CSR (and thus PGIE)
ITSR = old_itsr; // Restore ITSR
IER = old_ier; // Restore IER
IRP = old_irp ; // Restore IRP

}

void ISR2void) //ISR triggered by the module EPWM2
{
unsigned int old_csr;
unsigned int old_irp;
unsigned int old_itsr, old_ier;
static int v;

old_irp = IRP ;// Save IRP
old_csr = CSR ;// Save CSR (and thus PGIE)
old_itsr = ITSR; // Save ITSR
old_ier = IER; // Save IER
IER=0x0;
for(v = 0; v < 5; v++) {IER |= (1 << v);};
CSR = old_csr | 1 ; // Enable interrupts

/* period_regulation code */

HW_WR_REG32(CSL_CIC_0_REGS + CSL_CPINTC_STATUS_CLR_INDEX_REG, (uint32_t)CSL_CIC_EPWM_2_INT); // Clear the status of the CIC system interrupt number 202 (EPWM_2_INT)
HW_WR_REG16(CSL_PWM_2_CFG_REGS + PWMSS_EPWM_ETCLR, 0x1); // Clear the ETFLG register of the module EPWM2

CSR = CSR & -2 ; // Disable interrupts
CSR = old_csr ; // Restore CSR (and thus PGIE)
ITSR = old_itsr; // Restore ITSR
IER = old_ier; // Restore IER
IRP = old_irp ; // Restore IRP

}

The TRM SPRUHY8I reports the following suggestion:

If the host can accept other host interrupts while processing the ISR then disabling all the host interrupts(in Step 2) would be suggested, assuming the ISR only wants to process one interrupt at a time. There is
a global control bit (bit [0] in Global Enable Register) to enable/disable all the host interrupts to make theprocess easier (the individual host interrupt enables are maintained as well).

Is it possible to nest two ISRs by means of CIC module?

Best regards,

Benito

  • Hi Benito,

    English is not my native language, so i am not sure I have picked you situation right. Do you mean, that if ISR2 is running, and higher priority ISR1 happens in the middle, than ISR2 takes longer to complete? If so, that is natural and expected. But probably you had other point, so we'd better hear some kind of clarification.

  • Dear mr. Victor Kazmirenko,

    thank you for your feedback.

    I am sorry. Probably I did not explained well my problem.

    I am able to implement NESTING SOFTWARE on INTC of K2G c66x DSP.

    However I am not sure to have correctly implemented NESTING SOFTWARE on CIC (Chip Interrupt Controller). 

    Within the ISR, I added the following instructions: 

    void ISR2(void) // ISR triggered by the module EPWM2
               {
                unsigned int old_csr;
                unsigned int old_irp;
                unsigned int old_itsr, old_ier;
               static int v;

               old_irp = IRP ;// Save IRP
               old_csr = CSR ;// Save CSR (and thus PGIE)
               old_itsr = ITSR; // Save ITSR
               old_ier = IER; // Save IER
               IER=0x0;
               for(v = 0; v < 5; v++) {IER |= (1 << v);};
               CSR = old_csr | 1 ; // Enable interrupts

               

               /* Here there are the instruction to set the PERIOD of EPWM2 equal to 833us */  

           

               HW_WR_REG32(CSL_CIC_0_REGS + CSL_CPINTC_STATUS_CLR_INDEX_REG, (uint32_t)CSL_CIC_EPWM_2_INT); 

               HW_WR_REG16(CSL_PWM_2_CFG_REGS + PWMSS_EPWM_ETCLR, 0x1);

              CSR = CSR & -2 ; // Disable interrupts
              CSR = old_csr ; // Restore CSR (and thus PGIE)
              ITSR = old_itsr; // Restore ITSR
              IER = old_ier; // Restore IER
              IRP = old_irp ; // Restore IRP

             }

     

    Red text is the instructions to implement the NESTING SOFTWARE on INTC.

    HW_WR_REG16(CSL_PWM_2_CFG_REGS + PWMSS_EPWM_ETCLR, 0x1) is the instruction to clear the bit EPWM_ETFLG[0] in order to trigger the next occurance of  ISR2 within INTC

    HW_WR_REG32(CSL_CIC_0_REGS + CSL_CPINTC_STATUS_CLR_INDEX_REG, (uint32_t)CSL_CIC_EPWM_2_INT);  is the instruction to clear the status of the interrupt in CIC module in order to trigger the next occurance of  ISR2 within CIC.

    With the same approach, I implemented the ISR1 as reported in the previous post.

      

    Do you mean, that if ISR2 is running, and higher priority ISR1 happens in the middle, than ISR2 takes longer to complete?

     If ISR1 does not nest ISR2, the period of ISR2 is fixed (833us). This is an expected behaviour. 

    On the other hand, if ISR2 is running and ISR1 happens, next occurance of ISR2 is not correctly triggered. Indeed, the period of ISR2 decreases (it is about 533us)

    I would like to know if my functions ISR2 and ISR1 are correct. 

    The TRM SPRUHY8I reports the following suggestion:

    If the host can accept other host interrupts while processing the ISR then disabling all the host interrupts(in Step 2) would be suggested, assuming the ISR only wants to process one interrupt at a time. There is
    a global control bit (bit [0] in Global Enable Register) to enable/disable all the host interrupts to make theprocess easier (the individual host interrupt enables are maintained as well).

    This suggestion comes from the TRM.  If I disable all the host interrupts at the beginning of  each ISR, the occurance of ISR2 is always correct but the ISR1 is delayed because the software nesting is disabled.  This is not allowed in my application because ISR1 has the highest priority and it cannot be delayed. 

    Best regards,

    Benito

  • Benito,

    I see your point, but sorry, I am of no help here. Whenever we have realtime system with asynchronous events we use sysbios rather than attempt coding rtos. I guess the situation is called preemption in some documents. To my knowledge, to make program with interrupts one have to preserve registers modified by ISR and restore them on return. This can be done by compiler if interrupt keyword is specified for ISR. I don't see that in your snippet, hope it's there in your real code. Sorry, no experience with baremetal.

  • Dear mr Victor Kazmirenko, 

    thank you for your support. 

    Probably, as you mentioned, the problem is the pre-emption. 

    Indeed, my code fails if ISR1 happens while ISR2 is executing the following instructions: 

    etaMax = ptNOM->etaMaxS-(ptNOM->frEtaMaxS-freal)*ptNOM->KetaMax;
    etaMax = (etaMax < ptNOM->etaMaxS) ? etaMax : ptNOM->etaMaxS;
    etaMax = (etaMax > ptNOM->etaMaxI) ? etaMax : ptNOM->etaMaxI;

    Probably a wrong preservation of the registers involves wrong calculations of my variables.

    By replacing the static struct ptNom with numbers, the problem disappears.....!

    etaMax = 1.0-(30.0-freal)*0.05;
    etaMax = (etaMax < 1.0) ? etaMax : 1.0;
    etaMax = (etaMax > 0.85) ? etaMax : 0.85;

     

    To my knowledge, to make program with interrupts one have to preserve registers modified by ISR and restore them on return

    Yes, I do. At the beginning of my ISRs I preserve the following registers: CSR, ITSR, IER and  IRP. Should I preserve other registers? ...

    This can be done by compiler if interrupt keyword is specified for ISR. I don't see that in your snippet, hope it's there in your real code.

    I do not use the keyword __interrupt. I developed my code starting from the CSL examples within  the pdk_k2g_1_0_16. No example uses this keyword...

    I have just added this keyword to my ISRs but they never happens...the system does not work anymore.

    Best regards,

    Benito

  • Benito,

    I don't want to guide you wrong, so just few more considerations rather than real help.

    There is certain gap between compiler user guide and CSL examples in PDK. Requirement for interrupt keyword in compiler manual is correct. That is a way it produces register save and restore for ISRs. Compiler monitors which processor registers get manipulated in ISR and preserve their content.

    CSL examples are a way advanced software. Your observation is correct, that examples do not directly use interrupt keyword. I thinks that is because they have background infrastructure for that. Because you have mentioned pwm modules, let's take a look on pdk_k2g_1_0_11\packages\ti\csl\example\epwm\epwm_duty_cycle_test_app\apwm_app.c. At the very bottom of that file there calls to interrupt handles registering routines, like

        /* Register ISR */
        Intc_IntRegister(APP_EHRPWM_INT, (IntrFuncPtr) AppPwmIntrISR, 0);
        Intc_IntPrioritySet(APP_EHRPWM_INT, 1, 0);
        Intc_SystemEnable(APP_EHRPWM_INT);
    

    There routines defined in pdk_k2g_1_0_11\packages\ti\csl\arch\c66x\src\interrupt.c. And brief look inside suggests there is a table of service functions, declared with interrupt keyword, and their only purpose is to be a wrapper for user function. That explains, why your code was somehow working without that keyword. Also that way user don't have to worry about register save/restore.

    So the only question is about CSL example - is it designed to have interrupt preemption or not? I have no answer for that, we'd better wait until TI expert chimes in.

  • Dear mr Victor Kazmirenko,

    thank you for your help. 

    I performed a simple test. I programmed the module TIMER 1 to trigger a ISR named timerISR with a period of 100us. 

    Here below the function I used to program the module INTC. 

    eventId = (int32_t)CSL_C66X_COREPAC_TIMER_1_INTL; //  C66x Interrupt Sources ID

    intNum=4; // interrupt priority

    addrIsr=&timerIsr; // address of ISR

    intc_init(eventId, intNum, addrIsr); 

     

    The function intc_init is reported in the following snippet:

    void intc_init(uint32_t event, int32_t intNum, void *addrIsr)
    {
        CSL_IntcObj IntcObj;
        CSL_IntcEventHandlerRecord  evtHandler;
        CSL_IntcVectId n_priority;
    
         // record the index in the handle
         gContext.numEvtEntries = (Uint16)C66X_COREPAC_EVENT_CNT;
         gContext.eventhandlerRecord = gEventRecord;
         (void)CSL_intcInit(&gContext);
         FirstTime = (bool)true; // first time occured
         }
    
         (void)CSL_intcGlobalNmiEnable(); // This API enables global NMI
         (void)CSL_intcGlobalEnable((CSL_IntcGlobalEnableState *)NULL_PTR); // The API enables the global interrupt
    
         IntcHandle = CSL_intcOpen(&IntcObj, (CSL_IntcEventId)event, (CSL_IntcParam *)&intNum, (CSL_Status *) NULL_PTR); // It returns a handle to the INTC
    
         evtHandler.arg = (void *) (uintptr_t)&IntcHandle;
         evtHandler.handler = (CSL_IntcEventHandler)addrIsr;
         (void)CSL_intcPlugEventHandler(IntcHandle, &evtHandler); // It associates the event-handler to the event so that the occurence of the event would result in the event-handler being invoked.
         (void)CSL_intcHwControl(IntcHandle,CSL_INTC_CMD_EVTCLEAR,NULL_PTR); // It clears the events
         (void)CSL_intcHwControl(IntcHandle,CSL_INTC_CMD_EVTENABLE,NULL_PTR); // It enables the events
    
         (void)CSL_intcInterruptEnable((CSL_IntcVectId)intNum);      // Enabling the interrupt in INTC.
    
         //CSL_intcClose(IntcHandle);
        }

    Here below two different behaviours.

    CASE 1: No use of keyword interrupt WITH a function call in ISR

    void timerIsr(void)
         {   GPIOx_write(GPIO_AUDIOEXP16_PIN_NUM, GPIO_PIN_HIGH); // set GPIO high

             DSP_function() // this function performs some calculations 

             GPIOx_write(GPIO_AUDIOEXP16_PIN_NUM, GPIO_PIN_LOW); // set GPIO low
         }

    The interrupt is correctly triggered every 100us

    CASE 2: Use of keyword interrupt WITH a function call in ISR

    interrupt void timerIsr(void)
         {   GPIOx_write(GPIO_AUDIOEXP16_PIN_NUM, GPIO_PIN_HIGH); // set GPIO high

             DSP_function();  // this function performs some calculations 


              GPIOx_write(GPIO_AUDIOEXP16_PIN_NUM, GPIO_PIN_LOW);  // set GPIO low
         }

    The interrupt is triggered only within 30ms. After 30ms the execution suddenly exits from taskISR (the GPIO remains high) and never triggers it again. 

    CASE 3: Use of keyword interrupt WITHOUT any function call in ISR

    interrupt void timerIsr(void)
         {   GPIOx_write(GPIO_AUDIOEXP16_PIN_NUM, GPIO_PIN_HIGH); // set GPIO high

           /* no functions */

             GPIOx_write(GPIO_AUDIOEXP16_PIN_NUM, GPIO_PIN_LOW);  // set GPIO low
         }

    The interrupt is correctly triggered every 100us

    Therefore, in CASE 2 after 30ms the execution enters the DSP_run() but it never returns.... 

    Why does this happen? 

     

    Best regards,

    Benito

  • Benito,

    I think if you are using CSL infrastructure functions like Intc_IntRegister(), then you do NOT need to put interrupt keyword, it is already applied to ISR wrappers in arch guts.

  • Ok. I'll restore my code by removing the keyword interrupt....

    However my first concern still remains...

    etaMax = ptNOM->etaMaxS-(ptNOM->frEtaMaxS-freal)*ptNOM->KetaMax;
    etaMax = (etaMax < ptNOM->etaMaxS) ? etaMax : ptNOM->etaMaxS;
    etaMax = (etaMax > ptNOM->etaMaxI) ? etaMax : ptNOM->etaMaxI;

    This is the definition of my struct ptNOM: 

    const struct nom   *ptNOM;

    struct nom
    {
    unsigned long gesPotMan;
    float copPEr;
    float suiAvv;
    float potNom;
    float vFnom;
    float vFmin;
    float Pn_vFn;
    float jerkP;
    float jerkN;
    float K1Pde;
    float K2Pde;
    float etaMaxS;
    float etaMaxI;
    float frEtaMaxS;
    float KetaMax;
    float deltaPot;
    float KcAvv;
    float mille_copNom;
    float kCorCop;
    float MaxMan;
    float dvFnegMax;
    unsigned long cntDVFmax;
    float invCopNom;
    float fmNomDec;
    float vFnom2;
    };

    By replacing the struct ptNOM  with numbers everything works fine....

    etaMax = 1.0-(30.0-freal)*0.05;
    etaMax = (etaMax < 1.0) ? etaMax : 1.0;
    etaMax = (etaMax > 0.85) ? etaMax : 0.85;

    Therefore, the nesting of the ISR1 within the first script corrupts the calculations....

    Why does this happen?

    Best regards,

    Benito

  • Dear Victor,

    the problem seems to be related to the nesting of the ISR while the following instructions are in execution.

    etaMax = ptNOM->etaMaxS-(ptNOM->frEtaMaxS-freal)*ptNOM->KetaMax;
    etaMax = (etaMax < ptNOM->etaMaxS) ? etaMax : ptNOM->etaMaxS;
    etaMax = (etaMax > ptNOM->etaMaxI) ? etaMax : ptNOM->etaMaxI;

    I focused on the pointer to struct ptNom. 

    By replacing this pointer with the struct, the problem does not occur anymore. 

    etaMax = tbpard.etaMaxS-(tbpard_.frEtaMaxS-freal)*tbpard.KetaMax;
    etaMax = (etaMax < tbpard.etaMaxS) ? etaMax : tbpard.etaMaxS;
    etaMax = (etaMax > tbpard.etaMaxI) ? etaMax : tbpard.etaMaxI;

    Benito

  • Hi Benito,

    We know too little about your application, so opinions could be misleading. From what you describe it might look as data corruption. You should not take this workaround with fixed values as any definitive indication. Just it may happen, that in case of a structure pointer its placement in processor registers falls on some of registers, which get corrupted, while with constants they occupy some other registers, which do not get corrupted. So unless you know what really happens with that pointer, it's not safe to go.

    I've seen you other question about difference in calling function directly or via pointer. I believe that is same situation, when slightly different code may get or may get not corrupted just because of lucky or unlucky coincidence. I would not step in that thread in hope TI people will notice unanswered question faster.

    I have no practical experience with bare metal CSL aided code. I don't know, whether interrupts nesting and preemption is allowed, and if so, should there be any extra measures. I've spent some time looking over docs and source codes, but cannot make definite conclusion on that matter, you'd better wait for TI expert to chime in.

    With all said above, one should not concentrate on nesting and preemption. It might happen, that somehow there is a race condition in your app, when interrupt functions access shared resource like a data structure.

    Searching forum for info about you situation, I have found some time ago we discussed you app design, and I said in case of realtime system with asynchronous events (your pwm interrupts) of different priority I would consider realtime OS, SYSBIOS, as it has provision for all these situations. I am not telling you have to change your app design, just pointing, that at least for treads and preemption SYSBIOS is what it was designed for, it is documented, and there are debugging aids, like logs. With bare metal it seems to be programmer responsibility to take care about those things.

  • Dear Victor,

    I really appreciate your feedbacks and your support. 

    I will wait for a TI expert to chime in. 

    Best regards,

    Benito

  • Hello Benito,

    Sorry but TI does not support TI-RTOS based or baremetal SW development for K2G anymore, as announced here. Please try to search the E2E forum for related issues and solutions.

    Thanks for your understanding.

    Hello Victor,

    Thanks for sharing your insight and ideas. Your contribution is highly appreciated.

    Regards,

    Jianzhong

  • Dear Victor,

    as suggested by mr jianzhongxu, this kind of behavior is caused by memory corruption due to a insufficient stack size. 

    I write this post in order to close the enquiry.

    I hope this thread will be useful to other members of E2E community.

    Thank you Victor.

    Thank you mr jianzhongxu.

    Best regards,

    Benito