Pregunta:
Uso de la pila ATTiny2313 ISR
JohnC
2009-11-10 04:35:04 UTC
view on stackexchange narkive permalink

Estoy usando un ATTiny2313 para actuar como un concentrador en serie. Solo tiene 128bytes de RAM. Creo que me estoy quedando sin RAM durante el 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 forma de detectar el desbordamiento de la pila?

One responder:
#1
+9
Craig Trader
2009-11-10 06:15:52 UTC
view on stackexchange narkive permalink

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

La respuesta de ejecución de interrupciones para todas las interrupciones de 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 del programa para la rutina de manejo de interrupciones real. Durante este período de cuatro ciclos de reloj, el contador de programa se coloca en la pila. El vector es normalmente un salto a la rutina de interrupción y este salto toma tres ciclos de reloj. Si ocurre una interrupción durante la ejecución de una instrucción de ciclo múltiple, esta instrucción se completa antes de que se atienda 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 regreso 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 recupera de la pila, el puntero de la pila se incrementa en dos y se establece el bit I en SREG.

Así 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 manejador de interrupciones bien escrito necesite mucho espacio en la pila.

En cuanto al Stack Pointer 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 de ubicaciones de memoria más altas a ubicaciones de memoria más bajas. Esto implica que un comando Stack PUSH reduce el puntero de pila.

El puntero de pila apunta al área de pila de SRAM de datos donde se encuentran las pilas de interrupción y subrutina. 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 Stack Pointer debe establecerse para que apunte por encima de 0x60. El Stack Pointer se reduce en uno cuando los datos se envían al Stack con la instrucción PUSH, y se reduce en dos cuando la dirección de retorno se envía al Stack con una llamada o interrupción de subrutina. El puntero de la 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 puntero de pila AVR se implementa como dos registros de 8 bits en el espacio de E / S. El número de bits realmente utilizado 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 involucrará a GCC, GNU Binutils y avr-libc. Un vistazo rápido 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 es de 8 bits, int es de 16 bits, largo es de 32 bits, largo es de 64 bits, flotante y doble son de 32 bits (este es el único formato de coma flotante admitido), los punteros son de 16 bits (los punteros de función son direcciones de palabra, para permitir direccionamiento de programas de hasta 128K espacio de memoria). Hay una opción -mint8 (ver Opciones para el compilador de C avr-gcc) para hacer int 8 bits, pero eso no es compatible con avr-libc y viola los estándares C (int debe tener al menos 16 bits). Es posible que se elimine en una versión futura.

  • Registros de llamadas utilizadas (r18-r27, r30-r31): Puede ser asignado por gcc para datos locales. Puede usarlos libremente en subrutinas de ensamblador. Llamar a subrutinas C puede bloquear cualquiera de ellas; el llamante es responsable de guardar y restaurar.

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

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

    r0 - registro temporal, puede ser golpeado por cualquier código C (excepto los manejadores de interrupciones que lo guardan), puede usarse para recordar algo por un mientras está dentro de una parte del código ensamblador

    r1 - se supone que siempre es cero en cualquier código C, se puede usar para recordar algo por un tiempo dentro de una parte del 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 manejadores de interrupciones guardan y borran r1 al entrar, y restauran r1 al salir (en caso de que no sea 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 el carácter, tienen un registro libre encima de ellos). Esto permite hacer un mejor uso de la instrucción movw en el núcleo mejorado.

Si hay demasiados, los que no encajan 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 / con signo extendido 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 todos 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 superposición de variables y memoria RAM? 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 un 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 la pila (por defecto) para apuntar a la última dirección disponible en la 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 cual, por ejemplo, también puede ocurrir dentro de printf (), el montón de memoria dinámica también se encuentra allí. Consulte Áreas de memoria y Uso de malloc ()).

La cantidad de pila requerido para su aplicación no se puede determinar tan fácilmente. Por ejemplo, si llama de forma recursiva a una función y se olvida de romper esa recursividad, la cantidad de pila requerida es infinita. :-) Puede ver 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 donde sabe que las llamadas podrían estar anidadas.

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


Esta pregunta y respuesta fue traducida automáticamente del idioma inglés.El contenido original está disponible en stackexchange, a quien agradecemos la licencia cc by-sa 2.0 bajo la que se distribuye.
Loading...