В данной статье рассмотрена установка и настройка среды Eclipse для работы с микроконтроллером MDR12065.
Содержание статьи:
1. Установка и подготовка к работе IDE Eclipse
2. Подготовка отладчика для работы с OpenOCD
3. Создание и настройка проекта в IDE Eclipse
4. Сборка проектов
5. Создание и настройка конфигурации отладки
6. Программирование загрузочной программы в область BOOT FLASH
7. Дополнительные возможности
1. Установка и подготовка к работе IDE Eclipse
Скачать IDE Eclipse можно по следующей ссылке:
https://projects.eclipse.org/projects/iot.embed-cdt/downloads
Нужна версия Eclipse для встраиваемых систем (Eclipse Embedded CDT).
Помимо самой среды Eclipse, также понадобятся следующие программные средства:
1.
GNU RISC-V Embedded GCC – набор программ (компилятор, ассемблер, компоновщик, библиотеки, отладчик GDB), которые необходимы для разработки ПО на устройствах с ядром RISC-V.
2.
Windows Build Tools – набор программ (make, rm), которые необходимы для сборки и очистки проекта в среде Eclipse.
3.
OpenOCD – ПО для программирования и отладки устройств. OpenOCD для ОС Windows, собранный с поддержкой МК MDR12065 доступен для скачивания на
официальном сайте компании «Миландр», а также в конце статьи в разделе «Программное обеспечение».
Для работы среды разработки Eclipse в ОС Windows также требуется JRE (Java Runtime Environment) - комплекс ПО, необходимый для запуска Java-приложений. Поэтому, если запуск Eclipse не выполняется, то необходимо скачать и установить JRE. Скачать JRE можно с официального сайта:
https://www.oracle.com/java/technologies/javase-jre8-downloads.html.
При первом запуске среды, исполняемый файл которой расположен в архиве по пути eclipse/eclipse.exe, появится окно с выбором директории, в которой будет создан workspace (рисунок 1).

Рисунок 1 – Выбор директории в качестве workspace
Далее в настройках надо указать пути для подключения программных средств для работы с МК MDR12065. Для этого следует открыть окно «Window->Preferences» и п
ерейти во вкладку «MCU».
При работе под ОС Windows во вкладке «MCU->Global Build Tools Path» необходимо прописать путь к папке bin xPack Windows Build Tools (рисунок 2).

Рисунок 2 – Указание пути к xPack Windows Build Tools
Во вкладке «MCU->Global OpenOCD Path» необходимо прописать путь к папке с исполняемым файлом OpenOCD (рисунок 3). Данный путь используется в отладочных конфигурациях.

Рисунок 3 – Указание пути к OpenOCD
Во вкладке «MCU->Global RISC-V Toolchains Paths», необходимо выбрать toolchain по умолчанию «xPack GNU RISC-V Embedded GCC (riscv-none-elf-gcc)» и прописать путь к папке bin xPack GNU RISC-V Embedded GCC (рисунок 4).

