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?