Electronic – Problem in Clock Gating Control with ARM Tiva C

armassemblycembeddedmicrocontroller

When I access a peripheral register (such as a GPIO port) just after enabling the clock gating of the peripheral, a hard fault happens. For example:

  • This code doesn't work (generates a hard fault):

    /* enable clock to GPIOF at clock gating control register */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
    /* enable the GPIO pins for the LED (PF3, 2 1) as output */
    GPIO_PORTF_DIR_R = 0x0E;
    while(1) {}
    
  • But with this little nop tweak, it works:

    /* enable clock to GPIOF at clock gating control register */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
    asm(" nop");
    asm(" nop");
    /* enable the GPIO pins for the LED (PF3, 2 1) as output */
    GPIO_PORTF_DIR_R = 0x0E;
    while(1) {}
    

The code should work fine with no delay after enabling the clock, but I need to know what is happening here?

Long Story:

If I run the first code it gives me a bus error. I tried other ports in GPIO and also tried some UART channel and the problem persists. What I got is that: just adding sufficient asm(" nop"); instructions in the right place make the code works.

It appears that the problem is with the delay time between enabling the clock gating of a peripheral and accessing it's registers. Any solution that implements adding a sufficient delay works. I tried different things like using a delay function or using asm(" nop") instructions or just adding a debug breakpoint in the line just after enabling clock. Is this have something to do with clock gating latency or clock skew or such expressions?

I've debugged the code according to this document form TI. My fault register reads a value of 0x0000.0400 after fault which means that "A bus fault occurred, but that the exact faulting address is not known". I've checked the exception stack frame and analyzed the disassembly code that is executed before the fault happens and it seems that there's no problem. The document says that this fault occurs usually due to the STR instruction, but I've checked the memory pointers and it's all fine.

Disassembly of the not-working code:

11            SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
          main():
0000026c:   4913                ldr        r1, [pc, #0x4c]
0000026e:   6808                ldr        r0, [r1]
15            GPIO_PORTF_DIR_R = 0x0E;
00000270:   4A13                ldr        r2, [pc, #0x4c]
11            SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
00000272:   F0400020            orr        r0, r0, #0x20
00000276:   6008                str        r0, [r1]
15            GPIO_PORTF_DIR_R = 0x0E;
00000278:   200E                movs       r0, #0xe
0000027a:   6010                str        r0, [r2]

Disassembly of the working code:

11            SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
          main():
0000026c:   4914                ldr        r1, [pc, #0x50]
0000026e:   6808                ldr        r0, [r1]
00000270:   F0400020            orr        r0, r0, #0x20
00000274:   6008                str        r0, [r1]
00000276:   BF00                nop        
00000278:   BF00                nop        
15            GPIO_PORTF_DIR_R = 0x0E;
0000027a:   4912                ldr        r1, [pc, #0x48]
0000027c:   200E                movs       r0, #0xe
0000027e:   6008                str        r0, [r1]

From my experiments, it seems like after enabling clock for GPIO requires more than 1 clock cycles before accessing the GPIO port registers. For UART it is higher, about 5 to 6 clock cycles (I've measured that with the debugger).

IDE: Code Composer Studio.
Compiler: TI v18.12.2.LTS (with optimization level of 2).
OS: Ubuntu Bionic 18.04.2 LTS.

So, is that delay required? or unfortunately, I have a hardware problem?

Best Answer

As I recall this is a known issue with the Tiva processors: you have to wait two or three clock cycles after enabling the clock before you can actually use the peripheral. Some people would, sadly, add a statement that just reads the value in some volatile register to kill time. Unfortunately, sometimes one statement wasn't enough. The situation lead to some very ugly hacks.

TI eventually added the General-Purpose Input/Output Peripheral Ready (PRGPIO) reister. This register has one bit for each port, and you can read the bit to see when the port is ready to use. Similar registers are available for other peripheral devices, such as the Universal Asynchronous Receiver/Transmitter Peripheral Ready register.

Related Topic