¿Dónde se almacenan las variables constantes en el microcontrolador?

Según el diseño de la memoria del programa C, las variables constantes se almacenan en el segmento de datos inicializados de la RAM. Pero según algunos de los diseños de memoria del microcontrolador, las variables constantes se almacenan en la memoria FLASH. También he visto que el tamaño del archivo BIN de mi código aumenta cuando cambio las variables constantes a definiciones de macro. Ayúdame a aclarar esta ambigüedad. Usé Nuc2240 Cortex M0 .

La RAM en la mayoría de los micros es volátil, por lo que las constantes se almacenan en flash y pueden (o no) copiarse a la RAM durante la inicialización (depende del micro). El "diseño de memoria del programa C" al que hace referencia no es un estándar como tal, gran parte de este tipo de detalles dependen de la implementación. C en sí mismo es solo el lenguaje de alto nivel, el compilador y el enlazador determinarán el uso de la memoria, con algún conocimiento de la plataforma de destino. No veo ninguna "ambigüedad" en esto, a menos que haya entendido mal su preocupación.
Aquí se analizan varios esquemas diferentes en las respuestas (cargue la constante desde flash o RAM, o codifíquela en la instrucción). Puede ver lo que está haciendo su cadena de herramientas para varios casos al ver una lista de desmontaje del programa y seguir las instrucciones del ensamblador. Muchos IDE proporcionan la generación de listados de desensamblaje, o puede usar un programa independiente.

Respuestas (5)

Si estuviera escribiendo ensamblador, decidiría qué ubicaciones se utilizan para contener qué constantes o variables, a través de directivas org o similares.

Cuando está escribiendo C, la cadena de herramientas C que está utilizando almacenará cosas donde ha sido programada. Las variables obviamente tienden a ir en RAM, las constantes pueden ir en RAM o Flash, el programa tiende a ir en Flash.

Todo depende de cómo se haya diseñado la herramienta para ese procesador de destino en particular, de dónde obtiene las instrucciones de dónde colocar las cosas. Es posible que todo esté incluido en el código, en cuyo caso no tiene nada que decir, va a donde los creadores de la cadena de herramientas eligieron enviarlo. Puede tomar directivas de alguna parte (código fuente, línea de comando, archivo de creación, variable ambiental) para que pueda reorganizar cosas.

Lea su documentación específica. No es física, todo depende del capricho del hombre.

No es tarea del compilador sino del generador de código y del enlazador colocar segmentos con constantes en una ubicación dentro de EPROM, FLASH o RAM con batería. Pero las constantes también pueden colocarse en la RAM volátil si se inicializan al inicio del programa mediante un código especial generado por el compilador.
sí, el compilador soy yo siendo perezoso, la cadena de herramientas.

La respuesta a su pregunta es específica de la arquitectura particular que está utilizando. Como ha mencionado ARM, le daré una respuesta basada en esto.

El ARM Cortex es un diseño basado en registros, por lo que básicamente cualquier operando (const o no) debe llegar de alguna manera a uno de los registros internos antes de que pueda ser utilizado por las instrucciones del programa. Esto es fundamental para responder a su pregunta, porque hay un par de formas diferentes en que la arquitectura permite que esto se haga.

Para constantes pequeñas (normalmente menos de 8 o 10 bits), el valor se puede codificar directamente en ciertas instrucciones. Por ejemplo, hay una instrucción ADD #imm que agregará un valor de 12 bits codificado en la palabra de instrucción a un registro. Esto se puede usar para especificar directamente una constante en su programa. Si estaba agregando la misma constante en varios lugares diferentes en su código, y el valor era de 12 bits o menos, entonces el compilador simplemente usaría una instrucción ADD #imm en todos los lugares donde se requería su constante. En otras palabras, la const real se replicará en múltiples ubicaciones a lo largo del código que se almacena en FLASH.

Si la constante es grande, esto no funcionará, por lo que el valor deberá almacenarse en su propia ubicación de memoria FLASH dedicada. El chip tiene otra instrucción, llamada LDR, que usa para mover un valor de una ubicación de memoria a los registros internos donde se puede usar. A esta instrucción no le importa si el valor se almacena físicamente en FLASH o RAM, simplemente extrae el valor de una dirección específica en la memoria y lo coloca en un registro. Tenga en cuenta que, en comparación con la situación con pequeñas constantes, esto requiere un ciclo de instrucción adicional, pero si la constante se usa repetidamente en un bloque de código, el compilador la almacenará en caché colocándola en un registro de repuesto si hay uno disponible.

