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.

TM4C123GH6PM GPIO Interrupt Flag Clear Latency

Other Parts Discussed in Thread: TM4C123GH6PM

I wrote a simple program to test GPIOD6 interupt.  I hooked up a function generator to trigger the interrupt.  For every pulse from the function generator, the interrupt handler was executed twice.  I finally found the work around by moving the interrupt flag clear statement up to the beginning of the interrupt handler and that fixed the problem.  Attached is the original program and the modified interrupt handler that works at the end.

I am assuming that there is a latency in clearing the interrupt flag or maybe you can help me spot the bugs in my code.

I am using Keil uVision 4.72.10.0.

Original program that produced two interrupt handler with one pulse:

#include "TM4C123GH6PM.h"

int main(void)
{
    SYSCTL->RCGCGPIO |= 0x20;   // enable clock to PORTF
    SYSCTL->RCGCGPIO |= 0x08;   // enable clock to PORTD
   
    // configure PORTF for LED output
    GPIOF->DIR |= 0x0E;         // make PORTF3, 2, 1 output for LEDs
    GPIOF->DEN |= 0x0E;         // make PORTF4-0 digital pins
   
    // configure PORTD6 for falling edge trigger interrupt
    GPIOD->DIR &= ~0x40;        // make PORTD6 input pin
    GPIOD->DEN |= 0x40;         // make PORTD6 digital pin
    GPIOD->IS  &= ~0x40;        // make bit 4, 0 edge sensitive
    GPIOD->IBE &= ~0x40;        // trigger is controlled by IEV
    GPIOD->IEV &= ~0x40;        // falling edge trigger
    GPIOD->ICR |= 0x40;         // clear any prior interrupt
    GPIOD->IM  |= 0x40;         // unmask interrupt
   
    // enable interrupt in NVIC and set priority to 6
    NVIC->IP[3] = 6 << 5;       // set interrupt priority to 6
    NVIC->ISER[0] |= 0x00000008;    // enable IRQ3

    __enable_irq(); // global enable IRQs

    while(1)
    {   // wait for interrupts
    }
}

