LINUX.ORG.RU

Linker Script для STM32

 ,


1

2

Как известно, чтобы что-то собрать под STM32 нужен linker script для соответствующего микроконтроллера. Можно, конечно, открыть даташит и написать вручную, но это слишком скучно. Можно тырить из чужих проектов готовые скрипты, но это тоже не мой метод.

В конце-концов linker script для всех STM32 одинаковы почти полностью, отличаются только размерами ROM и RAM (а ещё у некоторых STM32 есть несколько блоков RAM). А у меня есть железное правило - «если у тебя в программе дважды встречается один и тот же код - вынеси его в отдельную функцию, чтобы он встречался один раз». В данном случае можно написать шаблон linker script с символьными именами вместо чисел (типа RAM_SIZE, FLASH_SIZE и т. д.), а затем уже при компиляции прогонять шаблон через cpp, объявив нужные параметры МК.

Вопрос в том, где эти параметры взять. Ну не вручную же вести БД в 21-ом веке! Первое что пришло в голову - у STMicroelectronics есть сайт. Там есть онлайн каталог с характеристиками микроконтроллеров. Немного времени и я уже могу скачать в JSON-формате список микроконтроллеров с их характеристиками в локальный файл (чтобы не качать его при каждой компиляции проекта). А ещё написал утилиту, которая парсит этот файл и выдаёт все нужные мне характеристики - Flash, RAM, и версию ядра ARM (чтобы делать параметры компиляции типа -mcpu=cortex-m3).

А в итоге получил непонятный баг - тестовая программа делала HardFault при любом обращении к стеку. В итоге оказалось, что у старших моделей STM32 память состоит из двух регионов - нормальная и CCM (с меньшим временем ожидания чтения, зато недоступная для DMA). Скажем, у STM32F407 первый регион 128 КБ и начинается с адреса 0x20000000 (как и у всех других STM32), второй регион начинается с адреса 0x10000000 и длится 64 КБ. А в каталоге, разумеется, указываются общий размер ОЗУ. И получилось, что я объявил в linker script, что RAM у меня 192 КБ, а не 128, что не было верно. А стек, как известно, находится в конце RAM. Ну и получил обращение по несуществующему адресу.

Проблема осложняется тем, что размер CCM не указан в каталоге, который я использовал, а также нет какого-либо явного закона (типа CCM_SIZE = 1/3 * RAM_SIZE, если у нас STM32F4 - CCM имеют не все STM32F4, зато его же имеют и некоторые STM32F3).

Решение проблемы - нужен другой каталог микроконтроллеров. Где его взять?

Пока на примете есть только эта штука - https://github.com/libopencm3/libopencm3/blob/master/ld/devices.data

Насколько хорошим решением будет использовать данный каталог? Что насчёт лицензии и использования в своём проекте? Возможно, вы знаете более хорошие машиночитаемые каталоги характеристик микроконтроллеров?

★★★★★

Последнее исправление: KivApple (всего исправлений: 3)

Ответ на: комментарий от Eddy_Em

https://github.com/eddyem/stm32samples/tree/master/1_wire/ld

У тебя там просто коллекция ld script, не? Причём только под некоторые микроконтроллеры. Я же веду речь о чём-то более автоматизированном.

Или я не там смотрю?

У меня уже сейчас есть Makefile, которому надо лишь сказать полное имя микроконтроллера (скажем, stm32f103ez), а он генерирует ld script и заголовочные файлы (с описаниями всех регистров всех периферии) на основе SVD-файлов и каталога чипов с сайта STMicroelectronics. Однако на старших моделях STM32 (некоторые STM32F4 и некоторые STM32F3) он создаёт неправильный ld script, потому что не знает про CCM.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

Я же веду речь о чём-то более автоматизированном.

В opencm3 уже есть. Я просто по традиции 103-и таскаю.

Eddy_Em ☆☆☆☆☆
()

В таблице каталога да, указан общий объём RAM. А вот если выбрать определённое подсемейство — то в описаловке уже будет показано разделение, например для STM32F469AG указано "Up to 384+4 KB of SRAM including 64-KB of CCM (core coupled memory) data RAM". Ну и так далее. Качать и парсить придётся больше.

one_more_hokum ★★★
()
Ответ на: комментарий от one_more_hokum

О. Спасибо за наводку, а то я что-то не обратил внимания.

Единственное, что не нравится - что надо хардкодить ссылки.

Сейчас качаю JSON-документ по адресу http://www.st.com/stonline/stappl/productcatalog/jsp/jsonDataForL3.jsp?subcla... - для других семейств скорее всего придётся лишь поменять последнее число (подсмотрев, куда обращается скрипт, на странице каталога). Конечно, новые семейства редко появляются, но с точки зрения перфекционизма было бы круто и их список как-то получать.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от one_more_hokum

А их откуда добываешь?

С arm.com. Правда приходится качать вручную из-за требования авторизации.

Кстати, можешь глянуть на мои потуги.

https://github.com/KivApple/controllerFramework