Рисунок 4 – Указание пути к GNU RISC-V Embedded GCC
После указания всех путей необходимо нажать кнопку «Apply and Close».
На этом подготовка среды Eclipse завершена.
При возникновении проблем далее при сборке проектов или запуске OpenOCD рекомендуется перепроверить корректность используемых программных компонентов и путей к ним.
2. Подготовка отладчика для работы с OpenOCD
МК MDR12065 поддерживает отладку только через интерфейс JTAG.
2.1. Подготовка платы
При работе с отладочным комплектом для микросхем MDR12065 ТСКЯ.469575.049 возможно два варианта подключения по JTAG:
- С использованием установленной на плату микросхемы FT2232HL, интерфейс 0 которой выполняет функцию JTAG для отладки микросхем MDR12065. При таком использовании достаточно подключить кабель USB A-B к ПК и к отладочной плате, дополнительный внешний отладчик-программатор не требуется. Дополнительно, интерфейс 1 микросхемы FT2232HL позволяет выполнять обмен данными с МК по интерфейсу UART1 на выводах PB[1], PB[0].
- С использованием внешнего отладчика-программатора. Например, в качестве внешнего отладчика можно использовать J-Link.
Выбор используемого отладчика на плате ТСКЯ.469575.049 задаётся с помощью DIP-переключателя на разъёме JTAG to USB:
- переключатели установлены в положение ON - используется встроенный отладчик, реализованный на базе микросхемы FT2232HL;
- переключатели в положении OFF – используется внешний отладчик.
Не допускается подключение внешнего отладчика к отладочной плате ТСКЯ.469575.049 при переключателях, установленных в положение ON, на разъёме JTAG to USB.
2.2. Установка драйверов отладчика
Для корректной работы отладчика с OpenOCD в Windows необходимо переустановить стандартные драйвера отладчика на WinUSB. Это можно сделать с помощью программы Zadig, подробнее об этом написано на сайте: https://xpack.github.io/openocd/install/, пункты «Drivers» и «Zadig».
Следует обратить внимание, что переустановка драйверов для внешнего отладчика делает его совместимым с OpenOCD, но при этом другие ПО (например, Keil, IAR, утилиты J-Link), использующие оригинальные драйвера, не смогут работать с отладчиком на данном ПК. Чтобы вернуть драйвера отладчика на ПК в исходное состояние, необходимо удалить драйвер Zadig и переустановить драйвер производителя.
Поэтому, чтобы не переустанавливать оригинальные драйвера для внешнего отладчика, рекомендуется использовать встроенный отладчик, установленный на отладочной плате.
Для встроенного отладчика также требуется переустановить драйвера. Для этого необходимо скачать программу Zadig с сайта: https://zadig.akeo.ie/, после чего запустить её с правами администратора. Далее следует включить питание на отладочной плате и подключить с помощью кабеля USB A-B отладочную плату к ПК. В программе Zadig в меню «Options» требуется выбрать пункт «List All Devices» - это позволит отобразить все устройства USB, подключенные к ПК. Как показано на рисунке 5, в списке устройств необходимо выбрать «Dual RS232-HS (Interface 0)», выбрать драйвер «WinUSB (v6.1.7600.16385)» (выбран по умолчанию), и нажать кнопку «Replace Driver». Через некоторое время программа выведет окно с сообщением о завершении установки драйвера.

Рисунок 5 – Переустановка драйвера для «Dual RS232-HS (Interface 0)» в Zadig
Драйвер устройства «Dual RS232-HS (Interface 1)» переустанавливать не нужно, так как это устройство используется для обмена данными с МК по интерфейсу UART.
Переустановка драйвера завершена, программу Zadig можно закрыть.
3. Создание и настройка проекта в IDE Eclipse
Для начала работы необходимо скачать архив с библиотекой SPL (Standard Peripherals Library) с официального сайта компании «Миландр».
Для поддержки МК MDR12065 требуется файл «Standard Peripherals Library для микросхем в металлокерамическом корпусе».
Для быстрого старта можно использовать шаблон проекта «MDR12065_Template», расположенный в скачанном архиве по пути ExampleProjects\MDR12065\Eclipse. В этом шаблоне уже настроены основные параметры проекта и подключена библиотека SPL, поэтому можно сразу перейти к сборке без дополнительной начальной конфигурации.
3.1 Создание проекта на основе шаблона
Чтобы добавить проект в workspace, необходимо импортировать проект, нажав «File -> Import». В открывшемся окне выбрать «General -> Existing Projects into Workspace» и нажать «Next», как показано на рисунке 6.

Рисунок 6 – Добавление проекта в workspace
В поле «Select root directory» необходимо указать путь до проекта. Далее в «Projects» установить галочку напротив выбранного проекта, а также установить пункт «Copy projects into workspace», чтобы проект был скопирован в workspace, если проект расположен не в папке workspace. Далее следует нажать «Finish». При появлении окна «Overwrite '.settings' in folder ...» - нажать «Not To All» (рисунок 7).