Sin embargo, ahora nos encontramos con un problema potencial. En la mayoría de los chips Cortex, la memoria FLASH funciona a la misma velocidad o más lentamente que el núcleo de la CPU. Si intentamos ejecutar una instrucción LDR desde una ubicación FLASH, esta operación de lectura de la memoria física debe competir con la lectura de la siguiente instrucción. Esto crea un cuello de botella, lo que hace que la CPU tenga que detenerse hasta que se hayan leído los datos. Peor aún, en muchas implementaciones, FLASH se ejecuta muy lentamente y se usa un caché de canalización para enviar instrucciones a la CPU más rápido. En estos dispositivos, la lectura, por ejemplo, de una tabla de valores constantes puede exponer el cuello de botella de acceso FLASH fundamental que causa una pérdida severa de rendimiento.

Por esta razón, muchos compiladores buscarán copiar cualquier dato constante en ubicaciones de RAM libres si es posible. Esto evitará tales problemas y, dado que normalmente se accede más rápido a la RAM que a FLASH, se pueden evitar muchos cuellos de botella en el rendimiento. Si hay RAM sin usar en su programa de todos modos, entonces prácticamente no hay gastos generales para el compilador para hacer esto.

Por lo tanto, definitivamente hay una lógica en la forma en que el compilador asigna datos constantes a la memoria física, pero afortunadamente, en la mayoría de las situaciones, los diseñadores de chips y compiladores han hecho todo este arduo trabajo por usted, y puede confiar en que está haciendo lo que es mejor para usted. su código particular y nivel de optimización.

El enlace que proporcionó explica lo que sucede en las computadoras normales, que cargan todo (incluido el código) en la RAM, de todos modos (porque esto es lo único disponible, pero hay mucho). No se aplica a la programación integrada, donde también tienes flash disponible. Por lo tanto, si utiliza un script de enlace apropiado para la programación integrada (que debería ser el valor predeterminado para los IDE integrados), los datos constantes en realidad terminan en flash, como debería ser.

Ahora, si usa definiciones de macro, es completamente diferente. El preprocesador reemplaza las macros dentro del código fuente, lo que significa que la constante se volverá a compilar de forma independiente cada vez que la use en su código. Y, si lo usa varias veces, de hecho, el código será más grande. Esto, por supuesto, no debería ser el caso de las constantes enteras simples. Para las constantes de cadena, los buenos compiladores intentarán fusionar varios valores idénticos para reducir este efecto y mantener el tamaño pequeño, pero esto no está garantizado y puede depender de si las cadenas solo se usan dentro de la misma unidad de compilación (archivo fuente .c) o a través de múltiples unidades de compilación. Para estructuras/objetos constantes, los compiladores generalmente no hacen mucha optimización.

Las respuestas serían más relevantes para su caso específico si proporcionara extractos de su código fuente e indicara qué compilador/IDE está utilizando.

la respuesta depende de su definición de "variable constante" y el compilador utilizado.

si de hecho es una "variable constante", la mayoría de los compiladores la almacenarán como en flash.

si se declara a través de una macro, no puede ser una variable constante, es una constante. generalmente se almacena en flash y se copia en ram.

Parece que estás confundiendo constantes con variables . Esto es especialmente importante si está utilizando C y no C++.

En C, lo que llamas una variable constante (como un const int) en realidad se incluirá en la RAM. Así es como el compilador se ocupa de la necesidad de hacer referencia a veces a la dirección de la variable (punteros).

Si se trata de una variable global, el compilador la colocará en la sección de datos inicializados , lo que significa que los valores van a parpadear en la .etextsección y se copian en .sdatala sección (parte de .data) mediante el script de inicio . Si es una variable local, el código de inicio de la función hará esto como cualquier otra variable.

Sin embargo, si usa una macro, la "variable" no obtiene un espacio de direcciones. Eso significa que se carga como inmediato , ya sea por la misma instrucción que está usando (si el valor es lo suficientemente pequeño y la instrucción admite inmediatos) o se carga en un registro justo antes de la instrucción que usa el valor. Esto usará solo espacio flash, suponiendo que esté ejecutando código desde flash.

Por otro lado, si está usando C++, el compilador hace optimizaciones usando el valor directamente desde flash. Esto no se hace en C por diseño. Ver esta respuesta para más información.

Por cierto, así es como el Cortex-M3 que uso hace las cosas en GCC.