Cuando accedo a un registro periférico (como un puerto GPIO) justo después de habilitar la activación del reloj del periférico, ocurre una falla grave. Por ejemplo:
Este código no funciona (genera una falla grave):
/* 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) {}
Pero con este pequeño nop
ajuste, funciona:
/* 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) {}
El código debería funcionar bien sin demora después de habilitar el reloj, pero necesito saber qué está sucediendo aquí.
Larga historia:
Si ejecuto el primer código, me da un error de bus. Probé otros puertos en GPIO y también probé algún canal UART y el problema persiste. Lo que obtuve es que: solo agregar suficientes asm(" nop");
instrucciones en el lugar correcto hace que el código funcione.
Parece que el problema está en el tiempo de retraso entre habilitar la activación del reloj de un periférico y acceder a sus registros. Cualquier solución que implemente agregar un retraso suficiente funciona. Intenté diferentes cosas, como usar una función de retraso o usar instrucciones asm ("nop") o simplemente agregar un punto de interrupción de depuración en la línea justo después de habilitar el reloj. ¿Tiene esto algo que ver con la latencia de activación del reloj o el sesgo del reloj o expresiones similares?
He depurado el código de acuerdo con este formulario de documento TI. Mi registro de fallas lee un valor de 0x0000.0400
después de la falla, lo que significa que "Ocurrió una falla en el bus, pero no se conoce la dirección exacta de la falla" . Revisé el marco de la pila de excepciones y analicé el código de desensamblaje que se ejecuta antes de que ocurra la falla y parece que no hay problema. El documento dice que esta falla generalmente ocurre debido a la STR
instrucción, pero revisé los punteros de memoria y todo está bien.
Desmontaje del código que no funciona:
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]
Desmontaje del código de trabajo:
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]
Según mis experimentos, parece que después de habilitar el reloj para GPIO se requieren más de 1 ciclo de reloj antes de acceder a los registros del puerto GPIO. Para UART es más alto, alrededor de 5 a 6 ciclos de reloj (lo he medido con el depurador).
IDE: Code Composer Studio. Compilador: TI v18.12.2.LTS (con nivel de optimización de 2). Sistema operativo: Ubuntu Bionic 18.04.2 LTS.
Entonces, ¿es necesaria esa demora? o por desgracia, tengo un problema de hardware?
Como recuerdo, este es un problema conocido con los procesadores Tiva: debe esperar dos o tres ciclos de reloj después de habilitar el reloj antes de poder usar el periférico. Algunas personas, lamentablemente, agregarían una declaración que solo lea el valor en algún registro volátil para matar el tiempo. Desafortunadamente, a veces una declaración no era suficiente. La situación condujo a algunos hacks muy feos.
TI finalmente agregó el receptor Listo para periféricos de entrada/salida de uso general (PRGPIO). Este registro tiene un bit para cada puerto y puede leer el bit para ver cuándo el puerto está listo para usarse. Registros similares están disponibles para otros dispositivos periféricos, como el registro Universal Asynchronous Receiver/Transmitter Peripheral Ready.
Tiva es un Cortex-M4F. Si hay algún tipo de almacenamiento en búfer de escritura involucrado, es posible que la habilitación del reloj aún no llegue al periférico (o surta efecto), mientras que el código intenta acceder al periférico (aún deshabilitado) y obtiene una falla grave.
Comenzaría tratando de agregar una instrucción __DSB();
(o ) entre la activación del reloj y el acceso al periférico.asm("dsb");
asm(" dsb");
? el compilador no reconoce __DSB();
. El asm por alguna razón funciona para GPIO pero no funciona para UART como este código:SYSCTL_RCGCUART_R |= SYSCTL_RCGCUART_R7; asm(" dsb"); UART7_CTL_R = 0x00;
Viejo pedo
Gamal Othman
Viejo pedo