Рисунок 7 – Выбор проекта для импорта
Чтобы отобразилось основное окно проекта, необходимо закрыть стартовое окно «Welcome». Слева в поле «Project Explorer» должен отобразиться добавленный проект (рисунок 8).

Рисунок 8 – Отображение проекта в «Project Explorer»
Добавленный проект можно переименовать. Для это следует нажать ПКМ на имя проекта и выбрать пункт «Rename» (или нажать F2). После переименования следует изменить папку ресурсов. Для этого необходимо перейти в свойства проекта (ПКМ на имя проекта -> Properties или нажать «Alt+Enter»). В открывшемся окне необходимо перейти в «C/C++ Build -> Refresh Policy» и удалить папку со старым названием проекта, как показано на рисунке 9. Следует учитывать, что переименование проекта вызовет необходимость изменения предварительно сохраненных конфигураций отладки (см. Использование предварительно сохраненных файлов отладочных конфигураций).

Рисунок 9 – Удаление папки со старым названием проекта
Теперь необходимо нажать «Add Resourse» и выбрать папку с новым названием проекта (рисунок 10). Далее следует нажать «ОК» и «Apply and Close».

Рисунок 10 – Добавление папки с новым названием проекта
Добавление проекта в IDE Eclipse завершено.
3.2 Создание нового проекта с нуля
Для создания нового проекта надо выбрать пункт меню «File -> New -> C/C++ Project» (рисунок 11).

Рисунок 11 – Создание нового проекта
В открывшемся окне следует выбрать «Classic C++ Project» и нажать «Next» (рисунок 12).

Рисунок 12 – Выбор шаблона проекта
В следующем окне надо задать название проекта, выбрать тип проекта: «empty project» и в разделе «toolchains» указать «Risc-V Cross GCC» (рисунок 13).

Рисунок 13 – Настройка типа проекта и выбор компилятора
Далее в окне выбора конфигурации параметры пока можно оставить без изменений (их настройка выполняется в пункте Создание и настройка конфигурации отладки) и нажать «Next».
При корректно выполненной предварительной настройке путей (см. пункт Установка и подготовка к работе IDE Eclipse), в окне выбора «toolchain» должен быть автоматически выбран «xPack GNU RISC-V Embedded GCC» и путь до него (рисунок 14).
После проверки настроек необходимо нажать «Finish».

Рисунок 14 – Указание пути до исполняемого файла компилятора
Следующим шагом выполняется подключение к проекту библиотеки SPL. Библиотека может располагаться как внутри директории проекта, так и вне ее. Размещение библиотечных файлов внутри каталога проекта повышает переносимость, однако при наличии нескольких проектов приводит к дублированию файлов. В данной статье описывается вариант хранения SPL внутри директории проекта, при хранении за ее пределами последовательность действий аналогична, отличается только указываемыми в настройках проекта путями.
В корневом каталоге проекта нужно создать папку Libraries, а затем добавить в нее две директории Libraries/CMSIS и Libraries/SPL (рисунок 15).

Рисунок 15 – Создание структуры каталогов Libraries, CMSIS, SPL
Затем следует открыть архив с библиотекой SPL, скопировать содержимое каталога Libraries/SPL/MDR12065 в папку Libraries/SPL внутри нашего проекта и аналогичным образом перенести содержимое директории Libraries/CMSIS/MDR12065.
Далее требуется настроить пути к библиотечным файлам (рисунок 16). Для этого необходимо открыть свойства проекта и перейти в раздел «C/C++ General -> Path and Symbols». Во вкладке «Includes» следует добавить шесть директорий, указывая для каждой путь и устанавливая галочки «Add to all configurations», «Add to all languages» и «Is a workspace path»:
/${ProjName}/Libraries/CMSIS/DeviceSupport
/${ProjName}/Libraries/CMSIS/DeviceSupport/inc
/${ProjName}/Libraries/CMSIS/DeviceSupport/src
/${ProjName}/Libraries/CMSIS/CoreSupport/inc
/${ProjName}/Libraries/SPL/
/${ProjName}/Libraries/SPL/inc