Я пытаюсь сделать «убийцу Arduino» - набор библиотек, которые с одной стороны сильно упрощают разработку, но с другой не настолько просаживают производительность и огораживают доступ к железу (у меня это реализуется тем, что каждый метод имеет перезагруженные версии с платформоспецифичными параметрами).

Там есть каталог tools с моими скриптами для генерации заголовочных файлов и ld-скриптов.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)

Я тоже задавался подобным вопросом. Хотел даже собирать базу данных с называниями микроконтроллеров (тысячи их) и адресами памяти. Но забил.
Так что если сделаешь что-то рабочее, скастуй меня.

CYB3R ★★★★★
()
Ответ на: комментарий от CYB3R

https://github.com/KivApple/controllerFramework

Это мой проект-убийца ардуины. Можешь посмотреть на текущую систему сборки. В Makefile задаётся лишь имя контроллера (например, stm32f103ze или stm32f407vg), а оно само находит его в базе и получает необходимые параметры сборки.

Причём способно использовать официальный каталог STMicroelectronics (tools/stm32-mcu-info.cpp), но пока мне немного лень и я не добавил использование каталогов специфичных для семейства, только общий (в итоге не учитывается CCM-память для старших микроконтроллеров). Впрочем, на этот случай предусмотрен fallback-вариант - tools/libopencm3-device-info, который использует базу от libopencm3.

Заголовочный файл для МК генерирует программа tools/svd2header.cpp.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

У меня на гитхабе лежит антарес, там помимо stm32 есть еще куча всего. И есть православный kconfig, фреймворк для деплоя прошивок, аут-оф-директори билды и много еще чего. Присоединяйся ;)

Ld для stm32 можешь подглядеть у меня, там генерируется в динамике.

ncrmnt ★★★★★
()
Последнее исправление: ncrmnt (всего исправлений: 2)
Ответ на: комментарий от CYB3R

Там надо сначала выполнить:

make -C tools
tools/update_libopencm3_device_db
tools/update_stm32_catalog

Иначе скриптам сборки будет неоткуда брать информацию.

В каталоге examples лежат различные примеры. Тестировал только на том, что у меня есть - STM32F103 и STM32F407. Модуль USB пока только под STM32F103, но в ближайшее время планирую добавить поддержку STM32F4.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от ncrmnt

Посмотрел. Очень круто.

Однако мой проект имеет несколько иную специфику. Я по максимуму использую C++ и ООП. Например, для моей системы можно написать библиотеку поддержки I2C-расширителя IO, который будет обычными GPIO с точки зрения всех остальных модулей (в том числе его можно будет передать как параметр в качестве пина CE для какой-нибудь NRF24L01). Или внешний АЦП будет ничем не отличаться от внутреннего с точки зрения работы с ним.

При этом когда надо можно достучаться до вещей, особенных для конкретной платформы - помимо реализации методов базового класса каждая реализация периферии добавляет свои собственные методы.

А ещё поддержка Linux. Чтобы можно было собрать проект для Linux-одноплатника с GPIO и прочими интерфейсами и работать с ним как с МК. Разве что риалтаймовость на такой платформе слетит, но она не всегда нужна. Пока, правда на платформе Linux поддерживается только UART, но потом добавлю i2c-dev и GPIO. Благо у меня есть BananaPi, OrangePi и второй CubieBoard.

В далёкой перспективе добавить ко всему этому графический конфигуратор, чтобы можно было настроить периферию (в том числе задать имена объектам) и сохранить в конфигурационный файл. Соответственно, проект сможет иметь несколько конфигураций, выбираемых при сборке, с объектами разных классов (разные реализации одной и той же периферии) и с разными параметрами, но одинаковыми именами. Если основной код будет использовать только методы платформонезависимых классов, то его легко будет собрать под другую плату/МК.

Собственно, сейчас это выражается в примерах в том, что почти вся инициализация периферии МК выполняется в виде глобальных объвлений объектов (параметры передаются конструктору), а основная программа сразу начинает выполнять задачу.

В отличии от Arduino я хочу поддерживать нормальные платформы (ARM) и кастомные платы, а не горстку типовых платок на AVR. Плюс при желании легко дать доступ к аппаратным особенностям. В отличии от конфигураторов типа STMCube кроссплатформенность.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 4)
Ответ на: комментарий от CYB3R

Разумеется, сейчас там всё сырое, за стабильность не ручаюсь (всё что я знаю - что примеры лично у меня компилировались и работали на соответствующих платах как надо). Проекту всего две недели, из которых одну я бился с USB (удачно).

Уже есть поддержка: GPIO, EXTI, USART, I2C, ADC, таймеры, PWM, USB, USB CDC. Что-то на базовом уровне пока, что-то уже на вполне достаточном.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

У меня antares сейчас поддерживает avr, stm32, msp430, 8051(stc, at89, nrf24le1, nrf24lu1), mips (pic32),esp8266 + native режим для unit-тестов. Изначально я именно на переносимость и затачивался. Но вот С++, если хочешь переносимости между MCU это очень плохая затея.

1. На некоторых архитектурах (avr) ВНЕЗАПНО, нет исключений. А оверхед на вызов метода класса такой, что прошивка пухнет как на дрожжах.

