Uso de la pila ATTiny2313 ISR

Estoy usando un ATTiny2313 para actuar como concentrador en serie. Solo tiene 128 bytes de RAM. Creo que me estoy quedando sin RAM durante la ISR. Mi pregunta es cuánta RAM (pila) usa un ISR para guardar el contexto (registros). Es decir, si uso ISR, ¿cuánto me quedará de 128 bytes? ¿Hay alguna manera de detectar el desbordamiento de pila?

Respuestas (1)

Bueno, revisando la documentación de ATTiny2313 , en la página 15, dice:

La respuesta de ejecución de interrupción para todas las interrupciones AVR habilitadas es de cuatro ciclos de reloj como mínimo. Después de cuatro ciclos de reloj, se ejecuta la dirección del vector de programa para la rutina real de manejo de interrupciones. Durante este período de cuatro ciclos de reloj, el Contador de programa se coloca en la pila. El vector normalmente es un salto a la rutina de interrupción, y este salto toma tres ciclos de reloj. Si se produce una interrupción durante la ejecución de una instrucción de varios ciclos, esta instrucción se completa antes de que se cumpla la interrupción. Si se produce una interrupción cuando la MCU está en modo de suspensión, el tiempo de respuesta de ejecución de la interrupción aumenta en cuatro ciclos de reloj. Este aumento se suma al tiempo de inicio del modo de suspensión seleccionado.

Un retorno de una rutina de manejo de interrupciones toma cuatro ciclos de reloj. Durante estos cuatro ciclos de reloj, el Contador de programa (dos bytes) se extrae de la pila, el puntero de la pila se incrementa en dos y se establece el bit I en SREG.

Por lo tanto, en realidad solo está mirando 2 bytes en la pila durante una interrupción (la PC); cualquier otra cosa que un ISR ponga en la pila depende del propio ISR. No esperaría que un controlador de interrupciones bien escrito necesite mucho espacio de pila.

En cuanto al puntero de pila en sí, en la página 13, dice:

La pila se utiliza principalmente para almacenar datos temporales, para almacenar variables locales y para almacenar direcciones de retorno después de interrupciones y llamadas a subrutinas. El registro de puntero de pila siempre apunta a la parte superior de la pila. Tenga en cuenta que la pila se implementa a medida que crece desde ubicaciones de memoria más altas a ubicaciones de memoria más bajas. Esto implica que un comando Stack PUSH disminuye el Stack Pointer.

El puntero de pila apunta al área de pila de SRAM de datos donde se encuentran las pilas de subrutinas e interrupciones. Este espacio de pila en la SRAM de datos debe ser definido por el programa antes de que se ejecuten las llamadas a subrutinas o se habiliten las interrupciones. El puntero de pila debe configurarse para que apunte por encima de 0x60. El puntero de pila se reduce en uno cuando los datos se insertan en la pila con la instrucción PUSH, y se reduce en dos cuando la dirección de retorno se inserta en la pila con una llamada de subrutina o una interrupción. El puntero de pila se incrementa en uno cuando los datos se extraen de la pila con la instrucción POP, y se incrementa en dos cuando los datos se extraen de la pila con el retorno de la subrutina RET o el retorno de la interrupción RETI.

El AVR Stack Pointer se implementa como dos registros de 8 bits en el espacio de E/S. El número de bits realmente utilizados depende de la implementación. Tenga en cuenta que el espacio de datos en algunas implementaciones de la arquitectura AVR es tan pequeño que solo se necesita SPL. En este caso, el Registro SPH no estará presente.

En su caso, creo que solo hay SPL presente (128 bytes de RAM = 7 bits).

Más allá del hardware, depende de su marco, que para la mayoría de las partes de AVR incluirá GCC, GNU Binutils y avr-libc . Una mirada rápida a las preguntas frecuentes de avr-libc arrojó dos buenas preguntas:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage

