Tengo la respuesta para la memoria Flash, pero la pregunta sobre la memoria RAM aún se me escapa.
Arduino tiene esta característica súper agradable que muestra el uso de flash y RAM justo en el momento de la compilación. Ex. en la imagen de abajo puedes ver que este programa Arduino usa 2084 bytes de flash (6%) y que las variables globales y estáticas usan 188 bytes (9%) de memoria dinámica o SRAM.
Cuando compilo un programa de parpadeo simple en una placa de desarrollo Nucleo STM32F103RB en el IDE de System Workbench, me gustaría saber lo mismo: ¿cuánto Flash y RAM se usan y cuánto queda?
Cuando finaliza la construcción, System Workbench muestra:
Generating binary and Printing size information:
arm-none-eabi-objcopy -O binary "STM32F103RB_Nucleo.elf" "STM32F103RB_Nucleo.bin"
arm-none-eabi-size "STM32F103RB_Nucleo.elf"
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
Cuando miro el archivo de salida binario, "SW4STM32/Debug/STM32F103RB_Nucleo.bin", veo que tiene 2908 bytes, que es la suma de text
+ data
. Por lo tanto, ¡ese debe ser mi uso de Flash! Dado que tengo 128 KB de Flash, eso significa que estoy usando 2908/(128*1024) = 2 % del espacio total de Flash.
Pero, ¿cómo averiguo cuánta SRAM están usando mis variables globales y estáticas, y cuánto está disponible para las variables locales (como muestra Arduino)?
Todavía no tengo ni idea de lo que significa nada de esto, pero si esto es útil para ayudarlo a ayudarme , aquí está el resultado de objdump -h STM32F103RB_Nucleo.elf
:
$ objdump -h STM32F103RB_Nucleo.elf
STM32F103RB_Nucleo.elf: file format elf32-little
Sections:
Idx Name Size VMA LMA File off Algn
0 .isr_vector 0000010c 08000000 08000000 00010000 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .text 00000a1c 0800010c 0800010c 0001010c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .rodata 00000028 08000b28 08000b28 00010b28 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .init_array 00000004 08000b50 08000b50 00010b50 2**2
CONTENTS, ALLOC, LOAD, DATA
4 .fini_array 00000004 08000b54 08000b54 00010b54 2**2
CONTENTS, ALLOC, LOAD, DATA
5 .data 00000004 20000000 08000b58 00020000 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .bss 00000030 20000004 08000b5c 00020004 2**2
ALLOC
7 ._user_heap_stack 00000604 20000034 08000b5c 00020034 2**0
ALLOC
8 .ARM.attributes 00000029 00000000 00000000 00020004 2**0
CONTENTS, READONLY
9 .debug_info 00006795 00000000 00000000 0002002d 2**0
CONTENTS, READONLY, DEBUGGING
10 .debug_abbrev 000013b2 00000000 00000000 000267c2 2**0
CONTENTS, READONLY, DEBUGGING
11 .debug_loc 00000d0f 00000000 00000000 00027b74 2**0
CONTENTS, READONLY, DEBUGGING
12 .debug_aranges 000002e0 00000000 00000000 00028888 2**3
CONTENTS, READONLY, DEBUGGING
13 .debug_ranges 00000378 00000000 00000000 00028b68 2**3
CONTENTS, READONLY, DEBUGGING
14 .debug_macro 00001488 00000000 00000000 00028ee0 2**0
CONTENTS, READONLY, DEBUGGING
15 .debug_line 00003dcc 00000000 00000000 0002a368 2**0
CONTENTS, READONLY, DEBUGGING
16 .debug_str 00066766 00000000 00000000 0002e134 2**0
CONTENTS, READONLY, DEBUGGING
17 .comment 0000007f 00000000 00000000 0009489a 2**0
CONTENTS, READONLY
18 .debug_frame 00000610 00000000 00000000 0009491c 2**2
CONTENTS, READONLY, DEBUGGING
size
la salida de binutils del formato "sysv" ( size --format=sysv my_executable
) al formato "berkeley" ( size --format=berkeley my_executable
)La información que necesita está toda en la salida de size
(aka arm-none-eabi-size
):
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
text
es el tamaño de todo el código en su aplicación.
data
es el tamaño de las variables globales inicializadas. Cuenta contra la memoria flash y la RAM, ya que se copia de la memoria flash a la RAM durante el inicio.
bss
es el tamaño de las variables globales que se inicializan en cero (o no se inicializan y, por lo tanto, el valor predeterminado es cero). Se almacenan solo en RAM.
dec
y hex
son la suma de text + data + bss
en decimal y hexadecimal. Este valor realmente no significa mucho en un microcontrolador, por lo que debe ignorarse. (En entornos donde un programa debe cargarse en la memoria antes de ejecutarse, sería la huella de memoria total del programa).
Para calcular el uso de RAM de su programa, sume las columnas data
y bss
juntas.
SRAM = data + bss
Para calcular el uso de FLASH de su programa, agregue text
y data
.
FLASH = text + data
dec
y hex
son del tamaño de mi archivo ELF, mi archivo ELF es en realidad 616876 bytes. ¿Alguna idea de por qué?arm-none-eabi-size STM32F103RB_Nucleo.elf
.text
, .rodata
, .data
. y .bss
. ¿ Se data
muestra arriba por arm-none-eabi-size
la suma de lo que está en .rodata
y .data
en el script del enlazador?.rodata
todo esto?(In environments where a program must be loaded into memory before running, it would be the total memory footprint of the program.)
.... quiere decir como en una computadora normal, ¿verdad? Estoy tratando de ver estos valores para un programa en una computadora normal ahora, en lugar de en un microcontrolador. Entonces, size name_of_executable
parece ser la respuesta. Ej: size a.out
.Si desea un script bash de Linux rápido para calcular automáticamente el uso de Flash y SRAM, vea mi otra respuesta aquí .
Vaya directamente al "Resumen" en la parte inferior.
@duskwuff -inactive- respondió el quid de mi pregunta en su respuesta aquí , pero me gustaría agregar información adicional y también responder mis propias preguntas de seguimiento que escribí en los comentarios debajo de su respuesta.
En primer lugar, la sección "Conceptos básicos de secuencias de comandos de Linker" del manual de GNU Linker , incluida en su totalidad en mi sección "Referencias" a continuación, fue clave para aprender esta información. Consúltelo en la parte inferior de esta respuesta, a continuación.
Para una mirada más detallada a algunos de los aspectos de esta respuesta, consulte también mi otra respuesta a una pregunta relacionada que hice en Stack Overflow aquí .
Resulta que la información de objdump -h STM32F103RB_Nucleo.elf
contiene las mismas subsecciones de salida más específicas que el arm-none-eabi-size -x --format=sysv "STM32F103RB_Nucleo.elf"
comando, que muestra la size
salida en el sysv
formato en lugar del berkeley
formato predeterminado.
Aquí nuevamente está la salida en formato berkeley del comando size
( arm-none-eabi-size
para STM32 mcus):
arm-none-eabi-size "STM32F103RB_Nucleo.elf"
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
Tenga en cuenta que arm-none-eabi-size "STM32F103RB_Nucleo.elf"
es equivalente a arm-none-eabi-size --format=berkeley "STM32F103RB_Nucleo.elf"
, ya que --format=berkeley
es el valor predeterminado.
En el objdump -h STM32F103RB_Nucleo.elf
resultado que publiqué en mi pregunta, se encuentra toda la información que necesitamos para responder mi pregunta, solo que en un formato mucho más detallado.
Como explica el manual del enlazador GNU (ver más abajo), VMA significa "Dirección de memoria virtual" y LMA significa "Dirección de memoria de carga". Las direcciones VMA son donde se ubican los datos en tiempo de ejecución (que podría estar en SRAM volátil, ya que algunos datos se copian de Flash a SRAM en el arranque), y LMA es donde se ubican los datos cuando el dispositivo está apagado, y también antes de cargar en el arranque (por lo que debe estar solo en la memoria Flash no volátil ). Algunos datos se copiarán de Flash (dirección LMA) a SRAM (dirección VMA) en el arranque.
Para este microcontrolador STM32, la memoria Flash comienza en la dirección 0x08000000
y la SRAM comienza en la dirección 0x20000000
. Por lo tanto, cualquier sección de salida en la objdump -h
salida que tenga un VMA (dirección de tiempo de ejecución) de 0, por lo tanto, no se utiliza (ni siquiera en el microcontrolador) y se puede descartar de inmediato. Eso elimina la segunda mitad de la objdump -h
salida, la mayor parte de la cual es información de depuración, desde la .ARM.attributes
sección de salida hasta la .debug_frame
sección de salida, inclusive, dejándonos solo con estas secciones de salida de sysv que nos interesan :
Sections:
Idx Name Size VMA LMA File off Algn
0 .isr_vector 0000010c 08000000 08000000 00010000 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .text 00000a1c 0800010c 0800010c 0001010c 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .rodata 00000028 08000b28 08000b28 00010b28 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .init_array 00000004 08000b50 08000b50 00010b50 2**2
CONTENTS, ALLOC, LOAD, DATA
4 .fini_array 00000004 08000b54 08000b54 00010b54 2**2
CONTENTS, ALLOC, LOAD, DATA
5 .data 00000004 20000000 08000b58 00020000 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .bss 00000030 20000004 08000b5c 00020004 2**2
ALLOC
7 ._user_heap_stack 00000604 20000034 08000b5c 00020034 2**0
ALLOC
Cualquier sección marcada READONLY
, como puede ver, se almacena solo en la memoria Flash en las 0x08000000
direcciones de nivel y tiene la misma dirección LMA y VMA. Esto tiene sentido, ya que no es necesario copiarlo en SRAM si es de solo lectura. Las secciones marcadas READONLY
conforman la text
sección de formato berkeley. Incluyen:
.isr_vector
.text
.rodata
Entonces, sabemos que:
.isr_vector + .text + .rodata = text
Esto se puede verificar sumando sus tamaños hexadecimales:
10c + a1c + 28 = b50
¡0xb50 es el decimal 2896, que coincide con la salida del tamaño de berkeley para la text
sección! Y, nuevamente, como lo muestran las 0x08000000
direcciones de nivel tanto para LMA como para VMA, ¡esto significa que todas estas secciones están solo en la memoria Flash !
Aquí está mi descripción de lo que son estas secciones:
SECCIONES SYSV QUE COMPONEN LA text
SECCIÓN BERKELEY Y QUE ESTÁN EN LA MEMORIA FLASH SOLAMENTE:
.isr_vector
= la tabla de vectores ISR (rutina de servicio de interrupción). Simplemente apunta a todas las funciones de devolución de llamada ISR para todas las posibles interrupciones que el microcontrolador puede manejar..text
= lógica del programa; es decir: el código real..rodata
= Datos de solo lectura; es decir: const
y constexpr
variables estáticas y globales que son de solo lectura.A continuación, podemos ver que las READONLY
secciones NO que también son ALLOC
y LOAD
secciones incluyen:
.init_array
.fini_array
.data
Entonces, sabemos que conforman la data
sección de berkeley:
.init_array + .fini_array + .data = data
Esto se puede verificar sumando sus tamaños hexadecimales:
4 + 4 + 4 = c
0xc es el decimal 12, que coincide con la salida de tamaño berkeley para la data
sección.
No sé qué significan las secciones .init_array
o .fini_array
(si lo sabe, responda o publique un comentario), y estoy confundido sobre su ubicación, ya que sus direcciones LMA y VMA son idénticas, lo que indica que ambas están en Flash y solo en Flash. . Sin embargo, está claro por las direcciones de la .data
sección, que ocupa tanto la memoria Flash (en LMA [dirección de carga] = 0x08000b58) como la memoria SRAM (en VMA [dirección de tiempo de ejecución] = 0x20000000). Esto significa que estos datos se copian de Flash al comienzo de SRAM. Esto sucede durante la rutina de inicio. .data
contiene variables estáticas y globales no inicializadas en cero (es decir, inicializadas con algo distinto de cero). En resumen:
SECCIONES SYSV QUE COMPONEN LA data
SECCIÓN BERKELEY Y QUE ESTÁN TANTO EN FLASH COMO SRAM, Y QUE SE COPIAN DE FLASH A SRAM DURANTE EL INICIO:
.data
= NON-zero-inicializado (es decir: inicializado con algo distinto de cero) variables estáticas y globales¿SECCIONES SYSV QUE COMPONEN LA data
SECCIÓN BERKELEY Y QUE APARENTEMENTE ESTÁN SÓLO EN LA MEMORIA FLASH?:
.init_array
= desconocido.fini_array
= desconocidoEso nos deja solo con estas secciones marcadas con ALLOC
y nada más restante:
.bss
._user_heap_stack
Entonces, sabemos que conforman la bss
sección de berkeley:
.bss + ._user_heap_stack = bss
Esto se puede verificar sumando sus tamaños hexadecimales:
30 + 604 = 634
0x634 es el decimal 1588, que coincide con la salida de tamaño berkeley para la bss
sección.
¡Esto es realmente interesante! , ya que muestra que la bss
sección berkeley no solo incluye la .bss
sección de salida (variables estáticas y globales inicializadas en cero), sino que también incluye la ._user_heap_stack
sección de salida, que es quizás (o más bien, me parece ser) el tamaño del montón especificamos dentro del software de configuración STM32Cube. En cualquier caso, parece ser la SRAM reservada tanto para la pila de tiempo de ejecución (para las variables locales) como para el almacenamiento dinámico (para la memoria asignada dinámicamente). En resumen:
SECCIONES SYSV QUE COMPONEN LA bss
SECCIÓN BERKELEY Y QUE OCUPAN ESPACIO SOLO EN SRAM, PERO NO EN FLASH:
.bss
= variables estáticas y globales inicializadas en cero; esta SRAM se establece en ceros al inicio del programa.._user_heap_stack
= (Creo) SRAM completamente sin inicializar que se reserva para la pila de tiempo de ejecución (para variables locales) y el montón (para memoria asignada dinámicamente).Aquí está el desglose de las secciones de salida de sysv de la objdump -h STM32F103RB_Nucleo.elf
salida (que también se muestran con menos detalles en la arm-none-eabi-size -x --format=sysv "STM32F103RB_Nucleo.elf"
salida) que componen las secciones de salida de berkeley.
En la imagen a continuación, puede ver las 3 secciones de salida de berkely enmarcadas en diferentes colores:
[AMARILLO] Las secciones de salida en formato berkeley text
(solo lectura, lógica de programa y variables estáticas y globales constantes) están enmarcadas en amarillo .
.isr_vector + .text + .rodata = text
.isr_vector
[SOLO EN FLASH] = la tabla de vectores ISR (rutina de servicio de interrupción). Simplemente apunta a todas las funciones de devolución de llamada ISR para todas las posibles interrupciones que el microcontrolador puede manejar..text
[SOLO EN FLASH] = lógica del programa; es decir: el código real..rodata
[SOLO EN FLASH] = datos de solo lectura; es decir: const
y constexpr
variables estáticas y globales que son de solo lectura.[AZUL] Las secciones de salida en formato berkeley data
(variables estáticas y globales inicializadas distintas de cero [es decir, inicializadas con valores distintos de cero]) están encuadradas en azul .
.init_array + .fini_array + .data = data
.init_array
[Aparece SOLO FLASH] = desconocido..fini_array
[Aparece SOLO FLASH] = desconocido..data
[EN AMBOS FLASH Y SRAM] = Variables estáticas y globales inicializadas distintas de cero (es decir, inicializadas con algo distinto de cero). Estos valores deben copiarse de Flash a SRAM al inicio, para inicializar sus correspondientes variables estáticas o globales en SRAM.[ROJO] Las secciones de salida en formato berkeley bss
(variables estáticas y globales inicializadas en cero, y también, aparentemente, espacio de pila y pila no inicializado) están enmarcadas en rojo .
.bss + ._user_heap_stack = bss
.bss
[SOLO SRAM] = variables estáticas y globales inicializadas en cero; esta SRAM se establece en ceros al inicio del programa.._user_heap_stack
[SOLO SRAM] = SRAM completamente sin inicializar (creo) que se reserva para la pila de tiempo de ejecución (para variables locales) y el montón (para memoria asignada dinámicamente).[GRIS] Las secciones de salida de sysv desechadas que no contribuyen a ninguna de las 3 secciones de salida de berkeley están encuadradas en gris .
Conclusiones de la memoria:
text
+ berkeley data
.
.isr_vector
..text
..rodata
.bss
+ berkeley data
.
(.bss + .data)
. Observe los puntos ( .
) antes de cada uno de estos nombres aquí, a diferencia de la falta de puntos arriba.._user_heap_stack
.SRAM_total - (berkeley bss + berkeley data)
.Manual de GNU Linker ( ld
), sección "3.1 Conceptos básicos del script del vinculador": https://sourceware.org/binutils/docs/ld/Basic-Script-Concepts.html#Basic-Script-Concepts :
3.1 Conceptos básicos de la secuencia de comandos del enlazador
Necesitamos definir algunos conceptos básicos y vocabulario para describir el lenguaje de script del enlazador.
El enlazador combina archivos de entrada en un único archivo de salida. El archivo de salida y cada archivo de entrada están en un formato de datos especial conocido como formato de archivo de objeto . Cada archivo se denomina archivo de objeto . El archivo de salida a menudo se denomina ejecutable , pero para nuestros propósitos también lo llamaremos archivo de objeto. Cada archivo de objeto tiene, entre otras cosas, una lista de secciones . A veces nos referimos a una sección en un archivo de entrada como una sección de entrada ; De manera similar, una sección en el archivo de salida es una sección de salida .
Cada sección en un archivo de objeto tiene un nombre y un tamaño. La mayoría de las secciones también tienen un bloque de datos asociado, conocido como contenido de la sección . Una sección puede marcarse como cargable , lo que significa que el contenido debe cargarse en la memoria cuando se ejecuta el archivo de salida. Una sección sin contenido puede ser asignable , lo que significa que se debe reservar un área en la memoria, pero no se debe cargar nada en particular allí (en algunos casos, esta memoria debe ponerse a cero). Una sección que no se puede cargar ni asignar normalmente contiene algún tipo de información de depuración.
Cada sección de salida cargable o asignable tiene dos direcciones. El primero es el VMA , o dirección de memoria virtual. Esta es la dirección que tendrá la sección cuando se ejecute el archivo de salida. La segunda es la LMA , o dirección de memoria de carga. Esta es la dirección en la que se cargará la sección. En la mayoría de los casos, las dos direcciones serán las mismas. Un ejemplo de cuándo pueden ser diferentes es cuando una sección de datos se carga en la ROM y luego se copia en la RAM cuando se inicia el programa (esta técnica se usa a menudo para inicializar variables globales en un sistema basado en ROM). En este caso, la dirección de la ROM sería la LMA y la dirección de la RAM sería la VMA.
Puede ver las secciones en un archivo de objeto utilizando el
objdump
programa con la opción '-h'.Cada archivo de objeto también tiene una lista de símbolos , conocida como la tabla de símbolos . Un símbolo puede estar definido o no definido. Cada símbolo tiene un nombre, y cada símbolo definido tiene una dirección, entre otra información. Si compila un programa C o C++ en un archivo de objeto, obtendrá un símbolo definido para cada función definida y variable estática o global. Cada función indefinida o variable global a la que se hace referencia en el archivo de entrada se convertirá en un símbolo indefinido.
Puede ver los símbolos en un archivo de objeto usando el
nm
programa, o usando elobjdump
programa con la opción '-t'.
Mi propia respuesta a mi propia pregunta aquí: convertir size
la salida de binutils del formato "sysv" ( size --format=sysv my_executable
) al formato "berkeley" ( size --format=berkeley my_executable
)
.init_array
y .fini_array
-- las variables globales en C++ tendrán su constructor y destructores ejecutándose antes de que main comience a ejecutarse y después de que main salga, respectivamente. Estas dos secciones contienen (punteros a) funciones de construcción y destrucción que deben ser invocadas por la biblioteca de soporte de tiempo de ejecución. Pure ISO Standard C no tiene tal cosa (no puede inicializar una variable global C con una expresión dinámica), pero gcc lo agrega a C como una extensión usando __attribute((constructor))__
y __attribute__((destructor))
.Como una extensión de las otras dos respuestas, incluida la mía , aquí hay un script bash de Linux de 1 línea que puede usar para calcular FLASH
y SRAM
usar automáticamente.
Esto supone que su comando para ver la información de tamaño de Berkeley de su archivo .elf es arm-none-eabi-size "STM32F103RB_Nucleo.elf"
. Actualice esa ruta a su archivo .elf de interés. Si busca archivos .elf de Linux o archivos ejecutables normales en lugar de archivos .elf de STM32, use size
en lugar de arm-none-eabi-size
. Ej: size path/to/my_program.elf
.
De todos modos, aquí está el guión:
size_info=$(arm-none-eabi-size "STM32F103RB_Nucleo.elf" | awk NR\>1); \
text=$(echo "$size_info" | awk '{print $1}'); \
data=$(echo "$size_info" | awk '{print $2}'); \
bss=$(echo "$size_info" | awk '{print $3}'); \
flash=$(($text + $data)); \
sram=$(($bss + $data)); \
echo "FLASH used = $flash bytes"; \
echo "SRAM used by global variables = $sram bytes"
Suponiendo que arm-none-eabi-size "STM32F103RB_Nucleo.elf"
muestra esto:
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
... aquí hay un resultado de muestra de ejecutar el script anterior:
FLASH used = 2908 bytes
SRAM used by global variables = 1600 bytes
FLASH = text + data
SRAM = bss + data
text
= datos del programa + vector ISR + rodata (datos de solo lectura: const
y constexpr
variables estáticas y globales de solo lectura)data
= NON-zero-inicializado (es decir: inicializado con un número distinto de cero) variables estáticas y globales, + .init_array + .fini_arraybss
= variables estáticas y globales inicializadas en cero + espacio de pila y montón reservado por la secuencia de comandos del enlazadorPara obtener más detalles, vea mi otra respuesta, muy detallada, aquí: ¿Cómo averiguo en tiempo de compilación qué cantidad de memoria Flash y memoria dinámica (SRAM) de un STM32 se utiliza?
awk
para eliminar la primera línea de un blob de texto: https://superuser.com/questions/284258/remove-first-line-in-bash/284270#284270awk
ejemplo general de mis propias notas para recordarme cómo usarlo:du -h | tail -n 1 | awk '{print $1}'
chris stratton
grapas gabriel