Saltar al contenido

¿Qué reside en los diferentes tipos de memoria de un microcontrolador?

Luego de tanto luchar ya hallamos el arreglo de este atascamiento que muchos lectores de nuestro sitio han presentado. Si deseas aportar algún dato puedes aportar tu comentario.

Solución:

.texto

El segmento .text contiene el código real y está programado en la memoria Flash para microcontroladores. Puede haber más de un segmento de texto cuando hay varios bloques no contiguos de memoria Flash; por ejemplo, un vector de inicio y vectores de interrupción ubicados en la parte superior de la memoria, y un código que comienza en 0; o secciones separadas para un programa de arranque y principal.

.bss y .data

Hay tres tipos de datos que pueden asignarse de forma externa a una función o procedimiento; el primero son datos no inicializados (históricamente denominados .bss, que también incluyen los datos inicializados 0), y el segundo son datos inicializados (no bss) o .data. El nombre “bss” proviene históricamente de “Bloque iniciado por símbolo”, utilizado en un ensamblador hace unos 60 años. Ambas áreas de estas áreas están ubicadas en RAM.

A medida que se compila un programa, las variables se asignarán a una de estas dos áreas generales. Durante la etapa de vinculación, todos los elementos de datos se recopilarán juntos. Todas las variables que necesitan inicializarse tendrán una parte de la memoria del programa reservada para contener los valores iniciales, y justo antes de que se llame a main (), las variables se inicializarán, normalmente mediante un módulo llamado crt0. La sección bss se inicializa a todos ceros mediante el mismo código de inicio.

Con algunos microcontroladores, hay instrucciones más cortas que permiten el acceso a la primera página (las primeras 256 ubicaciones, en ocasiones llamadas página 0) de la RAM. El compilador de estos procesadores puede reservar una palabra clave como near para designar las variables que se colocarán allí. De manera similar, también hay microcontroladores que solo pueden hacer referencia a ciertas áreas a través de un registro de puntero (que requiere instrucciones adicionales), y tales variables se designan far. Finalmente, algunos procesadores pueden abordar una sección de la memoria bit a bit y el compilador tendrá una forma de especificar eso (como la palabra clave bit).

Por lo tanto, puede haber segmentos adicionales como .nearbss y .neardata, etc., donde se recopilan estas variables.

.rodata

El tercer tipo de datos externos a una función o procedimiento es como las variables inicializadas, excepto que es de solo lectura y no puede ser modificado por el programa. En el lenguaje C, estas variables se denotan mediante la const palabra clave. Por lo general, se almacenan como parte de la memoria flash del programa. A veces, se identifican como parte de un segmento .rodata (datos de solo lectura). En los microcontroladores que utilizan la arquitectura de Harvard, el compilador debe utilizar instrucciones especiales para acceder a estas variables.

apilar y amontonar

La pila y el montón se colocan en la RAM. Dependiendo de la arquitectura del procesador, la pila puede crecer o disminuir. Si crece, se colocará en la parte inferior de la RAM. Si crece hacia abajo, se colocará al final de la RAM. El montón utilizará la RAM restante no asignada a variables y crecerá en la dirección opuesta de la pila. El tamaño máximo de la pila y el montón generalmente se puede especificar como parámetros del vinculador.

Las variables colocadas en la pila son cualquier variable definida dentro de una función o procedimiento sin la palabra clave static. Alguna vez fueron llamadas variables automáticas (auto palabra clave), pero esa palabra clave no es necesaria. Históricamente, auto existe porque era parte de la lengua B que precedió a C, y allí se necesitaba. Los parámetros de función también se colocan en la pila.

Aquí hay un diseño típico para RAM (asumiendo que no hay una sección especial de la página 0):

ingrese la descripción de la imagen aquí

EEPROM, ROM y NVRAM

Antes de que apareciera la memoria Flash, se usaba EEPROM (memoria de solo lectura programable y borrable eléctricamente) para almacenar el programa y los datos constantes (segmentos .text y .rodata). Ahora hay solo una pequeña cantidad (por ejemplo, 2KB a 8KB bytes) de EEPROM disponible, si es que hay alguna, y generalmente se usa para almacenar datos de configuración u otras pequeñas cantidades de datos que deben retenerse durante un apagado y encendido. ciclo. Estos no se declaran como variables en el programa, sino que se escriben utilizando registros especiales en el microcontrolador. EEPROM también se puede implementar en un chip separado y se puede acceder a través de un bus SPI o I²C.

ROM es esencialmente lo mismo que Flash, excepto que está programado en fábrica (no programable por el usuario). Se usa solo para dispositivos de muy alto volumen.

NVRAM (RAM no volátil) es una alternativa a EEPROM y generalmente se implementa como un IC externo. La RAM normal puede considerarse no volátil si tiene una batería de respaldo; en ese caso, no se necesitan métodos de acceso especiales.

Aunque los datos se pueden guardar en Flash, la memoria Flash tiene un número limitado de ciclos de borrado / programa (1000 a 10,000), por lo que en realidad no está diseñada para eso. También requiere que se borren bloques de memoria a la vez, por lo que es inconveniente actualizar solo unos pocos bytes. Está diseñado para código y variables de solo lectura.

EEPROM tiene límites mucho más altos en ciclos de borrado / programa (100,000 a 1,000,000) por lo que es mucho mejor para este propósito. Si hay EEPROM disponible en el microcontrolador y es lo suficientemente grande, es donde desea guardar los datos no volátiles. Sin embargo, también tendrá que borrar en bloques primero (normalmente 4 KB) antes de escribir.

