Я решил поучиться работать с GNU LD. Начать решил с малого: в стандартный файл скрипта для STM32L152RE, который идет в комплекте с EmBitz, добавить секции для размещения данных в EEPROM (по умолчанию их там нет).
/* .stack_dummy section doesn't contains any symbols. It is only * used for linker to calculate size of stack sections, and assign * values to stack symbols later */ .stack_dummy (NOLOAD): { *(.stack) } > RAM
/* ДОБАВИЛ две секции, соответствующие двум банкам EEPROM. Использовал KEEP, чтобы компоновщик не удалял данные, которые по той или иной причине должны быть записаны в память, но не используются в программе непосредственно. */ .eeprom_bank_1 : { KEEP (*(.eep_bank_1)); } > EEP1
/* Set stack top to end of RAM, and stack limit move down by * size of stack_dummy section */ __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - SIZEOF(.stack_dummy); PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") }
Судя по тому, что я наблюдаю в выходном бинарнике, все работает.
И да, его правда видно в бинарнике начиная с адреса 0x08082000.
Но, тем не менее, у меня есть вопросы.
Вопрос номер один. Я просто добавил новые секции в конец перечисления секций. Этого достаточно, чтобы они встали, как надо? Если да, то зачем при объявлении секции .data используется атрибут AT?
Вопрос номер два. Я правильно понимаю, что, по сути, две записи, которые я привожу ниже, эквивалентны?
Я так понимаю, что в первом варианте компоновщик кроме всего прочего будет проверять размер, но вроде бы во втором он должен положить данные этой секции по тому же адресу.
_________________ Разница между теорией и практикой на практике гораздо больше, чем в теории.
Для тех, кто найдет эту тему поиском, напишу, в чем дело.
Как обычно, всему виной историческая терминология. Там есть два термина - LMA (load memory address) и VMA (virtual memory address). Правда, в наши дни уже давно ничего никуда не загружается. По сути, VMA - это тот стартовый адрес, который компоновщик будет учитывать при обращении к переменным, расположенным в этой секции. LMA - это адрес, по которому данные этой секции будут записаны в файл образа прошивки, а в нашем случае - и во FLASH контроллера, потому что прошивка записывается туда.
Адреса в случае выше указываются как
<имя секции> : AT [LMA] { ... } [ > <указанная память определяет VMA>]
В файле выше смысл в том, что RAM начинается с 0x20000000, и в тех местах, где производится обращение к переменным, расположенным в этой секции, компоновщик будет подставлять смещения от этого адреса. А вот константы, предназначенные для инициализации переменных, он положит сразу после кода (секция .text), то есть, во FLASH.
Если этого не сделать, то выходной .hex-файл будет инструктировать программатор записывать значения прямо в RAM. Он, конечно, запишет, это технически возможно, но смысл, заключающийся в резервировании памяти в RAM и хранении инициализационных констант во FLASH, будет потерян.
К слову, атрибут NOLOAD говорит компоновщику, что секцию надо всего лишь учитывать при расстановке смещений, но в выходной файл ее содержимое писать не надо.
Цитата:
EEPROM в STM32 поддерживает прямую запись?
Да. Сначала надо разблокировать запись в специальном регистре, и дальше можно писать по указателю. Ну а в нашем случае, если переменная расположена в новой секции, можно будет писать как в обычную переменную.
_________________ Разница между теорией и практикой на практике гораздо больше, чем в теории.
Оно конечно будет работать, но радости не принесёт. Размечать EEPROM как область памяти - означает позволить компилятору самому назначить место хранения переменных. Ну в том смысле что он будет там всё оптимизировать и складывать в аккуратную кучку. Это удобно, но не долговечно.
Качественное и безопасное устройство, работающее от аккумулятора, должно учитывать его физические и химические свойства, профили заряда и разряда, их изменение во времени и под влиянием различных условий, таких как температура и ток нагрузки. Мы расскажем о литий-ионных аккумуляторных батареях EVE и нескольких решениях от различных китайских компаний, рекомендуемых для разработок приложений с использованием этих АКБ. Представленные в статье китайские аналоги помогут заменить продукцию западных брендов с оптимизацией цены без потери качества.
Заголовок сообщения: Re: GNU LD. Пытаюсь написать свой скрипт компоновки.
Добавлено: Чт апр 08, 2021 23:17:14
Нашел транзистор. Понюхал.
Зарегистрирован: Вс сен 06, 2020 16:06:10 Сообщений: 156
Рейтинг сообщения:0
Более того - все время работать с разблокированным на запись EEPROM не очень удачная идея с точки зрения сохранности данных в нем. С всем что флеша касается вообще работают обычно так: разблокировали - записали что хотели - заблокировали обратно. Иначе записанного можно однажды лишиться при отклонениях от идеала.
И это, при рестарте переменные инициализируются же. Это чего, каждый ребут будет цикл еепром давать? Или оно как uninitialized идет? А как вы начальные значения пропишете только 1 раз? И вообще как оно будет non-volatile при этом, если так в лоб? Можно const'ами обвесить, но записывать как?
Кроме того - доступ на запись может bus stall на довольно почтенное время. Вам совсем плевать на реальное время и вы готовы с неконтролируемым (вами явно) тупняком чипа от такого доступа жить? Флеш так то довольно долго пишется (по микроконтроллерным меркам) и в это время модуль флеша не может код прошивки отдавать. В 2-банковых чипах, конечно, не занятый записью банк дееспособен пока другой пишется - но этим надо явно заморочиться, положив код в один и поюзав еепром в другом.
Ну и если хочется видеть это напрямую в памяти - например, завести typedef struct config какой-нибудь, переменную (-ые) которые заявлены - как _указатели_ на это, и по мере желания можно указатель назначать куда там охота (ram vs eeprom) например (но осторожно, указатели они такие). На еепром лучше указывать с полями const везде, во избежание. А записывать чем-то типа memcpy этого (из версии в RAM) - сняв лок, записав как надо, вернув лок - и вызывается в момент когда ОК что чип может довольно долго тупануть и за это ничего не будет. На 2-банковом в принципе можно код в 1 банк, еепром в другой, тогда это не проблема вроде, но потребует специального внимания.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
все время работать с разблокированным на запись EEPROM не очень удачная идея с точки зрения сохранности данных в нем.
С чего вы взяли, что я буду так делать?
Цитата:
И это, при рестарте переменные инициализируются же.
Инициализируются только переменные из сегмента .data. Для этого в startup-коде, который по умолчанию добавляется при компоновке, есть специальный цикл. Для всех остальных сегментов никакой инициалиации по умолчанию нет.
Цитата:
А как вы начальные значения пропишете только 1 раз?
То, что помещено в заданные сегменты, попадает в образ прошивки по соответствующим адресам и, соответственно, записывается программатором при прошивке чипа. В этом и есть прелесть задания области сегментом в скрипте компоновщика.
Цитата:
И вообще как оно будет non-volatile при этом, если так в лоб?
Эта область памяти non-volatile аппаратно.
Цитата:
Кроме того - доступ на запись может bus stall на довольно почтенное время. Вам совсем плевать на реальное время и вы готовы с неконтролируемым (вами явно) тупняком чипа от такого доступа жить?
FLASH находится в другом банке относительно EEPROM. Кроме того, запись туда имеет смысл производить только при сохранении настроек, например.
Цитата:
но осторожно, указатели они такие
Именно от этой проблемы избавляет введение специального сегмента. В этом случае компоновщик сам проверяет адреса.
_________________ Разница между теорией и практикой на практике гораздо больше, чем в теории.
Я вот тоже хочу научиться понимать и работать с линкером. Задача такая - есть кастомный бутлоадер и есть основное приложение. Как это делать и как отредактировать линкер скрипты для обеих частей в интернете разжёвано. Моя пробелма - и загрузчик, и приложение использую ST-шную библиотеку для криптографии STM32cryptographic...GCC.a. Вернее используют они конечно не всю библиотеку целиком, а только несколько функций. Как можно сделать, чтобы эти функции при линковке загрузчика ложились в определенное место в прошивке, а в приложении эти функции подхватывались из того же места?
Заголовок сообщения: Re: GNU LD. Пытаюсь написать свой скрипт компоновки.
Добавлено: Пт апр 23, 2021 18:31:11
Открыл глаза
Зарегистрирован: Вс мар 21, 2021 11:06:04 Сообщений: 41
Рейтинг сообщения:0
Использовать одни и те же данные или код и в бутлоадере и в основной прошивке плохая идея. В идеале, они должны быть абсолютно независимыми и не связанными. Единственное, что бутлоадер может знать об основной программе - её начальный адрес. Не более. А вот основная программа о бутлоадере не должна знать ничего. Вообще. Для неё его не существует.
Использовать одни и те же данные или код и в бутлоадере и в основной прошивке плохая идея. В идеале, они должны быть абсолютно независимыми и не связанными. Единственное, что бутлоадер может знать об основной программе - её начальный адрес. Не более. А вот основная программа о бутлоадере не должна знать ничего. Вообще. Для неё его не существует.
В иделе да, но на секундочку давайте предположим, что я знаю, что делаю (всего лишь не знаю, как ). Мы говорим о МК, и хотя сегодня они уже тоже дают много памяти, хранить вещи два раза - можно и обойтись.
Чёрт, да это уже на уровне практикума интересно попытаться реализовать.
Заголовок сообщения: Re: GNU LD. Пытаюсь написать свой скрипт компоновки.
Добавлено: Пт апр 23, 2021 19:20:01
Открыл глаза
Зарегистрирован: Вс мар 21, 2021 11:06:04 Сообщений: 41
Рейтинг сообщения:0
В соседней теме я уже давал ссылку на эту книжку. Вам она тоже пригодится. Тут глава про загрузчик, а в конце её раздел про API загрузчика. Это то, что вам нужно. Но я бы все-таки создал константную структуру указателей во флеш-памяти загрузчика по фиксированному адресу, который будет известен основной программе. Она будет обращаться к ней по этому адресу и вычитывать из неё указатели на нужные функции. Это избавит от траты ОЗУ.
В соседней теме я уже давал ссылку на эту книжку. Вам она тоже пригодится. Тут глава про загрузчик, а в конце её раздел про API загрузчика. Это то, что вам нужно. Но я бы все-таки создал константную структуру указателей во флеш-памяти загрузчика по фиксированному адресу, который будет известен основной программе. Она будет обращаться к ней по этому адресу и вычитывать из неё указатели на нужные функции. Это избавит от траты ОЗУ.
Ок, почитаю на выхах.
Пока мне удалось только положить функцию в загрузчике в отдельную секцию, её адрес - 0x080000c0. это void func(void) {}; но вызвать её из основной программы не получается, вызываю вот так ((void(*)(void))0x080000c0)(); Это было слишком наивно видимо буду читать
Заголовок сообщения: Re: GNU LD. Пытаюсь написать свой скрипт компоновки.
Добавлено: Пт апр 23, 2021 20:59:19
Открыл глаза
Зарегистрирован: Вс мар 21, 2021 11:06:04 Сообщений: 41
Рейтинг сообщения:0
И как? Работает или нет? Обратите внимание на эту страничку. Там сказано, чему должен быть равен младший бит в адресе перехода при вызове функции. А у вас он какой? Как адрес секции - с нулем в младшем бите?
1. Объявите тип структуры с полями-указателями на функции с требуемой сигнатурой. 2. В загрузчике создайте такую константную структуру, инициализируйте её указателями на требуемые функции, и поместите её в RO-секцию по фиксированному адресу. 3. В основной программе объявите константный указатель на эту структуру и инициализируйте его её фиксированным адресом. 4. В основной программе вызывайте функции, обращаясь к полям этой структуры через указатель на неё.
Как можно сделать, чтобы эти функции при линковке загрузчика ложились в определенное место в прошивке, а в приложении эти функции подхватывались из того же места?
Я не великий гуру линкер-скриптинга, но предположу.
В скрипте компоновщика пишем:
Код:
MEMORY { ... SHARED_CODE_REGION (rx) : ORIGIN <где должен начинаться разделяемый код>, LENGTH = <сколько выделим под разделяемый код> }
Заголовок сообщения: Re: GNU LD. Пытаюсь написать свой скрипт компоновки.
Добавлено: Вс апр 25, 2021 17:16:17
Открыл глаза
Зарегистрирован: Вс мар 21, 2021 11:06:04 Сообщений: 41
Рейтинг сообщения:0
YS писал(а):
В той программе, которая эти функции должна только использовать, но не содержать, к секции .shared_code_section добавляем атрибут NOLOAD.
При таком подходе существует отличная от нуля вероятность, что в двух разных проектах линкер положит функции в секцию в разном порядке. Имеет полное право тасовать их внутри секции как хочет. И тогда в программе, которая их только использует, при вызове этих функций будет неопределенное поведение. Скорее всего хардфолт или еще какой-нибудь фолт.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения