
" На всё есть свой конфиг. "
" Код отдельно, конфиги -отдельно "
« Лучшее программирование — конфигурирование »
В чем проблема?
В программировании микроконтроллеров при каждой новой компиляции адреса глобальных констант всегда оказываются в новых ячейках NOR Flash памяти. Из-за этого приходится каждый раз снова и снова редактировать XDF файл для утилиты TunerPRO.
В программировании микроконтроллеров порой надо сделать так, чтобы после сборки артефактов в прошивке глобальные константы оказались в строго заданных адресах NOR Flash памяти. Причем при полной пересборке проекта эти адреса оставались прежними.
Это особенно полезно если требуется конфигурационные и калибровочные данные выделить в отдельный интервал Flash памяти.
Потом это требование международного стандарта программирования ISO-26262.
Делается это для того, чтобы появилась возможность менять значения в этом интервале утилитой TunerPRO перед прошивкой бинаря.
Фиксированные адреса позволят вам всегда иметь один и тот же xdf файл для утилиты TunerPRO.
Определения
Компилятор - это консольная программа преобразующая исходный код в машинный, который затем может быть выполнен процессором или другой программой (виртуальной машиной). По сути компилятор это и есть программирующая программа. А весь ваш исходный Си-код это просто текстовый комментарий для компилятора.
Компоновщик - это консольная утилита, которая производит компоновку (линковку): принимает на вход один или несколько бинарных файлов с машинным кодом (*.o) плюс текстовый файл со скриптом компоновки(*.ld) и собирает из них финальный исполняемый файл для загрузки в микроконтроллер (*.elf). Собирает образ flash памяти для микропроцессора.
Решение
Чтобы дать понять компилятору и компоновщику, что надо брать в оборот особенную секцию надо внести изменения в двух местах. В настройках для компилятора и в настройках для компоновщика. Настройки для компилятора это не что иное как исходный код (сорцы). В случае с GCC есть спец слово __attribute_ в котором можно указать секцию в которую мы собираемся поместить ту или иную переменную. В коде это выглядит так.
#define CALIB_DATA_SECTION __attribute__((section (".CalibrationDataSection"))) const char CALIB_DATA_SECTION tuner_pro_text[16] = "test_data"; const uint8_t CALIB_DATA_SECTION tuner_pro_u8 = 0x12; const uint16_t CALIB_DATA_SECTION tuner_pro_u16 = 0x1234; const uint32_t CALIB_DATA_SECTION tuner_pro_u32 = 0x12345678; const int8_t CALIB_DATA_SECTION tuner_pro_s8 = -8; const int16_t CALIB_DATA_SECTION tuner_pro_s16 = -16; const int32_t CALIB_DATA_SECTION tuner_pro_s32 = -32; const float CALIB_DATA_SECTION tuner_pro_float = 5.5; const double CALIB_DATA_SECTION tuner_pro_double = 7.7;
Второе изменение на до сделать в настройках компоновщика. В случае с GCC это LD файлы. Сначала надо определить кусок памяти и назвать его CALIBRATION_DATA. Задать физический адрес и размер. Я программирую STM32F401RE и на всё у меня 512kByte flash памяти. Калибровочные данный я буду размещать в самом конце доступной памяти. Буквально в последнем килобайте NOR flash-a.
/* Specify the memory areas */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 511K CALIBRATION_DATA (r) : ORIGIN = 0x807FC00, LENGTH = 1K }
Затем надо определить секцию CalibrationDataSection и прикрепить её к разделу физической памяти.
.fill_flash : { . = ALIGN(4); _fill_start = .; /* Fill the remaining space in FLASH with 0xFF */ FILL(0xFF); . = ORIGIN(FLASH) + LENGTH(FLASH) - 1; /* Move location counter to end of FLASH */ BYTE(0xFF); /* Ensure the very last byte is also filled */ _fill_end = .; } > FLASH /* placing my named section at given address: */ .CalibrationDataSection : { . = ALIGN(4); _calib_data_start = .; KEEP(*(.CalibrationDataSection)) /* keep my variable even if not referenced */ _calib_data_end = .; } > CALIBRATION_DATA
Всё готово. Можно собирать и прошивать. Функция KEEP() не позволять сборщику мусора удалить секцию CalibrationDataSection даже если к ней никто не обращается.
Отладка
Чтобы проверять адреса я накропал вот вспомогательную функцию test_tuner_pro_address.
bool test_tuner_pro_address(void) { bool res = true; LOG_DEBUG(SYS, "%s():", __FUNCTION__); LOG_INFO(SYS, "&tuner_pro_u8:0x%p", &tuner_pro_u8); LOG_INFO(SYS, "&tuner_pro_u16:0x%p", &tuner_pro_u16); LOG_INFO(SYS, "&tuner_pro_u32:0x%p", &tuner_pro_u32); LOG_INFO(SYS, "&tuner_pro_s8:0x%p", &tuner_pro_s8); LOG_INFO(SYS, "&tuner_pro_s16:0x%p", &tuner_pro_s16); LOG_INFO(SYS, "&tuner_pro_s32:0x%p", &tuner_pro_s32); LOG_INFO(SYS, "&tuner_pro_float:0x%p", &tuner_pro_float); LOG_INFO(SYS, "&tuner_pro_double:0x%p", &tuner_pro_double); LOG_INFO(SYS, "&tuner_pro_text:0x%p", tuner_pro_text); return res; }
Первая сборка проекта показывает, что глобальные переменные в самом деле прыгнули в конец памяти (адреса после 0x807FC00).

Затем я беру и пересобираю прошивку. Вызываю make all. Снова вызываю CLI команду для просмотра физических адресов констант

Как можно заметить, адреса глобальных переменных теперь как будто приклеились в те же значения. Это видно даже в бинарном файле начиная с адреса 0x807FC00.

Успех!
Итог
Удалось научиться размещать глобальные константы по фиксированным адресам в физической памяти ARM микроконтроллера. Это открывает прямую дорогу для варьирования калибровочных или конфигурационных данных утилитой TunerPRO прямо внутри bin файлика без необходимости тотальной пересборки всей прошивки.
Словарь
Акроним | Расшифровка |
GCC | GNU Compiler Collection |
ISO | International Organization for Standardization |
XDF | extensible calibration definition format |
Ссылки
Название | URL |
Defining Variables at Absolute Addresses with gcc | https://mcuoneclipse.com/2012/11/01/defining-variables-at-absolute-addresses-with-gcc/ |
Покраска Cтека (Stack Painting) | |
ARM Cortex-M: Исполнение кода из RAM памяти | |
Почему Нам Нужен UART-Shell? | |
Обзор утилиты TunerPro (или const volatile) | https://habr.com/ru/articles/965828/?ysclid=mi1j35cxrp269814606 |
Вопросы
--Как размещать переменные по жестким адресам в других компиляторах: IAR, TCC, GHS, Clang и т. п.?
--Как принудительно прописать в 0xFF байты неиспользуемой FLASH памяти?
