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.

TMS570LC4357: VIM/Arm Core interaction (phantom IRQ) with Micrium

Part Number: TMS570LC4357

Hi - I'm using Micrium uCos III with a TMS570LC4357 and am trying to debug a phantom interrupt issue.  I've read spna063.pdf and still don't quite understand the core/VIM dependencies regarding the prevention of phantom interrupts.  Is there a document that more clearly describes how the index register (IRQINDEX, offset 0 in the VIM) is updated/cleared based on interrupts occurring and masking activity?  And also how the core is triggered during this process?   A timing diagram would be GREAT.  Even a logic diagram would help if it illustrates the race condition that is described in scenario 2 of spna063.

In my current configuration there are 2 interrupts enabled, RTI (index = 4, IRQ channel = 3) and DCAN3 (index = 46, IRQ channel = 45).  FIQ is not used.  All interrupts are funneled through the IRQ vector and the OS 'manages' the interrupts by masking all lower priority interrupts when servicing a particular request.  I'm seeing two situations -

1.  RTI occurs, and when interrupts are re-enabled, a nested CAN interrupt occurs which when serviced, winds up having a "0" in the index register.  Note, the way I can tell which IRQ I'm dealing with is I added some assembly code early on in the service to read the index and toggle a GPIO bit based on whether it's either RTI or CAN.  I also toggle a GPIO bit in the Micrium 'nested irq detection' section.  It seems every time a nested interrupt is detected, a phantom interrupt is generated.  Although rare, I don't understand either how to prevent, or, whether it's actually a problem or not - I haven't looked at whether the frame is dropped or whether the frame is serviced at a later time.

2.  A CAN interrupt occurs and another CAN interrupt nests which causes a phantom interrupt.  This I REALLY don't understand because I don't re-enable interrupts during the CAN service.  It's my understanding that CPSR bit 7 is set when an IRQ is serviced, thus disabling interrupts for the duration of the service.  So in this case, unless interrupts are explicitly re-enabled by clearing CPSR bit 7 (which I'm not), I don't see how the CAN interrupt could possibly nest.  But I'm definitely seeing it.

One of the things that doesn't seem quite right is that in the standard Micrium configuration, the IRQ index register is not read very early on in the ISR.  There seems to be some housekeeping that needs to occur prior.  This is just something I noticed that may or may not be an issue.  But I would think fishing the index out as soon as possible might mitigate the race condition.  But then again reading the index earlier might just move the same issue to an earlier time. 

Any thoughts or info would be appreciated.  I'll include the priority/masking routine - I added the line " if( ch_ix == 0U ) ... "  to abort the service of the phantom interrupt.  Without this line the code would lock up - the phantom interrupt would persist when re-enabling interrupts before the call to the specific ISR.

