STM8–Выполнение кода из ОЗУ
Рассмотрим как реализовать выполнение программного кода из ОЗУ работая с компилятором Cosmic.
Пример
Самый простой способ выделить область памяти и объявить её отдельной секцией.
Для этого правим файл компоновщика (*.lkf).
Вот типичный пример распределения памяти в простом проекте:
# ОЗУ
+seg .bsct -b 0x0 -m 0x100 -n .bsct
+seg .ubsct -a .bsct -n .ubsct
+seg .bit -a .ubsct -n .bit -id
+seg .data -b 0x100 -m 0x4ff -n .data
+seg .bss -a .data -n .bss
Что есть что:
секция | назначение |
bsct | так называемая “нулевая страница”, предназначена для инициализируемых переменных. Переменные значение которых требуется инициализировать до запуска основного кода. “Нулевая страница” вероятно из-за расположения в памяти, всегда должна быть первой. |
ubsct | предназначена для переменных не требующих начальной инициализации. |
bit | предназначенная для переменных размеров в один бит. |
data | предназначена для инициализируемых переменных. |
bss | предназначена для переменных не требующих начальной инициализации. |
Как видим у последних двух секций назначение совпадает с предыдущими, это связанно с различием в адресации. Для адресации всех данных в нулевой странице достаточно одного байта (short range), для секций data и bss, требуется уже два байта (long range).
Размер секции, начальный адрес и прочие опции определяется с помощью ключей:
+seg | добавляем секцию |
.bsct | имя секции (точка в начале имени обязательна) |
-b 0х00 | начальный адрес секции |
-m 0x100 | размер секции |
-n .bsct | имя секции (как я понял по этому имени секция доступна извне) |
Так как нам заранее не известен размер секций (bsct, ubsct, bit), самым простым способом оказалось объявление размера у одной (bsct) и добавление последующих двух (ubsct, bit) в неё. (ключ -a).
Аналогично для секций data и bss.
Код предпочтительно размещать за пределами нулевой секции.
Выделим кусочек для нашего кода:
+seg .data -b 0x100 -m 0x4ff -n .data
+seg .in_ram -a .data -n .in_ram -ic
+seg .bss -a .erase_in_ram -n .bss
Для экономии места я просто добавил секцию in_ram без жесткой привязки к памяти.
Ключик –ic помечает указывает что сегмент предназначен для перемещаемого кода, т.е. после компиляции мы будем иметь нужный нам код во флеш-памяти, который нужно будет скопировать в ОЗУ перед использованием.
Для тех кто работает с STVD, все ещё проще нужно просто прописать секцию в настройках проекта:
Место выделено, теперь нужно “обрамить” требуемые функции следующим образом:
#pragma section (in_ram)
void run_in_ram(void)
{
}
#pragma section ()
и перед вызовом скопировать код из флеш-памяти в ОЗУ:
_fctcpy('i'); // перед именем подчерк, мой подсветчик кода глючит ;(
Это библиотечная функция предназначена специально для перемещаемого кода, в качестве её аргумента необходимо указать первую букву от названия секции, в данном случае это буква “i” от имени in_ram.
Для использования библиотечной функции нужно сделать её объявление в проекте:
extern int _fctcpy(char name_section);
Функция возвращает ноль при успешном копировании, и не нулевое значение при ошибке.
Ну вот собственно и все.
Как видим все очень просто, если вы свободно владеете английским, к сожалению я такими способностями не обладаю ;( поэтому возможно ошибаюсь в некоторых моментах.
Алгоритм работы описан в документации к компилятору:
C Cross Compiler User’s Guide for ST Microelectronics STM8
в разделе “Перемещаемый код” (Moveable Code).
Стоит заметить что нужно быть внимательным при выполнении кода из ОЗУ, так как если в коде используются библиотечные функции, то будем иметь смешанный код. При чем сами того не замечая мы очень часто используем библиотечные коды, например работа с переменными размером 32 бита требует библиотечные функции (сложение, вычитание и т.п.).
Аналогичным образом реализована работа с кодом в ОЗУ у GCC, используемого мной в работе с микроконтроллерами STM32.
При выполнении кода из ОЗУ как правильно микроконтроллер потребляет меньший ток. Так же при некоторых операциях с памятью (флеш, EEPROM) необходимо выполнение кода из ОЗУ.