¿Qué registros utiliza el compilador de C?

  • Tipos de datos: char tiene 8 bits, int tiene 16 bits, long tiene 32 bits, long long tiene 64 bits, float y double tienen 32 bits (este es el único formato de punto flotante compatible), los punteros tienen 16 bits (los punteros de función son palabras direcciones, para permitir el direccionamiento de hasta 128K de espacio de memoria de programa). Hay una opción -mint8 (consulte Opciones para el compilador de C avr-gcc) para hacer int de 8 bits, pero avr-libc no lo admite y viola los estándares de C (int debe tener al menos 16 bits). Es posible que se elimine en una versión futura.

  • Registros de uso de llamadas (r18-r27, r30-r31): pueden ser asignados por gcc para datos locales. Puede usarlos libremente en las subrutinas del ensamblador. Llamar a las subrutinas C puede destruir cualquiera de ellas: la persona que llama es responsable de guardar y restaurar.

  • Registros de llamadas guardadas (r2-r17, r28-r29): pueden ser asignados por gcc para datos locales. Llamar a las subrutinas C las deja sin cambios. Las subrutinas del ensamblador son responsables de guardar y restaurar estos registros, si se modifican. r29:r28 (puntero Y) se utiliza como puntero de marco (apunta a datos locales en la pila) si es necesario. El requisito de que el receptor de la llamada guarde/conserve el contenido de estos registros incluso se aplica en situaciones en las que el compilador los asigna para el paso de argumentos.

  • Registros fijos (r0, r1): nunca asignados por gcc para datos locales, pero a menudo se usan para propósitos fijos:

    r0: registro temporal, puede ser golpeado por cualquier código C (excepto los controladores de interrupción que lo guardan), puede usarse para recordar algo por un tiempo dentro de una pieza de código ensamblador

    r1: se supone que siempre es cero en cualquier código C, puede usarse para recordar algo durante un tiempo dentro de una pieza de código ensamblador, pero luego debe borrarse después de su uso (clr r1). Esto incluye cualquier uso de las instrucciones [f]mul[s[u]], que devuelven su resultado en r1:r0. Los controladores de interrupción guardan y borran r1 al entrar y restauran r1 al salir (en caso de que no fuera cero).

  • Convenciones de llamadas a funciones: Argumentos: asignados de izquierda a derecha, r25 a r8. Todos los argumentos están alineados para comenzar en registros pares (los argumentos de tamaño impar, incluido char, tienen un registro libre encima de ellos). Esto permite hacer un mejor uso de la instrucción movw en el núcleo mejorado.

Si son demasiados, los que no caben se pasan a la pila.

Valores de retorno: 8 bits en r24 (¡no r25!), 16 bits en r25:r24, hasta 32 bits en r22-r25, hasta 64 bits en r18-r25. Los valores de retorno de 8 bits son cero/extensión de signo a 16 bits por la función llamada (el carácter sin signo es más eficiente que el carácter con signo, solo clr r25). Los argumentos de las funciones con listas de argumentos variables (printf, etc.) se pasan a la pila y char se extiende a int.

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ramoverlap

¿Cómo detectar problemas de memoria RAM y superposición de variables? Simplemente puede ejecutar avr-nm en su archivo de salida (ELF). Ejecútelo con la opción -n y ordenará los símbolos numéricamente (por defecto, están ordenados alfabéticamente).

Busque el símbolo _end, esa es la primera dirección en la RAM que no está asignada por una variable. (avr-gcc agrega internamente 0x800000 a todas las direcciones de variables de datos/bss, así que ignore este desplazamiento). Luego, el código de inicialización en tiempo de ejecución inicializa el puntero de pila (de forma predeterminada) para apuntar a la última dirección disponible en SRAM (interna) . Por lo tanto, la región entre _end y el final de SRAM es lo que está disponible para la pila. (Si su aplicación usa malloc(), lo que, por ejemplo, también puede ocurrir dentro de printf(), el montón para la memoria dinámica también se encuentra allí. Consulte Áreas de memoria y Uso de malloc().)

La cantidad de pila requerida para su aplicación no se puede determinar tan fácilmente. Por ejemplo, si llama recursivamente a una función y olvida romper esa recursividad, la cantidad de pila requerida es infinita. :-) Puede mirar el código ensamblador generado (avr-gcc ... -S), hay un comentario en cada archivo ensamblador generado que le indica el tamaño del marco para cada función generada. Esa es la cantidad de pila requerida para esta función, debe sumar eso para todas las funciones en las que sabe que las llamadas podrían anidarse.

Sí, pero el compilador avr gcc programará un registro para guardar/restaurar en el ISR, ¿no?
Tal vez, vea lo que agregué sobre el registro, la pila y el uso de RAM. Si está realmente preocupado, le sugiero que genere la fuente de ensamblaje (gcc -S foo.c) y la examine en detalle.