void GPIOD_Handler(void)
{
    GPIOF->DATA ^= 8;           // toggle green LED
    GPIOD->ICR |= 0x40;         // clear the interrupt flag

By swapping the two statements, the interrupt handler is executed only once for each pulse.

void GPIOD_Handler(void)
{
    GPIOD->ICR |= 0x40;         // clear the interrupt flag
    GPIOF->DATA ^= 8;           // toggle green LED
}

  • Shujen Chen said:
    I am assuming that there is a latency in clearing the interrupt flag

    The TivaWare" Peripheral Driver Library Users's Guide contains the following note for functions which clear the interrupt flag:
    Because there is a write buffer in the Cortex-M processor, it may take several clock cycles before the interrupt source is actually cleared. Therefore, it is recommended that the interrupt source be cleared early in the interrupt handler (as opposed to the very last action) to avoid returning from the interrupt handler before the interrupt source is actually cleared. Failure to do so may result in the interrupt handler being immediately reentered (because the interrupt controller still sees the interrupt source asserted).
    So, yes there is a latency in clearing the interrupt flag.

  • Thanks for the explanation.  I searched the datasheet and found the similar statements:

    After a write to clear an interrupt source, it may take several processor cycles for the

    NVIC to see the interrupt source de-assert. Thus if the interrupt clear is done as the

    last action in an interrupt handler, it is possible for the interrupt handler to complete

    while the NVIC sees the interrupt as still asserted, causing the interrupt handler to be

    re-entered errantly. This situation can be avoided by either clearing the interrupt source

    at the beginning of the interrupt handler or by performing a read or write after the write

    to clear the interrupt source (and flush the write buffer).

  • Hi Shujen.

    And for some reason - if you absolutely must clear the interrupt flag at the end only - you can always take use of the fact that the peripheral memory is bit-banded. To avoid the R-M-W cycle of register modifications, you can write a 0 to the counterpart word of the interrupt flag bit in the bit-banded memory.

    This will save you some cycles spent in the ISR.

    Kindly refer to the bit-banded memory section in the datasheet.

    Regards,

    Shashank 

  • Shashank,

    I know the data registers are bit-banded but I am not aware that the control/status registers are bit-banded.

  • Hi Shujen.

    The GPIOICR register is at an offset of 0x41C from the base address of the ports available on the controller. In case of TM4C123GH6PM, PORTD is at 0x40007000. Also, the memory from 0x40000000 to 0x400FFFFF is bit-banded.

    The bit-banded alias of bit 0 of GPIOICR for PORTD would be: 0x42008380. Similarly for your case (bit 6), the bit-banded alias would be 0x42008380 + 6*4 = 0x42008398.

    Regards,

    Shashank

  • @ Shashank & Shujen,

    Applaud you both for the depth, clarity & effort exhibited here.  Like Shujen - I did not have that awareness of bit-band's ability to impact (certain) control registers.

    One item does concern - appears (on occasion) useful to clear the interrupt flag @ the end of the handler.  And - does not the, "cycle saving w/in the ISR" (as you neatly/uniquely propose) run counter to the MCU manual's advice to, "clear the interrupt early - so that (needed time passes) & the interrupt has indeed been cleared? 

    Overall your approach is clever/inventive - but believe this apparent conflict merits some review...

  • One main reason I cleared the interrupt flag at the end of the interrupt handler is to clear pending interrupt (of the same interrupt source) that was triggered during the execution of the interrupt handler.  But with NVIC queuing up the interrupts, I found clearing interrupt flag does not clear the pending interrupt (of the same interrupt source) in queue.

    On the other subject, it appears that the buffered write to the registers has wider impact than the interrupt flag clear.  I posted a timing problem of I2C that could be the result of the buffered write:

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/297829.aspx

  • Shujen Chen said:
    One main reason I cleared the interrupt flag at the end of the interrupt handler is to clear pending interrupt (of the same interrupt source) that was triggered during the execution of the interrupt handler.

    If after clearing the interrupt flag at the end of the interrupt handler, you then readback the register before returning from the interrupt handler, does the read drain the write buffer and ensure the interrupt flag is cleared before the handler returns?

    E.g. does the following work:

    void GPIOD_Handler(void)
    {
        volatile uint32_t readback;

        GPIOF->DATA ^= 8; // toggle green LED
        GPIOD->ICR |= 0x40; // clear the interrupt flag
        readback = GPIOD->ICR; // Ensure write to interrupt flag has been drained from the write buffer before returning
    }
  • Chester,

    Because the write operation of the RMW cycles of the GPIOD->ICR |= 0x40 instruction will be completed by the time it is time for the load operation that has to be carried out for readback = GPIOD->ICR instruction, I think it should work.

    Shujen,

    Can you verify this by looking at the dis-assembly of the code and keeping in mind that the Cortex M4 has a three stage pipeline?

    Regards,

    Shashank

  • Shujen Chen said:

    One main reason I cleared the interrupt flag at the end of the interrupt handler is to clear pending interrupt (of the same interrupt source) that was triggered during the execution of the interrupt handler.  But with NVIC queuing up the interrupts, I found clearing interrupt flag does not clear the pending interrupt (of the same interrupt source) in queue.

    Shujen,

    For your application, if you do not wish to enter the ISR again even if the same interrupt comes while you are servicing the previous instance of the same, you can clear the pending interrupt bit in the SWTRIG register. This will tell the NVIC that the interrupt is not pending any more. Please note, you will need to consider the access privileges as SWTRIG allows only privileged writes. You can read more about this in the MPU and NVIC section of the datasheet.

    Regards,

    Shashank

  • Thanks for the replies.  Before reading your suggestions, I implemented a read of GPIOMIS register trying to force the write of ICR through.  It seems to work.  I assume any read from a register will flush the write buffer.

    Now I have to try to clear the pending interrupt in NVIC as you suggested.

  • Shujen,

    I have some good news for you. The datasheet says, in the handler mode (while executing ISR or handling execptions, you are in the handler mode...else you are in the thread mode) - the software execution is always privileged.

    You can directly clear the corresponding interrupt pending bit in the SWTRIG register as long as you are doing it from the ISR.

    You can find more info in the "Processor Mode and Privilege Levels for Software Execution" section of the datasheet.

    Regards,

    Shashank