void  BSP_IntHandlerSrc (CPU_INT16U  src_id)
{
    CPU_FNCT_PTR   isr_fnct;
    CPU_REG32     *p_vim_tbl;
    CPU_INT32U     ch_ix;
    CPU_INT32U     pend_reg_0;
    CPU_INT32U     pend_reg_1;
    CPU_INT32U     pend_reg_2;
    CPU_INT32U     pend_reg_3;
    CPU_INT32U     pend_mask;
        
    switch (src_id) {
        case OS_CPU_ARM_EXCEPT_IRQ:
             ch_ix     = (CPU_INT32U    )BSP_INT_REG_IRQ_INDEX;
             if( ch_ix == 0U ) { phantomIRQcnt++; break; /*phantomInterrupt();*/ }
             p_vim_tbl = (CPU_REG32    *)BSP_INT_ADDR_INT_TBL;
             isr_fnct  = (CPU_FNCT_PTR  )p_vim_tbl[ch_ix];

             ch_ix--;
             if( (ch_ix == 16u) || (ch_ix == 35u) || (ch_ix == 45u) || (ch_ix == 113u) ) { 
                 // execute ISR without enabling interrupts
                 (*isr_fnct)((void *)ch_ix);  /* Invoke ISR, and pass it the channel as the arg.      */
                 break;
             }

             if (isr_fnct != (CPU_FNCT_PTR)0) {

                 pend_reg_0 = BSP_INT_REG_REQENACLR0;           /* Store interrupt state                                */
                 pend_reg_1 = BSP_INT_REG_REQENACLR1;
                 pend_reg_2 = BSP_INT_REG_REQENACLR2;
                 pend_reg_3 = BSP_INT_REG_REQENACLR3;
                
                 if (ch_ix <= 31) {                             /* Dis. all interrupts of lower priority than current   */
                     pend_mask = 0xFFFFFFFF << ch_ix;
                     BSP_INT_REG_REQENACLR0 = pend_mask;
                     BSP_INT_REG_REQENACLR1 = 0xFFFFFFFF;
                     BSP_INT_REG_REQENACLR2 = 0xFFFFFFFF;
                     BSP_INT_REG_REQENACLR3 = 0xFFFFFFFF;
                 } else if (ch_ix <= 63) {
                     pend_mask = 0xFFFFFFFF << (ch_ix - 32);
                     BSP_INT_REG_REQENACLR1 = pend_mask;
                     BSP_INT_REG_REQENACLR2 = 0xFFFFFFFF;
                     BSP_INT_REG_REQENACLR3 = 0xFFFFFFFF;
                 } else if (ch_ix <= 95) {
                     pend_mask = 0xFFFFFFFF << (ch_ix - 64);
                     BSP_INT_REG_REQENACLR2 = pend_mask;
                     BSP_INT_REG_REQENACLR3 = 0xFFFFFFFF;
                 } else {
                     pend_mask = 0xFFFFFFFF << (ch_ix - 96);
                     BSP_INT_REG_REQENACLR3 = pend_mask;
                 }

                 // execute all other ISRs with interrupts enabled
                 CPU_IntEn();                 /* Enable high-priority interrupts                      */
                 (*isr_fnct)((void *)ch_ix);  /* Invoke ISR, and pass it the channel as the arg.      */
                 CPU_IntDis();                /* Disable interrupts                                   */
                 
                 BSP_INT_REG_REQENASET0 = pend_reg_0;  /* Restore original interrupt state               */
                 BSP_INT_REG_REQENASET1 = pend_reg_1;
                 BSP_INT_REG_REQENASET2 = pend_reg_2;
                 BSP_INT_REG_REQENASET3 = pend_reg_3;
             }
             else { nullIRQcnt++; }
             break;

        case OS_CPU_ARM_EXCEPT_FIQ:                             /* See Note #1.                                         */
        default:
             break;
    }
}

  • Hello Dan,

    I am not familiar with the Micrium. You are right, TMS570 doesn't support nested interrupt in hardware. When an IRQ is serviced, CPSR bit 7 is set, IRQ is disabled.

    This application note may be helpful to you.

    www.ti.com/.../spna219.pdf
  • Thanks for the info - I'll look through the app note. I'm still looking for better info on how the core and VIM interact though. Again, a timing diagram would be ideal. It needs to show the race condition that is hinted at in scenario 2 of spna063. From what I understand, the issue comes when an interrupt from a peripheral comes at the exact moment the instruction executed to mask the interrupt occurs. In this situation, just after the mask, the index register is updated (vector removed) but the interrupt has been passed to the core. When the core acts on the interrupt, the index is no longer valid and the phantom occurs. I'm trying to figure out either 1) how to prevent this condition from happening, or 2) if this condition happens, whether it even causes any problems - it seems like when the mask is eventually removed, the interrupt should still be pending and be re-asserted and handled correctly. Is this true?
  • Hello Dan,

    I don't have the timing diagram you mentioned. The device supports 3 different possibilities for SW to handle the interrupts. Index mode (legacy), register vector, and HW vectored INT (ISR).

    When an event occurs within a peripheral, the peripheral makes an interrupt request to VIM, then VIM prioritizes the requests and provides the address of the highest ISR to CPU. CPU starts executing the ISR instructions from the address in the ISR.

    For example,
    1. Enable GIO INT and GIO pins as input
    2. Press the GIO buttons (GIOB4, GIOB5) on launchpad
    3. VIM will make INT request to CPU (register vector mode) and provide the ISR address to CPU VIC port (HW vectored interrupt mode), and write ISR address to IRQ Index Register
    4. CPSR IRQ bit is cleared
    5. Jump to ISR
    6. read the GIO interrupt offset register --> VIM IRQ Index Register will be cleared to point to phantom ISR
  • I understand the supported interrupt service modes. I understand what is supposed to happen - your example describes a simple expected case fine. However what I'm seeing is a bit more complicated, doesn't match what is expected and involves the mask register. Following your example, everything would be identical up to point 5. Then, what I would expect to happen is,

    5. Jump to ISR
    6. Mask GIO INT in VIM and all other lower priority interrupts
    7. Another peripheral of lower priority has an INT request (but should be held masked and pending)
    8. Interrupts enabled (SPSR IRQ bit set), but pending interrupts are still masked
    9. GIO interrupt offset register read, GIO service clears GIO INT, GIO service completes
    10. Interrupts disabled (SPSR IRQ bit cleared)
    11. Interrupt mask removed in VIM (both GIO mask and other lower priority peripheral mask)
    12. First ISR completes, 'Normal' state popped (interrupts re-enabled)
    13. Pending interrupt from other peripheral becomes active, repeat from step 1 with other peripheral INT
    14. Complete to step 12 above without second interrupt.

    What does happen (observed) is somewhere between 6 and 8 above, somehow, a lower priority interrupt actually gets registered and serviced by the core and the index fetched during this event indicates a phantom interrupt. I have no idea how this could be happening. Thus, I was interested in the edge cases mentioned in spna063. But I guess if there is no more info to be had, I'll have to figure something else out.
  • Hi Dan,

    You manually clear the IRQ bit in SPSR to enable the IRQ interrupt within the GIO IRQ ISR. The Cortex-R4 doesn't support nested interrupt in hardware.
  • Yes. Step 4 in your example happens automatically (CPSR IRQ bit is cleared) but in step 8, 10 and 11, the interrupts are explicitly set/cleared. I understand this processor doesn't support nested interrupts in hardware. This OS is supposed to handle nested interrupts in software. But it's not working very well. I'm trying to understand how both the processor hardware and OS work so I can fix it.
  • Hi Dan,

    This is the only appnote related to the SW solution for the nested interrupt on Hercules MCU. I am sorry for that.

    www.ti.com/.../spna219.pdf
  • Thanks, I think the info you've provided will have to do for now.