2. 8051 хоть и древняя, как дерьмо мамонта, архитектура, на ней есть много «вкусных» камней, типа nrf24le1, nrf24lu1, интерфейсные мсх от cypress. И т.п. А под 8051 вообще нет C++, только С и sdcc. Причем последний не особенно проглатывает конструкции, которые проглатывает gcc, потому чтобы код и там и там собирался иногда приходится попотеть.

Например, для моей системы можно написать библиотеку поддержки I2C-расширителя IO, который будет обычными GPIO с точки зрения всех остальных модулей (в том числе его можно будет передать как параметр в качестве пина CE для какой-нибудь NRF24L01). Или внешний АЦП будет ничем не отличаться от внутреннего с точки зрения работы с ним.

Очень плохая идея, наступаешь на те же грабли, что и ардуинщики. Если в линуксовом ядре такой подход будет работать, там производительности много, а юз-кейзов для GPIO - щелкать питание раз в вечность на каком-то девайсе. На таком эмбеддеде на GPIO часто делают битбанг разных интерфейсов, или необходимо обеспечить точную задержку между тем, что выдаст GPIO. У тебя из-за это программной подушки оно будет раз в 100 медленнее, как минимум. С GPIO вообще лучше всего никакой абстракции не делать - это ИМХО момент, где она вредна, в отличие от других протоколов.

ncrmnt ★★★★★
()
Ответ на: комментарий от ncrmnt

Ну мой фреймворк в первую очередь для возможности быстро и просто (но без быдлокода) сделать что-то, пожертвовав немного оптимальности. Понятное дело, что в условиях, когда надо выжать максимум из аппаратуры, он не подойдёт.

Arduino не подходит, потому что:

1) Поддерживает очень мало железа. Почти полностью заточена под набор стандартных плат, шаг в сторону - уже не так и удобно становится. Самое вкусное - ARM-процессоры не поддерживаются вообще (кроме того, что стоит в Arduino Due, причём там не используется никаких качественных отличий ARM от AVR - только количественные типа большей частоты и количества портов).

2) Сама архитектура Arduino полностью заточена под AVR, с которых всё и начиналось. Все остальные архитектуры натягиваются на убогую архитектуру AVR-ок.

3) Присутствует немало сомнительных архитектурных решений. Особенно выделяется тормознутые функции работы с портами.

Я ориентируюсь в первую очередь на процессоры с полноценной фон-неймановской архитектурой, на которой виртуальные функции и прочие вещи не сильно раздувают код. Исключения не использую, потому что они раздувают код на любой платформе. Однако при этом не привязываюсь к конкретной архитектуре.

И никаких «в 100 раз медленнее» тут нет.

Для установки значения порта, например, надо:

1) Загрузить адрес указателя на класс в регистр

2) Загрузить адрес vtable из класса

3) Загрузить адрес функции

4) Войти в функцию

5) Загрузить из класса (this передаётся в регистре из-за оптимизации) указатель на регистры порта.

6) Загрузить значение ODR в регистр

7) Изменить значение (как его изменить тоже передалось через регистр)

9) Сохранить из регистра в ODR

10) Выйти из функции

Я смотрел в отладчике - все действия это одна-две инструкции.

При этом пункты 6-9 нужно делать в любом случае и без всякого ООП.

Получается чуть больше, чем в 3 раза больше инструкций для записи значения в порт. А никак не в 100 раз. Это на STM32, на AVR, разумеется, будет хуже.

С учётом того, что короткие функции инлайнятся (я описал функции чтения-записи GPIO внутри описания класса), если вызывающий код знает, что обращается к STM32GPIO, а не к просто GPIO, то выполняются лишь действия с 5 по 9, что вообще незначительный оверхед.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 3)
Ответ на: комментарий от KivApple

1) Поддерживает очень мало железа. Почти полностью заточена под набор стандартных плат, шаг в сторону - уже не так и удобно становится. Самое вкусное - ARM-процессоры не поддерживаются вообще (кроме того, что стоит в Arduino Due, причём там не используется никаких качественных отличий ARM от AVR - только количественные типа большей частоты и количества портов).

leaflabs maple для ARM, chipkit для PIC32, тысячи их. Есть даже порты под msp430. Просто под каждый чип свой «порт», с не совсем совместимым API.

2) Сама архитектура Arduino полностью заточена под AVR, с которых всё и начиналось. Все остальные архитектуры натягиваются на убогую архитектуру AVR-ок.

двойной буллшит. а) Об архитектуре не думали, писали ее жаба-программисты на С++. б). У AVR хорошая архитектура для своих задач, если уметь ей пользоваться.

3) Присутствует немало сомнительных архитектурных решений. Особенно выделяется тормознутые функции работы с портами.

И ты их будешь повторять так или иначе, если будешь делать на плюсах. Если уж хочешь С++ - лучше вливайся в ряды девелоперов дурины и доведи ее до вменяемого состояния.

В общем дело конечно твое, но лично я считаю что С++ на голом железе не место.

ncrmnt ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.