Bloqueo al ejecutar ISR reubicados en ARM Cortex-M0 +

1

He estado escribiendo un pequeño programa para mi Freescale Kinetis KE04Z que se ejecuta en la memoria RAM para programar el flash del dispositivo. He estado usando SWD para colocar el programa en RAM y ejecutarlo. Todo iba bien (parpadeando los LED como prueba, etc.) hasta que intenté iniciar el temporizador de interrupción periódica (módulo PIT) para confirmar la velocidad del microcontrolador al destellar un LED con cierta sincronización específica (el manual del controlador de flash dice que si la velocidad es incorrecta entonces podría destruir el flash). No tengo un osciloscopio disponible en este momento, de lo contrario simplemente descartaría el PIT, escribiría el registro de activación de pin de gpio (PTOR) una y otra vez, y mediría el período de la onda cuadrada resultante.

Tengo en mi sección de inicio el siguiente ensamblaje que mueve el VTOR a mi tabla de vectores de interrupción:

ldr r0, =0xE000ED08
ldr r1, =__interrupt_vector_table
str r1,[r0]

__interrupt_vector_table apunta al comienzo de la sección de RAM donde he colocado mi tabla de vectores de interrupción (0x1fffff00 para el registro). El código se puede encontrar aquí . La lista de códigos de inicio y la tabla de interrupciones es aquí .

En mi método principal tengo lo siguiente:

SIM->SCGC |= SIM_SCGC_PIT_MASK; //turn on PIT clock
PIT->MCR = 0; //activate PIT
PIT->CHANNEL[0].LDVAL = 1.2e6; //100mS tick
PIT->CHANNEL[0].TCTRL = PIT_TCTRL_TEN_MASK; //enable the timer
PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TIE_MASK; //enable the interrupt
NVIC->ISER[0] = 1 << 22; //enable interrupt in NVIC
__enable_irq(); //cpsie i

Fue en este punto donde comencé a tener problemas. Una vez que el PIT se activara, se produciría un bloqueo de acuerdo con el registro SIM_SRSID cuando lo leí a través de SWD. Tengo un par de pensamientos sobre por qué esto podría suceder:

  • El ISR se ejecuta, pero nunca regresa, lo que hace que el WDT se apague y se inicie un reinicio, lo que hace que el VTOR se reinicie a 0x0. Dado que el dispositivo no está programado, el vector de reinicio es 0xffffffff, lo que provoca un bloqueo.
  • El ISR ejecuta, rompe los registros y evita que el método principal reinicie correctamente el WDT, causando la misma situación que la anterior.
  • La interrupción se desactiva, pero por alguna razón, el NVIC no puede ejecutar mi ISR e inicia un HardFault. Mi código de error solo se bloquea para siempre, lo que eventualmente causará que el WDT se apague y así sucesivamente.
  • Mi tabla de interrupciones escrita a mano está desactivada por una y una de las ISR predeterminadas se está ejecutando y causando un tiempo de espera de WDT (o no estoy habilitando la ISR correcta y está ocurriendo lo mismo). La cuestión es que nunca he podido mostrar que mi código ISR predeterminado se estaba ejecutando en lugar de mi controlador IRQ real. He realizado algunas pruebas utilizando las ubicaciones de memoria de escritura para valores específicos y luego las leo después del restablecimiento y el código para establecer esos valores nunca se ha ejecutado.

Estoy bastante seguro de que tiene que ver con mi ISR o la interrupción en general porque una vez que comento la línea de código que establece PIT_TCTRL0.TIE, el programa funciona bien y no se bloquea.

Mi ISR es muy simple en este momento:

void PIT_CH0_IRQHandler(void)
{
    PIT->CHANNEL[0].TFLG = 0x1; //reset flags
}

Que compila a:

2000001c <PIT_CH0_IRQHandler>:
2000001c: 2201       movs r2, #1
2000001e: 4b01       ldr r3, [pc, #4] ; (20000024 <PIT_CH0_IRQHandler+0x8>)
20000020: 611a       str r2, [r3, #16]
20000022: 4770       bx lr
20000024: 400370fc strdmi r7, [r3], -ip

Sinceramente, no sé si esto es correcto. No hay ahorro de contexto o cualquier otra cosa que haya visto en otros ISR, pero nunca he visto en detalle los ISR ARM Cortext M0 +, por lo que sé, el NVIC se encarga de guardar el contexto.

Mi pregunta es:

  • ¿El código ISR, el código NVIC o el código de reubicación de VTOR es el problema aquí?
  • ¿O tal vez la ubicación del VTOR en una ubicación en la RAM sea algo malo y no funciona correctamente?

Esta es la primera vez que escribo un programa que se ejecuta en RAM y también la primera vez que reubico el VTOR, por lo que no tengo mucha experiencia en estos asuntos. He hecho un montón de código de interrupción para un K20 antes, pero el VTOR siempre estaba en 0x0. Si he omitido algo o necesito proporcionar información adicional, simplemente avíseme.

    
pregunta Los Frijoles

2 respuestas

3

Solo quería publicar un seguimiento ya que solucioné el problema. No coincide con ninguna respuesta existente, pero eso se debe a que proviene de una fuente inesperada.

Estaba pensando en cómo exactamente NVIC almacena el contexto, ya que parece ser el momento que hace que las cosas se vuelvan locas y me di cuenta de que probablemente usa la pila.

Ejecuto la siguiente secuencia en SWD al cargar mi programa (probablemente no sea la mejor manera de hacerlo, pero es un trabajo en progreso):

  1. Habilitar depuración en DHCSR
  2. Habilitar la captura de reinicio en DEMCR
  3. Borra cualquier marca de reinicio en DHCSR
  4. Use AIRCR para solicitar un restablecimiento y espere a que ocurra al sondear DHCSR . El procesador ahora se detiene justo después de que se produce un reinicio. El VTOR ahora es 0x0 , el registro 15 ahora apunta a 0xffffffff ya que el flash (ubicado en 0x0 ) no está programado), ... y el puntero de pila también es 0xffffffff (no se dio cuenta de esto hasta ahora )
  5. Utilice el AHB-AP para cargar el programa
  6. Configure el VTOR para que apunte al nuevo VTOR en mi programa. Establecer el registro 15 al inicio de la rutina de restablecimiento en mi código (cuya dirección se encuentra en VTOR + 4).
  7. Apague el procesador y déjelo funcionar.

El procesador se ejecutaría hasta la primera interrupción. Me di cuenta de que, dado que había descuidado la configuración del puntero de la pila durante el paso 6 y seguía apuntando a algún lugar fuera de la RAM, el NVIC probablemente no tenía dónde guardar el contexto y probablemente causó una falla. Esto finalmente permitiría que el WDT se apague, ya que solo se interrumpirá sin fin en esas rutinas, lo que causaría que el VTOR se restablezca de nuevo a 0x0 , y luego intente ejecutar 0xffffffff (ya que eso es lo que está en la dirección 0x4 ) que resultaría en un LOCKUP .

Simplemente colocando el puntero de pila (registro 14) en la parte superior de mi pila durante el paso 6 de arriba, solucioné mi problema.

    
respondido por el Los Frijoles
1

Esta no es una respuesta completa y no soy un experto en ensamblaje, pero he comprobado algunos puntos (y puedo ayudarlo):

El diseño de tu tabla vectorial está bien. El posicionamiento de su tabla de vectores reubicados también está bien (un múltiplo de 256). El código que usa para reubicar la tabla vectorial en el registro VTOR también parece correcto, es la dirección correcta que usa.

El núcleo de Cortex-M realiza el guardado de contexto (empujando cosas en la pila) en la entrada de interrupción automáticamente, por lo que no habrá código para eso en la versión compilada.

En el manual de referencia ( página 119 ), en realidad digamos que reubicar la tabla de vectores en RAM está bien:

  

El núcleo CM0 + agrega soporte para un registro de compensación de tabla vectorial programable (VTOR   )   para reubicar la tabla de vector de excepción. Este dispositivo es compatible con el arranque desde flash interno   y RAM.

     

Este dispositivo admite el arranque desde una memoria flash interna con los vectores de restablecimiento ubicados en   direcciones 0x0 (SP_main inicial), 0x4 (PC inicial) y RAM con la reubicación de la excepción   tabla de vectores a la memoria RAM.

Tampoco estoy al tanto de ninguna trampa o procedimiento especial necesario para hacer la reubicación.

He comprobado la descripción del periférico y la asignación de NVIC y su código ISR. Lo único que puedo encontrar que haces diferente a lo descrito es:

PIT->CHANNEL[0].TCTRL = PIT_TCTRL_TEN_MASK; //enable the timer
PIT->CHANNEL[0].TCTRL |= PIT_TCTRL_TIE_MASK; //enable the interrupt

Lo hacen al revés, primero habilita la interrupción y luego habilita el temporizador. También recomiendan borrar el indicador TIF antes de habilitar el temporizador.

La única otra cosa que puedo encontrar que probablemente no esté causando un problema ahora es que tiene una conversión implícita de doble a un entero de 32 bits sin signo en curso, lo que no es el más limpio (puede dar un comportamiento extraño en algunas ocasiones):

PIT->CHANNEL[0].LDVAL = 1.2e6; //100mS tick

Yo diría que 1200000U no se puede leer mejor pero es seguro que realmente obtenga el valor que desea en su registro.

¿Puede deshabilitar el WDT y detener la ejecución de su controlador bloqueado para ver dónde termina? Ese es el enfoque que utilizo para averiguar qué está pasando, también la pila puede ser muy útil para ver de dónde viene en esa situación.

    
respondido por el Arsenal

Lea otras preguntas en las etiquetas