Si no hay EEPROM o es demasiado pequeño, se necesita un chip externo. Una EEPROM de 32 KB cuesta solo 66 ¢ y se puede borrar / escribir 1,000,000 de veces. Una NVRAM con el mismo número de operaciones de borrado / programa es mucho más cara (x10). Las NVRAM suelen ser más rápidas para leer que las EEPROM, pero más lentas para escribir. Pueden escribirse en un byte a la vez o en bloques.

Una mejor alternativa a ambos es FRAM (RAM ferroeléctrica), que tiene ciclos de escritura esencialmente infinitos (100 billones) y no tiene retrasos de escritura. Tiene aproximadamente el mismo precio que la NVRAM, alrededor de $ 5 por 32KB.

Sistema integrado normal:

Segment     Memory   Contents

.data       RAM      Explicitly initialized variables with static storage duration
.bss        RAM      Zero-initialized variables with static storage duration
.stack      RAM      Local variables and function call parameters
.heap       RAM      Dynamically allocated variables (usually not used in embedded systems)
.rodata     ROM      const variables with static storage duration. String literals.
.text       ROM      The program. Integer constants. Initializer lists.

Además, suele haber segmentos de flash separados para el código de inicio y los vectores de interrupción.


Explicación:

Una variable tiene static duración del almacenamiento si se declara como static o si reside en el ámbito del archivo (a veces llamado descuidadamente “global”). C tiene una regla que establece que todos static las variables de duración de almacenamiento que el programador no inicializó explícitamente deben inicializarse a cero.

Cada static La variable de duración de almacenamiento que se inicializa a cero, implícita o explícitamente, termina en .bss. Mientras que aquellos que se inicializan explícitamente a un valor distinto de cero terminan en .data.

Ejemplos:

static int a;                // .bss
static int b = 0;            // .bss      
int c;                       // .bss
static int d = 1;            // .data
int e = 1;                   // .data

void func (void)

  static int x;              // .bss
  static int y = 0;          // .bss
  static int z = 1;          // .data
  static int* ptr = NULL;    // .bss

Tenga en cuenta que una configuración no estándar muy común para los sistemas integrados es tener un “inicio mínimo”, lo que significa que el programa omitirá todos inicialización de objetos con static duración del almacenamiento. Por lo tanto, sería prudente no escribir nunca programas que se basen en los valores de inicialización de dichas variables, sino que los establezca en “tiempo de ejecución” antes de que se utilicen por primera vez.

Ejemplos de los otros segmentos:

const int a = 0;           // .rodata
const int b;               // .rodata (nonsense code but C allows it, unlike C++)
static const int c = 0;    // .rodata
static const int d = 1;    // .rodata

void func (int param)      // .stack

  int e;                   // .stack
  int f=0;                 // .stack
  int g=1;                 // .stack
  const int h=param;       // .stack
  static const int i=1;    // .rodata, static storage duration

  char* ptr;               // ptr goes to .stack
  ptr = malloc(1);         // pointed-at memory goes to .heap

Las variables que pueden ir a la pila a menudo pueden terminar en los registros de la CPU durante la optimización. Como regla general, cualquier variable que no tenga su dirección tomada puede colocarse en un registro de CPU.

Tenga en cuenta que los punteros son un poco más intrincados que otras variables, ya que permiten dos tipos diferentes de const, dependiendo de si los datos apuntados deben ser de solo lectura o si el propio puntero debe serlo. Es muy importante conocer la diferencia para que sus punteros no terminen en la RAM por accidente, cuando quería que estuvieran en flash.

int* j=0;                  // .bss
const int* k=0;            // .bss, non-const pointer to const data
int* const l=0;            // .rodata, const pointer to non-const data
const int* const m=0;      // .rodata, const pointer to const data

void (*fptr1)(void);       // .bss
void (*const fptr2)(void); // .rodata
void (const* fptr3)(void); // invalid, doesn't make sense since functions can't be modified

En el caso de constantes enteras, listas de inicializadores, string literales, etc., pueden terminar en .text o .rodata dependiendo del compilador. Probablemente, terminen como:

#define n 0                // .text
int o = 5;                 // 5 goes to .text (part of the instruction)
int p[] = 1,2,3;         // 1,2,3 goes to .text
char q[] = "hello";        // "hello" goes to .rodata

Si bien cualquier dato puede ir a cualquier memoria que elija el programador, generalmente el sistema funciona mejor (y está destinado a ser utilizado) cuando el perfil de uso de los datos coincide con los perfiles de lectura / escritura de la memoria.

Por ejemplo, el código del programa es WFRM (escribe pocos, lee muchos), y hay mucho. Esto encaja muy bien con FLASH. ROM OTOH es W una vez RM.

La pila y el montón son pequeños, con muchas lecturas y escrituras. Eso encajaría mejor con la RAM.

EEPROM no se adaptaría bien a ninguno de esos usos, pero sí se adapta al perfil de pequeñas cantidades de datos persistentes a través de los potenciadores, por lo que los datos de inicialización específicos del usuario y quizás los resultados de registro.

Si para ti ha sido de ayuda nuestro post, agradeceríamos que lo compartas con el resto programadores así nos ayudas a extender nuestro contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *