У меня есть ответ для флэш-памяти, но вопрос с оперативной памятью все еще ускользает от меня.
У Arduino есть отличная функция, которая отображает использование флэш-памяти и оперативной памяти прямо во время компиляции. Бывший. на изображении ниже вы можете видеть, что эта программа Arduino использует 2084 байта флэш-памяти (6%) и что глобальные и статические переменные используют 188 байтов (9%) динамической памяти или SRAM.
Когда я компилирую простую программу мигания на плате разработки STM32F103RB Nucleo в IDE System Workbench, я хотел бы знать то же самое: сколько флэш-памяти и оперативной памяти используется и сколько осталось?
По завершении сборки System Workbench показывает:
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
Когда я смотрю на двоичный выходной файл «SW4STM32/Debug/STM32F103RB_Nucleo.bin», я вижу, что он составляет 2908 байт, что является суммой text
+ data
. Следовательно, это должно быть мое использование Flash! Поскольку у меня 128 КБ флэш-памяти, это означает, что я использую 2908/(128*1024) = 2% от общего объема флэш-памяти.
Но как узнать, сколько SRAM используют мои глобальные и статические переменные и сколько доступно для локальных переменных (как показывает Arduino)?
Я еще не знаю, что это означает, но если это поможет вам помочь мне , вот вывод 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
size --format=sysv my_executable
size --format=berkeley my_executable
Вся необходимая вам информация содержится в выводе size
(ака arm-none-eabi-size
):
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
text
— это размер всего кода в вашем приложении.
data
размер инициализированных глобальных переменных. Он учитывается как во флэш-памяти, так и в ОЗУ, поскольку во время запуска копируется из флэш-памяти в ОЗУ.
bss
это размер глобальных переменных, которые инициализируются нулем (или не инициализируются и, следовательно, по умолчанию равны нулю). Они хранятся только в оперативной памяти.
dec
и hex
представляют собой сумму text + data + bss
в десятичном и шестнадцатеричном формате. Это значение на самом деле мало что значит для микроконтроллера, поэтому его следует игнорировать. (В средах, где программа должна быть загружена в память перед запуском, это будет общий объем памяти, занимаемой программой.)
Чтобы рассчитать использование оперативной памяти вашей программой, сложите столбцы data
и bss
вместе.
SRAM = data + bss
Чтобы рассчитать использование FLASH вашей программой, добавьте text
и data
.
FLASH = text + data
dec
и hex
это размер моего файла ELF, мой файл ELF на самом деле составляет 616876 байт. Есть идеи, почему?arm-none-eabi-size STM32F103RB_Nucleo.elf
.text
, .rodata
, .data
., и .bss
. Показана data
выше arm-none-eabi-size
сумма того, что есть .rodata
и .data
в скрипте компоновщика?.rodata
во всем этом?(In environments where a program must be loaded into memory before running, it would be the total memory footprint of the program.)
.... вы имеете в виду, как на обычном компьютере, верно? Я сейчас пытаюсь посмотреть эти значения для программы на обычном компьютере, а не на микроконтроллере. Итак, size name_of_executable
кажется, ответ. Пример: size a.out
.Если вам нужен быстрый bash-скрипт Linux для автоматического расчета использования Flash и SRAM, см. мой другой ответ здесь .
Перейти прямо вниз к «Резюме» внизу.
@duskwuff -inactive- ответил на суть моего вопроса в своем / его ответе здесь , но я хотел бы добавить некоторые дополнительные сведения, а также ответить на свои дополнительные вопросы, которые я написал в комментариях под его ответом.
Во-первых, ключом к изучению этой информации был раздел «Основные концепции скриптов компоновщика» руководства GNU Linker , полностью включенный в мой раздел «Ссылки» ниже. Обратитесь к нему внизу этого ответа, ниже.
Для более подробного изучения некоторых аспектов этого ответа обратитесь также к другому моему ответу на связанный вопрос, который я задал в Stack Overflow здесь .
Оказывается, информация from objdump -h STM32F103RB_Nucleo.elf
содержит те же, более конкретные подразделы вывода, что и команда arm-none-eabi-size -x --format=sysv "STM32F103RB_Nucleo.elf"
, которая показывает size
вывод в sysv
формате, а не в berkeley
формате по умолчанию.
Вот снова вывод в формате berkeley из команды size
( arm-none-eabi-size
для STM32 mcus):
arm-none-eabi-size "STM32F103RB_Nucleo.elf"
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
Обратите внимание, что arm-none-eabi-size "STM32F103RB_Nucleo.elf"
это эквивалентно arm-none-eabi-size --format=berkeley "STM32F103RB_Nucleo.elf"
, поскольку --format=berkeley
используется по умолчанию.
В objdump -h STM32F103RB_Nucleo.elf
выводе, который я разместил в своем вопросе, найдена вся информация, необходимая для ответа на мой вопрос, только в гораздо более подробном формате.
Как поясняется в руководстве компоновщика GNU (см. ниже), VMA означает «Адрес виртуальной памяти», а LMA означает «Адрес загрузки памяти». Адреса VMA — это место, где находятся данные во время выполнения (которые могут находиться в энергозависимой SRAM, поскольку некоторые данные копируются из флэш-памяти в SRAM при загрузке), а LMA — это место, где находятся данные, когда устройство выключено, и также до загрузки при загрузке (поэтому он должен находиться только в энергонезависимой флэш-памяти ). Некоторые данные будут скопированы из флэш-памяти (адрес LMA) в SRAM (адрес VMA) при загрузке.
Для этого микроконтроллера STM32 флэш-память начинается с адреса 0x08000000
, а SRAM — с адреса 0x20000000
. Таким образом, любые выходные разделы на objdump -h
выходе, которые имеют VMA (адрес времени выполнения) 0, поэтому не используются (даже на микроконтроллере) и могут быть немедленно отброшены. Это исключает вторую половину вывода objdump -h
, большую часть которой составляет отладочная информация, от .ARM.attributes
раздела вывода до .debug_frame
раздела вывода включительно, оставляя нам только эти разделы вывода sysv, которые нас интересуют :
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
Как видите , любой раздел READONLY
, помеченный , хранится только во Flash-памяти по 0x08000000
адресам -уровня и имеет один и тот же адрес LMA и VMA. Это имеет смысл, так как нет необходимости копировать его в SRAM, если он доступен только для чтения. Отмеченные разделы READONLY
составляют text
раздел в формате Беркли. Они включают:
.isr_vector
.text
.rodata
Итак, мы знаем, что:
.isr_vector + .text + .rodata = text
Это можно проверить, просуммировав их шестнадцатеричные размеры:
10c + a1c + 28 = b50
0xb50 — это десятичное число 2896, что соответствует выходному размеру text
раздела по Беркли! И, опять же, как показывают 0x08000000
адреса уровня LMA и VMA, это означает, что все эти разделы находятся только во флэш-памяти !
Вот мое описание этих разделов:
РАЗДЕЛЫ SYSV, СОСТАВЛЯЮЩИЕ text
РАЗДЕЛ BERKELEY И ТОЛЬКО ВО ФЛЭШ-ПАМЯТИ:
.isr_vector
= таблица векторов ISR (подпрограмма обслуживания прерываний). Он просто указывает на все функции обратного вызова ISR для всех возможных прерываний, которые может обработать микроконтроллер..text
= логика программы; то есть: фактический код..rodata
= данные только для чтения; то есть: const
и constexpr
статические и глобальные переменные, которые доступны только для чтения.Далее мы видим, что NON- READONLY
разделы, которые также являются ALLOC
и LOAD
разделами, включают в себя:
.init_array
.fini_array
.data
Итак, мы знаем, что они составляют data
секцию беркли:
.init_array + .fini_array + .data = data
Это можно проверить, просуммировав их шестнадцатеричные размеры:
4 + 4 + 4 = c
0xc — это десятичное число 12, что соответствует выходному размеру data
раздела по Беркли.
Я не знаю, что означают разделы .init_array
или .fini_array
(если вы знаете, пожалуйста, ответьте или оставьте комментарий), и я запутался в их местонахождении, поскольку их адреса LMA и VMA идентичны, что указывает на то, что они как во Flash, так и только во Flash. . Однако по адресам секции видно .data
, что она занимает как Flash-память (при LMA [адрес загрузки] = 0x08000b58), так и память SRAM (при VMA [адрес времени выполнения] = 0x20000000). Это означает, что эти данные копируются из флэш-памяти в начало SRAM. Это происходит во время процедуры запуска. .data
содержит НЕ-нулевые инициализированные (т.е. инициализированные чем-то отличным от нуля) статические и глобальные переменные. В итоге:
РАЗДЕЛЫ SYSV, СОСТАВЛЯЮЩИЕ data
РАЗДЕЛ BERKELEY, НАХОДЯЩИЕСЯ КАК ВО FLASH, ТАК И В SRAM, И КОТОРЫЕ КОПИРУЮТСЯ ИЗ FLASH В SRAM ВО ВРЕМЯ ЗАПУСКА:
.data
= НЕнулевые инициализированные (т.е. инициализированные чем-то отличным от нуля) статические и глобальные переменныеРАЗДЕЛЫ SYSV, СОСТАВЛЯЮЩИЕ data
РАЗДЕЛ BERKELEY И КОТОРЫЕ ЯВНО НАХОДЯТСЯ ТОЛЬКО В ФЛЭШ-ПАМЯТИ?:
.init_array
= неизвестно.fini_array
= неизвестноЭто оставляет нам только эти разделы, отмеченные ALLOC
и больше ничего не осталось:
.bss
._user_heap_stack
Итак, мы знаем, что они составляют bss
раздел Беркли:
.bss + ._user_heap_stack = bss
Это можно проверить, просуммировав их шестнадцатеричные размеры:
30 + 604 = 634
0x634 — это десятичное число 1588, которое соответствует размеру беркли для раздела bss
.
Это действительно интересно! , так как это показывает, что раздел беркли bss
включает не только .bss
раздел вывода (статические и глобальные переменные с нулевой инициализацией), но также включает раздел вывода ._user_heap_stack
, который, возможно (или, скорее, мне кажется) размер кучи мы указываем внутри программного обеспечения конфигурации STM32Cube. В любом случае это SRAM, выделенная как для стека времени выполнения (для локальных переменных), так и для кучи (для динамически выделяемой памяти). В итоге:
РАЗДЕЛЫ SYSV, СОСТАВЛЯЮЩИЕ bss
РАЗДЕЛ BERKELEY И ЗАНИМАЮЩИЕ МЕСТО ТОЛЬКО В SRAM, НО НЕ FLASH:
.bss
= инициализированные нулем статические и глобальные переменные; эта SRAM устанавливается на все нули при запуске программы.._user_heap_stack
= (я думаю) полностью неинициализированная SRAM, которая выделена для стека времени выполнения (для локальных переменных) и кучи (для динамически выделяемой памяти).Вот разбивка того, какие разделы вывода sysv из objdump -h STM32F103RB_Nucleo.elf
вывода (также показанные с меньшими подробностями в arm-none-eabi-size -x --format=sysv "STM32F103RB_Nucleo.elf"
выводе) составляют какие разделы вывода berkeley.
На изображении ниже вы можете увидеть все 3 раздела вывода berkely, обведенные разными цветами:
[ЖЕЛТЫЙ] Разделы вывода в формате Беркли text
(только для чтения, программная логика и константные статические и глобальные переменные) выделены желтым цветом .
.isr_vector + .text + .rodata = text
.isr_vector
[ТОЛЬКО ДЛЯ ФЛЭШ] = таблица векторов ISR (процедура обслуживания прерываний). Он просто указывает на все функции обратного вызова ISR для всех возможных прерываний, которые может обработать микроконтроллер..text
[ТОЛЬКО ВО FLASH] = программная логика; то есть: фактический код..rodata
[ТОЛЬКО FLASH] = данные только для чтения; то есть: const
и constexpr
статические и глобальные переменные, которые доступны только для чтения.[СИНИЙ] Разделы вывода в формате Беркли data
(ненулевые инициализированные [т.е. инициализированные значениями, отличными от нуля] статические и глобальные переменные) выделены синим цветом .
.init_array + .fini_array + .data = data
.init_array
[ТОЛЬКО ВСПЫШКА появляется] = неизвестно..fini_array
[ТОЛЬКО ВСПЫШКА появляется] = неизвестно..data
[В ОБА FLASH И SRAM] = ненулевые инициализированные (т.е. инициализированные чем-то отличным от нуля) статические и глобальные переменные. Эти значения должны быть скопированы из флэш-памяти в SRAM при запуске, чтобы инициализировать соответствующие им статические или глобальные переменные в SRAM.[RED] Секции вывода в формате berkeley bss
(статические и глобальные переменные, инициализированные нулями, а также, по-видимому, неинициализированные пространство стека и кучи) выделены красным .
.bss + ._user_heap_stack = bss
.bss
[ТОЛЬКО SRAM] = статические и глобальные переменные с нулевой инициализацией; эта SRAM устанавливается на все нули при запуске программы.._user_heap_stack
[ТОЛЬКО SRAM] = полностью неинициализированная (я думаю) SRAM, которая выделена для стека времени выполнения (для локальных переменных) и кучи (для динамически выделяемой памяти).[GREY] Отброшенные разделы вывода sysv, которые не участвуют ни в одном из 3 разделов вывода berkeley, выделены серым цветом .
Выводы памяти:
text
+ berkeley data
.
.isr_vector
..text
..rodata
.bss
+ berkeley data
.
(.bss + .data)
. Обратите внимание на точки ( .
) перед каждым из этих имен здесь, в отличие от отсутствия точек выше.._user_heap_stack
.SRAM_total - (berkeley bss + berkeley data)
.Руководство GNU Linker ( ld
), раздел «3.1 Основные концепции скриптов компоновщика»: https://sourceware.org/binutils/docs/ld/Basic-Script-Concepts.html#Basic-Script-Concepts :
3.1 Основные концепции скрипта компоновщика
Нам нужно определить некоторые основные понятия и словарный запас, чтобы описать скриптовый язык компоновщика.
Компоновщик объединяет входные файлы в один выходной файл. Выходной файл и каждый входной файл имеют специальный формат данных, известный как формат объектного файла . Каждый файл называется объектным файлом . Выходной файл часто называют исполняемым файлом , но для наших целей мы также будем называть его объектным файлом. Каждый объектный файл имеет, среди прочего, список разделов . Иногда мы называем раздел во входном файле входным разделом ; аналогично раздел в выходном файле является выходным разделом .
Каждый раздел в объектном файле имеет имя и размер. Большинство разделов также имеют связанный блок данных, известный как содержимое раздела . Раздел может быть помечен как загружаемый , что означает, что содержимое должно быть загружено в память при запуске выходного файла. Раздел без содержимого может быть выделяемым , что означает, что область в памяти должна быть выделена, но ничего конкретного туда не должно загружаться (в некоторых случаях эта память должна быть обнулена). Раздел, который нельзя ни загрузить, ни выделить, обычно содержит некоторую отладочную информацию.
Каждая загружаемая или размещаемая секция вывода имеет два адреса. Первый — это VMA или адрес виртуальной памяти. Это адрес, который раздел будет иметь при запуске выходного файла. Второй — это LMA или адрес загрузки памяти. Это адрес, по которому раздел будет загружен. В большинстве случаев два адреса будут одинаковыми. Примером того, когда они могут отличаться, является случай, когда раздел данных загружается в ПЗУ, а затем копируется в ОЗУ при запуске программы (этот метод часто используется для инициализации глобальных переменных в системе на основе ПЗУ). В этом случае адрес ROM будет LMA, а адрес RAM будет VMA.
Вы можете увидеть разделы в объектном файле, используя
objdump
программу с опцией '-h'.Каждый объектный файл также имеет список символов , известный как таблица символов . Символ может быть определен или не определен. У каждого символа есть имя, а у каждого определенного символа, среди прочего, есть адрес. Если вы скомпилируете программу на C или C++ в объектный файл, вы получите определенный символ для каждой определенной функции и глобальной или статической переменной. Каждая неопределенная функция или глобальная переменная, на которую есть ссылка во входном файле, станет неопределенным символом.
Вы можете увидеть символы в объектном файле с помощью программы
nm
или с помощьюobjdump
программы с опцией '-t'.
Мой собственный ответ на мой собственный вопрос здесь: преобразовать вывод binutils из формата «sysv» ( ) в формат «berkeley» ( )size
size --format=sysv my_executable
size --format=berkeley my_executable
.init_array
и .fini_array
-- глобальные переменные в C++ будут иметь свои конструкторы и деструкторы, запускаемые до того, как main начнет выполняться и после выхода из main соответственно. Эти два раздела содержат (указатели на) функции создания и уничтожения, которые должны вызываться библиотекой поддержки времени выполнения. В чистом стандарте ISO C такой вещи нет (вы не можете инициализировать глобальную переменную C динамическим выражением), но gcc добавляет ее в C как расширение, используя __attribute((constructor))__
и __attribute__((destructor))
.В качестве расширения двух других ответов, включая мой собственный , вот однострочный скрипт Linux bash, который вы можете использовать для автоматического расчета FLASH
и SRAM
использования.
Это предполагает, что ваша команда для просмотра информации о размере Berkeley вашего файла .elf имеет формат arm-none-eabi-size "STM32F103RB_Nucleo.elf"
. Обновите этот путь до интересующего вас файла .elf. Если вы ищете файлы Linux .elf или обычные исполняемые файлы вместо файлов .elf STM32, используйте size
вместо arm-none-eabi-size
. Пример: size path/to/my_program.elf
.
В любом случае, вот сценарий:
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"
Предполагая, что arm-none-eabi-size "STM32F103RB_Nucleo.elf"
это показывает:
text data bss dec hex filename
2896 12 1588 4496 1190 STM32F103RB_Nucleo.elf
... вот пример запуска вышеуказанного скрипта:
FLASH used = 2908 bytes
SRAM used by global variables = 1600 bytes
FLASH = text + data
SRAM = bss + data
text
= данные программы + вектор ISR + родата (данные только для чтения: const
статические constexpr
и глобальные переменные только для чтения)data
= НЕнулевые инициализированные (т.е. инициализированные числом, отличным от нуля) статические и глобальные переменные, + .init_array + .fini_arraybss
= инициализированные нулями статические и глобальные переменные + пространство стека и кучи, выделенное сценарием компоновщикаДля получения более подробной информации см. Мой другой, очень подробный ответ здесь: Как мне узнать во время компиляции, сколько флэш-памяти STM32 и динамической памяти (SRAM) израсходовано?
awk
для удаления первой строки текстового блоба: https://superuser.com/questions/284258/remove-first-line-in-bash/284270#284270awk
пример из моих собственных заметок , чтобы напомнить мне, как его использовать:du -h | tail -n 1 | awk '{print $1}'
Крис Стрэттон
Габриэль Стейплс