Рисунок 16 – Настройка путей к библиотечным файлам
После этого нужно перейти во вкладку «Library Paths» (рисунок 17) и нажать «Add», затем указать путь /${ProjName}/Libraries/CMSIS/DeviceSupport/src/gcc, установить те же галочки, что и на предыдущей вкладке и нажать «OK».
Для применения настроек следует нажать «Apply and Close» и обновить дерево проекта, нажав «Refresh» в контекстном меню или «F5».

Рисунок 17 – Настройка Library Paths
В дереве проекта должна появиться папка Libraries со всеми скопированными файлами (рисунок 18).

Рисунок 18 – Дерево проекта
Начиная с версии 2.1 системы команд RV32I инструкции FENCE.I и CSR-инструкции вынесены в отдельные расширения - Zifencei и Zicsr соответственно. В то же время ядро BM-310S0 реализует систему команд RV32I версии 2.0, когда данные инструкции являлись частью базовой системы команд. В связи с этим в настройках GNU RISC-V Embedded GCC проекта необходимо указать использование расширений Zicsr и Zifencei. Помимо этого, согласно п.11 «Система команд» спецификации, поддерживаются расширения команд для битовых манипуляций и для скалярной криптографии.
Настройка расширений осуществляется в настройках проекта (Workspace -> Properties -> C/C++ Build -> Settings -> Tool Settings -> Target Processor), в поле «Other extensions» нужна записать «_zicsr_zifencei_zicntr_zba_zbb_zbc_zbs_zbkb_zbkc_zbkx_zknd_zkne_zknh_zksed_zksh», а также выбрать остальные значения в соответствии с рисунком 19.

