Lo que sucede en un procesador incorporado cuando la ejecución alcanza esa return
declaración final ¿Todo se congela tal como está? consumo de energía, etc., con un NOP eterno en el cielo? ¿o los NOP se ejecutan continuamente, o un procesador se apagará por completo?
Parte de la razón por la que pregunto es porque me pregunto si un procesador necesita apagarse antes de finalizar la ejecución y, si lo hace, ¿cómo termina alguna vez la ejecución si se ha apagado antes?
Respuestas:
Esta es una pregunta que mi padre siempre me hacía. " ¿Por qué no solo ejecuta todas las instrucciones y se detiene al final? "
Echemos un vistazo a un ejemplo patológico. El siguiente código fue compilado en el compilador C18 de Microchip para el PIC18:
Produce la siguiente salida del ensamblador:
Como puede ver, se llama a main (), y al final contiene una declaración de devolución, aunque no lo pusimos explícitamente allí. Cuando main regresa, la CPU ejecuta la siguiente instrucción, que es simplemente un GOTO para volver al comienzo del código. main () simplemente se llama una y otra vez.
Ahora, habiendo dicho esto, esta no es la forma en que la gente haría las cosas por lo general. Nunca he escrito ningún código incrustado que permita que main () salga así. Principalmente, mi código se vería así:
Por lo tanto, normalmente nunca dejaría que salga main ().
"OK ok" estás diciendo. Todo esto es muy interesante porque el compilador se asegura de que nunca haya una última declaración de devolución. Pero, ¿qué sucede si forzamos el problema? ¿Qué pasa si codifico mi ensamblador a mano y no vuelvo al principio?
Bueno, obviamente la CPU seguiría ejecutando las siguientes instrucciones. Esos se verían así:
La siguiente dirección de memoria después de la última instrucción en main () está vacía. En un microcontrolador con memoria FLASH, una instrucción vacía contiene el valor 0xFFFF. Al menos en un PIC, ese código operativo se interpreta como 'nop' o 'no operación'. Simplemente no hace nada. La CPU continuaría ejecutando esos nops hasta el final de la memoria.
¿Qué hay después de eso?
En la última instrucción, el puntero de instrucciones de la CPU es 0x7FFe. Cuando la CPU agrega 2 a su puntero de instrucción, obtiene 0x8000, lo que se considera un desbordamiento en un PIC con solo 32k FLASH, por lo que vuelve a 0x0000, y la CPU continúa felizmente ejecutando instrucciones al comienzo del código , como si se hubiera reiniciado.
También preguntaste sobre la necesidad de apagar. Básicamente puedes hacer lo que quieras, y depende de tu aplicación.
Si tuvieras una aplicación que solo necesitara hacer una cosa después del encendido, y luego no hacer nada más, solo podrías poner un momento (1); al final de main () para que la CPU deje de hacer algo notable.
Si la aplicación requería que la CPU se apagara, entonces, dependiendo de la CPU, probablemente habrá varios modos de suspensión disponibles. Sin embargo, las CPU tienen la costumbre de volver a despertarse, por lo que debe asegurarse de que no haya un límite de tiempo para el sueño, y que no haya un temporizador Watch Dog activo, etc.
Incluso podría organizar algunos circuitos externos que le permitirían a la CPU cortar completamente su propia energía cuando hubiera terminado. Vea esta pregunta: Usar un botón momentáneo como interruptor de palanca de encendido y apagado .
fuente
Para el código compilado, depende del compilador. El compilador ARM gcc Rowley CrossWorks que utilizo salta para codificar en el archivo crt0.s que tiene un bucle infinito. El compilador Microchip C30 para los dispositivos dsPIC y PIC24 de 16 bits (también basado en gcc) reinicia el procesador.
Por supuesto, la mayoría del software embebido nunca termina así, y ejecuta código continuamente en un bucle.
fuente
Hay dos puntos a destacar aquí:
El concepto de apagado de un programa normalmente no existe en un entorno integrado. A un nivel bajo, una CPU ejecutará instrucciones mientras pueda; no existe tal cosa como una "declaración de devolución final". Una CPU puede detener la ejecución si encuentra una falla irrecuperable o si se detiene explícitamente (ponerla en modo de suspensión, un modo de baja potencia, etc.), pero tenga en cuenta que incluso los modos de suspensión o fallas irrecuperables generalmente no garantizan que no vaya a haber más código ser ejecutado. Puede despertar desde los modos de suspensión (así es como se usan normalmente), e incluso una CPU bloqueada puede ejecutar un controlador NMI (este es el caso de Cortex-M). También se ejecutará un watchdog, y es posible que no pueda deshabilitarlo en algunos microcontroladores una vez que esté habilitado. Los detalles varían mucho entre arquitecturas.
En el caso de firmware escrito en un lenguaje como C o C ++, lo que sucede si main () sale está determinado por el código de inicio. Por ejemplo, aquí está la parte relevante del código de inicio de la Biblioteca Periférica Estándar STM32 (para una cadena de herramientas GNU, los comentarios son míos):
Este código ingresará en un bucle infinito cuando main () regrese, aunque de una manera no obvia (se
bl main
cargalr
con la dirección de la siguiente instrucción que es efectivamente un salto en sí mismo). No se hacen intentos para detener la CPU o hacer que entre en un modo de bajo consumo, etc. Si tiene una necesidad legítima de algo de eso en su aplicación, deberá hacerlo usted mismo.Tenga en cuenta que, como se especifica en el ARMv7-M ARM A2.3.1, el registro de enlace se establece en 0xFFFFFFFF en el reinicio, y una bifurcación a esa dirección provocará un error. Entonces, los diseñadores de Cortex-M decidieron tratar el retorno del controlador de reinicio como anormal, y es difícil discutir con ellos.
Hablando de una necesidad legítima de detener la CPU después de que el firmware haya finalizado, es difícil imaginar algo que no sea mejor atendido por un apagado de su dispositivo. (Si deshabilita su CPU "para siempre", lo único que se puede hacer con su dispositivo es un ciclo de energía o un reinicio externo del hardware). Puede desactivar una señal ENABLE para su convertidor DC / DC o apagar su fuente de alimentación en de otra manera, como lo hace una PC ATX.
fuente
loop: b loop
. Me pregunto si realmente querían hacer una devolución, pero se olvidaron de ahorrarlr
.Al preguntar sobre
return
, estás pensando en un nivel demasiado alto. El código C se traduce en código de máquina. Por lo tanto, si piensa en el procesador que extrae ciegamente las instrucciones de la memoria y las ejecuta, no tiene idea de cuál es la "final"return
. Entonces, los procesadores no tienen un fin inherente, sino que depende del programador manejar el caso final. Como Leon señala en su respuesta, los compiladores han programado un comportamiento predeterminado, pero a menudo el programador puede querer su propia secuencia de apagado (he hecho varias cosas como ingresar a un modo de baja potencia y detener, o esperar a que se conecte un cable USB) en y luego reiniciar).Muchos microprocesadores tienen instrucciones de detención, que detienen el procesador sin afectar los periféricos. Otros procesadores pueden confiar en "detenerse" simplemente saltando a la misma dirección repetidamente. Hay muchas opciones, pero depende del programador porque el procesador simplemente seguirá leyendo las instrucciones de la memoria, incluso si esa memoria no tenía la intención de ser instrucciones.
fuente
El problema no es incrustado (un sistema embebido puede ejecutar Linux o incluso Windows), sino autónomo o completamente desnudo: el programa de aplicación (compilado) es lo único que se ejecuta en la computadora (no importa si es un microcontrolador o microprocesador).
Para la mayoría de los idiomas, el idioma no define lo que sucede cuando termina el 'principal' y no hay ningún sistema operativo al que volver. Para C, depende de lo que esté en el archivo de inicio (a menudo crt0.s). En la mayoría de los casos, el usuario puede (o incluso debe) proporcionar el código de inicio, por lo que la respuesta final es: lo que escriba es el código de inicio, o lo que esté en el código de inicio que especifique.
En la práctica hay 3 enfoques:
No tome medidas especiales. qué sucede cuando el rendimiento principal no está definido.
salte a 0 o use cualquier otro medio para reiniciar la aplicación.
ingrese un bucle cerrado (o deshabilite las interrupciones y ejecute una instrucción de detención), bloqueando el procesador para siempre.
Lo apropiado depende de la aplicación. Una tarjeta de felicitación fur-elise y un sistema de control de frenos (solo por mencionar dos sistemas integrados) probablemente deberían reiniciarse. La desventaja de reiniciar es que el problema puede pasar desapercibido.
fuente
Estaba mirando un código ATtiny45 desmontado (C ++ compilado por avr-gcc) el otro día y lo que hace al final del código es saltar a 0x0000. Básicamente haciendo un reinicio / reinicio.
Si el compilador / ensamblador omite el último salto a 0x0000, todos los bytes en la memoria del programa se interpretan como código de máquina 'válido' y se ejecuta hasta que el contador del programa pase a 0x0000.
En AVR, un byte de 00 (valor predeterminado cuando una celda está vacía) es un NOP = Sin operación. Por lo tanto, funciona muy rápido, sin hacer nada más que tomarse un tiempo.
fuente
Generalmente, el
main
código compilado se vincula luego con el código de inicio (puede integrarse en la cadena de herramientas, proporcionado por el proveedor del chip, escrito por usted, etc.).Luego, Linker coloca todas las aplicaciones y el código de inicio en segmentos de memoria, por lo que la respuesta a sus preguntas depende de: 1. el código de inicio, porque puede, por ejemplo:
bl lr
ob .
), que será similar al "fin del programa", pero las interrupciones y los periféricos habilitados anteriormente seguirán funcionando,main
).simplemente ignore "lo que será el próximo" después de la llamada a
main
devoluciones.main
comportamiento dependerá de su enlazador (y / o script de enlazador usado durante el enlace).Si se coloca otra función / código después de su
main
, se ejecutará con valores de argumento no válidos / indefinidos,Si watchdog está habilitado, eventualmente restablecerá MCU a pesar de todos los bucles interminables en los que se encuentra (por supuesto, si no se volverá a cargar).
fuente
La mejor manera de detener un dispositivo integrado es esperar para siempre con las instrucciones de NOP.
La segunda forma es cerrar el dispositivo utilizando el dispositivo mismo. Si puede controlar un relé con sus instrucciones, simplemente puede abrir el interruptor que está alimentando su dispositivo incorporado y no habrá consumo de energía.
fuente
Fue explicado claramente en el manual. Normalmente, la CPU generará una excepción general porque accederá a una ubicación de memoria que está fuera del segmento de la pila. [excepción de protección de memoria].
¿Qué quisiste decir con el sistema embebido? ¿Microprocesador o microcontrolador? De cualquier manera, está definido en el manual.
En la CPU x86 apagamos la computadora enviando el comando al controlador ACIP. Entrar en el modo de gestión del sistema. De modo que ese controlador es un chip de E / S y no necesita apagarlo manualmente.
Lea la especificación ACPI para más información.
fuente