Рисунок 19 – Настройка сборки
Инструкции работы с CSR-регистрами используются на уровне CMSIS. Без указания использования расширений Zifencei и Zicsr сборка проекта будет завершаться, например, с ошибками вида «Error: unrecognized opcode `csrr s10,mhartid'» и аналогичными ошибками.
Расширение _zicntr поддерживается в xPack GNU RISC-V Embedded GCC с версии v14.2.0-2 - при использовании более ранних версий xPack GNU RISC-V Embedded GCC расширение _zicntr в настройках следует убрать (в старых версиях не вынесено в отдельное расширение).
Далее в разделе «GNU RISC-V Cross C++ Linker -> General» необходимо выбрать линковочный скрипт в зависимости от того, где будет находиться и выполняться программа в памяти микроконтроллера:
- для записи и исполнения во Flash-памяти следует указать файл «link_FLASH.ld»;
-
для записи и исполнения программы в ОЗУ следует указать файл link_RAM_TCM_debug.ld;
-
для записи программы во Flash-память, но её исполнения из ОЗУ следует указать файл link_RAM_TCM_release.ld.
Эти файлы расположены в каталоге Libraries/CMSIS/DeviceSupport/src/gcc, поэтому, так как данная папка ранее была добавлена в Library Paths, достаточно указать только имя линковочного скрипта без полного пути.
Также необходимо установить флаг «Do not use standard start files (-nostartfiles)» (рисунок 20).

Рисунок 20 – Настройка параметров линковщика
Затем в подразделе «Miscellaneous» в поле «Linker flags» следует добавить «--enable-non-contiguous-regions» (рисунок 21).

Рисунок 21 – Добавление флага «--enable-non-contiguous-regions»
4. Сборка проектов
Компиляция проекта под выбранную конфигурацию в GUI осуществляется с помощью кнопки в виде молотка на панели инструментов (выделен зеленым на рисунке 22). Нажав на стрелку рядом, можно выбрать конфигурацию сборки. Значок документа (выделен синим) компилирует активные конфигурации в каждом открытом проекте (Сtrl+B).

Рисунок 22 – Кнопки для сборки проекта
По умолчанию в проектах реализован набор разных видов сборки, которые отличаются настройками конфигурационного файла компоновщика (link_*.ld):
- "FLASH" - код расположен во Flash-памяти, данные - в ОЗУ на системной шине TCM;
- "RAM_TCM_Debug/_Release" - код и данные расположены в ОЗУ на системной шине TCM.
Отличия между Debug/Release конфигурациями для сборки под области RAM заключается в том, что для конфигурации Debug код изначально располагается сразу в ОЗУ (в связи с этим существует особенность с отладкой в части опции "Debug in RAM", описанная далее) и его загрузка в память возможна только отладчиком-программатором (при штатном запуске МК после сброса или подачи питания программа не будет исполняться, т.к. не записана в энергонезависимую память). В то же время в конфигурации Release код изначально располагается в Flash-памяти и копируется в ОЗУ с помощью startup-файла, в связи с чем исполнение загруженного ПО при штатном запуске МК возможно.
Для каждой конфигурации сборки используется свой конфигурационный файл компоновщика link_*.ld. Отличие данных link_*.ld файлов заключается в различном описании регионов памяти (блоки REGION_ALIAS для разных конфигураций).
5. Создание и настройка конфигурации отладки
При первоначальном ознакомлении для предотвращения проблем, вызванных неправильными настройками, рекомендуется использовать отладочные конфигурации, которые уже сохранены в проектах - в них все необходимые настройки выполнены корректно.
5.1. Создание новых конфигураций отладки
Примечание: В приложенных проектах созданы готовые для использования шаблоны файлов конфигурации (см. "Использование предварительно сохраненных файлов отладочных конфигураций"). Приведенные в текущем пункте настройки нужны для создания новых или модификации имеющихся отладочных конфигураций.
После сборки проекта для создания конфигурации отладки необходимо нажать на стрелку у значка "жука" и выбрать пункт "Debug Configurations…" (рисунок 23).

Рисунок 23 – Расположение кнопки Debug Configurations
В открывшемся окне слева необходимо выбрать пункт "GDB OpenOCD Debugging" и нажать кнопку "New", как показано на рисунке 24.

Рисунок 24 – Настройки окна Debug Configurations
Во вкладке "Main" необходимо выбрать требуемый проект, при этом также можно изменить название отладочной конфигурации. Для выбора проекта необходимо нажать кнопку "Search Project" и выбрать соответствующий elf-файл (рисунок 25).

Рисунок 25 – Конфигурация отладки во вкладке Main
Во вкладке "Debugger" в поле "Config options" необходимо указать файлы конфигурации отладчика (рисунок 26):
|
On-board FTDI2232HL
|
-f interface/ftdi/mdr-ft2232hl.cfg -f target/MDR12065.cfg
|
|
J-Link
|
-f interface/jlink.cfg -f target/MDR12065.cfg
|
При использовании данных файлов конфигурации отладчика для повышения скорости обмена между отладчиком и микросхемой при начале отладки в качестве источника системной частоты микросхемы отладчиком настраивается частота HSI без предделителей. По сбросу предварительный делитель активен и, если системная частота не настраивается в ПО, будет видна разница между скоростью работы ПО без отладки и скоростью работы ПО при использовании отладки. Для работы на требуемой частоте, чтобы разница между штатной работой и работой под отладкой не наблюдалась, необходимо настроить частоты в используемой программе микроконтроллера.
В случае необходимости прошивки загрузочной программы при запуске отладки в поле "Config options" можно дополнительно после подключения файла конфигурации прописать команду программирования BOOT загрузочной программой:
-c "program bootloader/MDR1206_Bootloader.hex preverify verify"
Данная команда запрограммирует образ типовой загрузочной программы MDR1206_Bootloader.hex (описание типовой загрузочной программы приведено в спецификации) в область BOOT.
В поле "Commands" необходимо указать следующие команды:
set mem inaccessible-by-default off
set arch riscv:rv32
set remotetimeout 250

Рисунок 26 – Конфигурация отладки во вкладке Debugger
Во вкладке "Startup" необходимо убрать галочку с пункта "Enable Arm semihosting".
ВАЖНО!
Опция "Debug in RAM" указывает отладчику при каждом запуске отладки выполнять загрузку программы, а также принудительно устанавливать начальный адрес программы (функция _start из файла startup). Данную опцию необходимо устанавливать в двух случаях:
- выполняется отладка программы, загруженной в память ОЗУ (конфигурации сборки "RAM_TCM_Debug");
- используется микросхема без загрузочной программы (загрузочная программа не запрограммирована в область BOOT) и выполняется отладка программы, загруженной во Flash-память (конфигурация сборки "FLASH").
Когда галочка убрана с "Debug in RAM", то после загрузки программы отладчик установит точку останова на функцию main() и сбросит МК. МК стартует с загрузочной программы, которая передаёт управление во Flash-память (режим MODE[1:0] = 00 для стандартной загрузочной программы). Когда выполнение программы дойдёт до точки останова в main(), то МК остановится, а в окне отладки появится синяя стрелочка напротив main(), указывающая, что МК остановлен.

Рисунок 27 – Конфигурация отладки во вкладке Startup
Во вкладке "SVD Path" необходимо указать путь к SVD-файлу, описывающему периферию МК, рисунок 28. SVD-файл также расположен локально в приложенных проектах, в папке svd.

Рисунок 28 – Конфигурация отладки во вкладке SVD Path
5.2. Использование предварительно сохраненных файлов отладочных конфигураций
В приложенных проектах файлы отладочных конфигураций под все конфигурации сборок для отладки с помощью FTDI2232HL уже реализованы (рисунок 29). Отладочные конфигурации сохранены в проектах в папке launch_cfg и автоматически добавляются в список отладочных конфигураций в окне "Debug Configurations" после импорта проекта (изменять имя проекта при этом не рекомендуется - иначе руководствоваться примечанием ниже). При необходимости настройки могут быть скорректированы (например, при использовании другого отладчика и образа собранной программы для загрузки) согласно инструкциям, приведенным в пункте выше.
Дополнительно следует отметить, что в отладочных конфигурациях для FLASH-памяти в них прописана команда программирования образа загрузочной программы MDR1206_Bootloader.hex из папки bootloader. При каждом запуске отладки с командой программирования образа MDR1206_Bootloader.hex осуществляется проверка на наличие загрузочной программы в памяти и ее запись, если контрольная сумма не совпадает (загрузочная программа не запрограммирована или в памяти по этим адресам другие данные).

Рисунок 29 – Предварительно созданные отладочные конфигурации
Если проект был переименован, сохраненные отладочные конфигурации не будут работать, т.к. в их структуре содержится информация о названии проекта и собираемого elf-файла. Для того, чтобы они работали с переименованным проектом, необходимо каждый файл отладочной конфигурации .launch открыть как текстовый файл (ПКМ по файлу .launch, в контекстном меню "Open with" -> "Text Editor"), найти все старые названия проекта (Ctrl+F) и заменить на новые. Также рекомендуется изменить названия проектов на новые в названии файлов .launch. Для упрощения в проектах в папке с отладочными конфигурациями добавлен файл rename-launch-configurations.bat для применения этих изменений.
5.3. Запуск отладки
Перед включением блока питания в сеть необходимо проверить, что переключатель "Power" находится в положении "Off". Только после включения блока питания в сеть можно включать питание на плате: "Power" -> "On".
Для запуска отладки необходимо включить питание отладочной платы, подключить кабель USB A-B от платы к ПК, после чего нажать кнопку "Debug" в окне "Debug Configuraions" при выбранной отладочной конфигурации.
6. Программирование загрузочной программы в область BOOT FLASH
Микросхема MDR12065 поставляется запрограммированной загрузочной программой. Загрузочная программа в микроконтроллере расположена в области BOOT (информационной области Flash-памяти) и может быть стерта или перезаписана при использовании. Рекомендуется всегда использовать актуальную версию загрузочной программы с
сайта.
Скомпилированный образ описанного в спецификации примера загрузочной программы доступен в составе приложенных проектов в папках bootloader - файл MDR1206_Bootloader.hex. Данный файл является общим для микросхем MDR12065, MDR1206FI и MDR1206AFI.
Для программирования загрузочной программы в область BOOT можно скомпилировать любой из примеров проектов, выбрать прилагаемую отладочную конфигурацию "FLASH with Bootloader FTDI2232HL" и запустить отладку (следует обратить внимание, что конфигурация настроена именно для использования микросхемы-отладчика FTDI2232HL). Загрузка образа типовой загрузочной программы перед загрузкой программы проекта осуществляется с помощью прописанной в настройках отладочной конфигурации команды '-c "program bootloader/MDR1206_Bootloader.hex preverify verify"' для OpenOCD.
Если требуется, например, выбрать другой отладчик или отключить загрузку образа основной программы проекта, необходимо изменить настройки отладочной конфигурации согласно пункту
"Создание новых конфигураций отладки".
7. Дополнительные возможности
При разработке программы имеется возможность использовать дополнительные настройки проекта, такие как размещение функций в ОЗУ, размещение данных в AHB RAM для контроллера DMA, использование функций для работы с динамической памятью. Подробнее об этом описано в пунктах ниже.
7.1 Размещение функций в ОЗУ
В конфигурационных ld-файлах компоновщика добавлен ряд секций памяти. Данные секции также прописаны в файле system_MDR12065_sections.h на уровне CMSIS (описание секций приведено в h-файле). Если требуется разместить функцию в ОЗУ (при исполнении startup они будут скопированы из Flash-памяти в TCM RAM), для этого нужно добавить в объявлении функции определение __RAM_TEXT или __RAM_TEXT_NOINLINE. Пример размещения функции foo() в ОЗУ приведён во фрагменте кода 1.
Фрагмент кода 1 - Размещение функции foo() в ОЗУ
__RAM_TEXT_NOINLINE void foo(void);
Необходимо обратить внимание, что точки останова, которые проставлены в функции, расположенной в ОЗУ, могут работать некорректно и вызывать исключение "illegal instruction". Это происходит из-за того, что отладчик в таких функциях ставит программные точки останова до их инициализации.
В результате возникает следующая ситуация. При запуске отладки МК сбрасывается. После сброса МК содержимое ОЗУ, в котором будет находится требуемая функция, неопределенно. Отладчик до копирования функции из Flash в ОЗУ ставит в ОЗУ, где, как он предполагает, уже размещена функция, программную точку останова. Программная точка останова предполагает перезапись инструкции специальной командой, при этом инструкция, расположенная на месте точки останова, запоминается отладчиком. Так как точка останова ставится до копирования функции из Flash в ОЗУ, то отладчик запоминает данные неинициализированной ОЗУ, т.е. "мусор". После остановки в функции main(), когда ОЗУ уже проинициализировано, отладчик восстанавливает инструкции на местах точек останова, тем самым записывая в ОЗУ сохранённый ранее "мусор". Реальная инструкция будет утрачена.
Таким образом, не рекомендуется перед запуском отладки устанавливать или активировать точки останова, которые находятся в функциях, расположенных в ОЗУ. После остановки в main(), когда ОЗУ уже проинициализировано, можно устанавливать точки останова в любую область программы.
При работе с микроконтроллером может также потребоваться перенос функции в ОЗУ, например, из Flash-памяти не на этапе сборки проекта, а прямо по ходу исполнения программы. Что необходимо учитывать в таком случае?
При рассмотрении данного вопроса мы говорим о том, что функция, находящаяся во Flash-памяти, должна быть скопирована по адресам памяти ОЗУ с соблюдением следующих требований:
- Выравнивание функции, которая будет перенесена из Flash-памяти в память ОЗУ. На уровне объявления прототипа функции выравнивание достигается объявлением атрибута
__attribute__((aligned(4)))
- Мы должны гарантировать, что при использовании функции memcpy() для копирования функции из Flash-памяти в память ОЗУ функция копируется целиком, то есть верно задан аргумент "size". Контрольная сверка должна выполняться по .map файлу собранного проекта.
- После выполнения операции копирования функции в память ОЗУ необходимо для надежности вызвать инструкции "fence" и "fence.i" при помощи ассемблерных вставок:
__asm__ volatile ("fence iorw, iorw" ::: "memory");
и
__asm__ volatile ("fence.i");
Это нужно, чтобы обеспечить барьерную синхронизацию данных и инструкций.
- Объявление функции, которая будет использоваться для запуска перенесенной функции, необходимо выравнивать аналогично п.1. Закономерно эта функция для осуществления вызова должна представлять из себя функцию-указатель на адрес, куда была перенесена исходная функция. Например, это можно сделать следующим образом:
__attribute__((aligned(4))) void (*ram_func_ptr)(void) = (void (*)(void))func_addr;
ram_func_ptr является названием функции для дальнейшего вызова, а func_addr - адрес, куда была перенесена функция в память ОЗУ.
- Если переносимая функция внутри себя содержит подвызов других функций (для функций, которые в себе не содержат вызовов других функций, данный пункт может не рассматриваться), то из-за нарушения относительной адресации вызов этих функций необходимо реализовывать через "функции-трамплины". Например, переносимая функция моргает светодиодами и содержит в себе две функции: PORT_ToggleBit() и DELAY_WaitMs(). В таком случае для корректного вызова переносимая функция должна вызывать второстепенные функции, объявленные следующим образом:
static void (* volatile delay_wait_ms_ptr)(uint32_t) = DELAY_WaitMs;
static void (* volatile port_toggle_bit_ptr)(MDR_PORT_TypeDef*, uint32_t) = PORT_ToggleBit;
Видим, что "функции-трамплины" ссылаются на оригинальные функции. Соответственно, в переносимой функции для корректной работы используются функции delay_wait_ms_ptr() и port_toggle_bit_ptr().
7.2 Размещение данных в памяти AHB RAM
МК MDR12065 имеет три области ОЗУ: TCMA RAM, TCMB RAM и AHB RAM. Ядро имеет доступ ко всем трём областям ОЗУ, контроллер DMA получает доступ к памяти TCM RAM по шине AHB-Lite (рисунок 30) через арбитр доступа к блокам памяти TCM в ядре.

Рисунок 30 – Доступ к областям ОЗУ со стороны процессорного ядра и контроллера DMA
Интенсивный обмен данными в TCM RAM с помощью DMA может значительно замедлить работу как программы, так и DMA, поскольку ядро и DMA будут работать по одной шине. Поэтому данные, используемые контроллером DMA, предпочтительней размещать в области памяти AHB RAM. Для этого добавлены секции, доступные через определения __RAM_AHB_DATA и __RAM_AHB_BSS, которые предназначены для размещения инициализированных и неинициализированных данных соответственно. В startup-файл добавлен код инициализации данных.
Пример размещения массивов данных в памяти AHB RAM приведён во фрагменте кода 2.
Фрагмент кода 2 - Размещение массивов данных в памяти AHB RAM
__RAM_AHB_BSS int array[100]; // Данные инициализируются нулями, при старте не хранятся в Flash-памяти.
__RAM_AHB_DATA char init_message[] = "Hello World from MDR12065!"; // Данные при старте заполняются из Flash в ОЗУ.
7.3 Использование функций для работы с динамической памятью
Функции для работы с динамической памятью (malloc(), calloc(), realloc(), free()) предоставляет библиотека newlib (реализация стандартной библиотеки C). Библиотека newlib поставляется в двух вариантах - обычная версия (newlib) и компактная версия (newlib-nano). Для использования newlib-nano в настройках проекта "C/C++ Build -> Settings -> GNU RISC-V Cross C++ Linker -> Miscellaneous", необходимо установить опцию "Use newlib-nano" (рисунок 31).

Рисунок 31 – Подключение библиотеки newlib-nano
Библиотека newlib взаимодействует с операционной системой (ОС) через системные вызовы. Реализация системных вызовов по умолчанию вызывает ОС с помощью исключения ecall. Если в проекте не используется ОС, то функции системных вызовов необходимо реализовать самостоятельно. Подробнее про реализацию функций системных вызовов написано на сайте newlib.
Для работы с функциями, которые используют динамическую память, необходимо реализовать функцию _sbrk().