Про векторы прерываний очень хорошо сказано в Programming Manual на серию Кортексов. Для M3, стоящем в F401 - это будет документ PM0214, стр.40. Табличка с определенной структурой, в которую помещаются значения адресов, по которым располагается тот или иной исполняемый код, вызываемый при наступлении события прерыавния/исключения. Вторая строчка таблички от начала называется reset Vector и она хранит адрес, по которому расположена первая инструкция исполняемого кода, вызываемая сразу после события сброса МК и которая будет выполнена первой инструкцией. Помещаться эта инструкция может в любом месте из диапазона разрешенных адресов для исполнения инструкций. Скрипи загрузчика .ld размещает секции в бинарном файле по определенным адресам. Табличка векторов тоже может быть помещена не в самом начале флеша, а где-нить в другом месте, о чем нужно дополнительно сообщить микроконтроллеру в коде программы, изменив VTOR (там, где он есть). Именно скрипт .ld будет ответственнен за то, что куда кладется на этапе компиляции В зависимости от конкретной IDE и кмопилятора, состав и синтаксис файлов запуска может отличаться.
Спасибо. Т.е. по адресу 0x08000000 во флеш записывается наша прошивка, в начале нашей прошивки расположена таблица векторов (адресов), фактически смещений относительно начала прошивки (которую собрал компоновщик). Встроенный загрузчик stm32 при старте копирует таблицу векторов из флеша в ОЗУ на начальный адрес 0x00000000. После чего встроенный загрузчик пробегает по таблице, активирует прерывания и прочее согласно данных таблицы, после читает адрес во второй строке таблицы, где расположен адрес начальной функции (reset Vector), в моём случае эта функция _start :
Встроенный загрузчик stm32 при старте копирует таблицу векторов из флеша в ОЗУ на начальный адрес 0x00000000.
Никак нет. При включении питания на адрес 0x00000000 отображается область памяти, заданная на ножках BOOT0 и BOOT1. Это может быть SRAM (0x20000000), FLASH (0x0800000) или SystemFlash (адрес не помню). Соответственно, то что у вас размещено в коде по адресу 0x08000000 и будет таблицей векторов прерываний. В ней в начале записан адрес стека, затем адреса функций обработки прерываний начиная с Reset_Handler. Посмотрите приложенный файл, он на С, там многое станет понятно.
Таблица даже не смещений, а абсолютных адресов. Вот так она выглядит в самой прошивке микроконтроллера. Второй элемент таблицы - адрес начала работы программного кода
После чего встроенный загрузчик пробегает по таблице, активирует прерывания и прочее согласно данных таблицы,
И опять нет. Таблица векторов прерываний просто лежит в памяти. При возникновении прерывания контроллер прерываний выбирает по номеру прерывания адрес функции обработчика и передаёт ей управление. Прадварительно сохранив в стеке всё что нужно для возврата из прерывания, само собой.
Для кортексов, коими являются чипы STM32, эта опция не имеет особого смысла. Выполнение кода начинается с адреса, указанного в таблице векторов прерывания на месте Reset_Handler.
функция _start производит инициализацию и секций памяти, а также устройств (в том числе внешних).
Минимально это инициализация сегментов данных. Нулевых занулением, инициализируемых - копированием из флэш. Строго необходима настройка только тех устройств, без которых невозможна инициализация данных и дальнейшая работа кода. Всё остальное можно делать в main. Я, например, предпочитаю всё железо инициализировать до main, в котором оставляю только логику работы программы. Но это всё уже вкусовщина.
Есть такая функция, зовется SystemInit() и вызывается до входа в main() как раз из файла "startup_stm32... .s ". По логике названия, оная должна выполнять инициализацию системы. Неплохая задумка, довольно удобная. Ну а по факту написанного в ней, она выполняет только настройку системных частот и латентности флеша. А в случае кубогенерирования, нужность этой функции вообще сведена близко к нулю, поскольку настройка тактирования передана в другую функцию, вызываемую из main(). То есть, изначально по названию задумывалось чуть ли не как основа основ при запуске, а в последствии она стала заброшкой, мусором, который просто забыли удалить. Да и сам файл "system_stm32..... " так же изначально задумывался как главный файл конфига системы, а сейчас превратился почти в такой же мусор.
Есть такая штука, как CMSIS, которая призвана сделать единообразным код для кортексов разных производителей, разных тулчейнов, кодогенераторов и т.д. Следовать ему достаточно хорошая практика. Библиотеки для STM32 и кодогенератор куба достаточно неплохо соответствуют этому шаблону.
Последний раз редактировалось VladislavS Пт авг 26, 2022 17:24:08, всего редактировалось 1 раз.
Она то призвана. Да вот с логикой не очень хорошо, недоработали до конца. И реализация в кубогенераторе, повторюсь, совсем не способствует логике как CMSIS, так и вообще логике вещей. Кубогенератор исповедует свою логику тут.
То есть, изначально по названию задумывалось чуть ли не как основа основ при запуске, а в последствии она стала заброшкой, мусором, который просто забыли удалить.
Задумывалось что функция SystemInit() оставит после себя рам память свободной. Для этого в GCC есть нативная галочка - использовать память повторно. Условие всегда срабатывает для SystemInit(), и почти никогда для всего остального. По этой причине крайне желательно вычистить весь мусор от кодогенераторов, и в ручную настроить периферию по максимуму.
Залезать в startup.s понадобится когда проект перерастёт стадию моргания светиком, но это уже другая тема.
Задумывалось что функция SystemInit() оставит после себя рам память свободной.
Я бы сказал больше. Она задумывалась выполняться, когда ещё никакого глобального окружения нет (память не инициализирована) и предназначена для подготовки системы к этой самой инициализации. Собственно и использовать в ней никаких глобальных данных нельзя.
*** Блин, дочегоже неудобно тут цитировать цитаты... Вручную копировать/удалять.... елы палы, я уже пальцы сломал добавлять цитаты
Кто не доработал? Да вот даже вы. посмотрите:
Цитата:
Я, например, предпочитаю всё железо инициализировать до main, в котором оставляю только логику работы программы.
то есть, фактически складываете юзер-код в область системы. Впрочем, дело ваше, я тоже так делаю.
Цитата:
функция SystemInit() оставит после себя рам память свободной.
Дак после возврата из любой вызываемой функции стек возвращается в предыдущую позицию и ранее занятая память остается свободной.
Цитата:
алезать в startup.s понадобится когда проект перерастёт стадию моргания светиком,
Практически никогда. Только после того, как вы создадите дополнительные секции, требующие дополнительного обнуления/копирования, или немного изменить поведение кода до входа в main. Например, исключить вызов SystemInit или наоборот, добавить вызов доп.ф-ций инита.
Из описания файла:э This module performs: * - Set the initial SP * - Set the initial PC == Reset_Handler, * - Set the vector table entries with the exceptions ISR address * - Configure the clock system --(это записано в файле system_stm32xxx .с) * - Branches to main in the C library (which eventually * calls main()).
Цитата:
Она задумывалась выполняться,
В её описании однозначно написано, для чего она задумывалась: - SystemInit(): Setups the system clock (System clock source, PLL Multiplier * factors, AHB/APBx prescalers and Flash settings). * This function is called at startup just after reset and * before branch to main program. This call is made inside * the "startup_stm32f1xx_xx.s" file.
Цитата:
(память не инициализирована)
Да ну. Секции data и bss подготавливаются еще до SystemInit (которая в том числе выполняет запуск внешней S(D)RAM, если надо). После неё вызываются статические конструкторы, а затем уже main
Да ну. Секции data и bss подготавливаются еще до SystemInit (которая в том числе выполняет запуск внешней S(D)RAM, если надо). После неё вызываются статические конструкторы, а затем уже main
Открываем репозиторий CMSIS, находим там первый попавшийся стартап и наблюдаем, что SystemInit это первое что выполняется в Reset_Handler.
SystemInit (которая в том числе выполняет запуск внешней S(D)RAM, если надо). После неё вызываются статические конструкторы, а затем уже main
Интересно, а если сегменты данных в этой самой SDRAM и находятся? Или в чипах STM32H7 бывают блоки SRAM выключенные при подаче питания. Как тогда в них до SystemInit что-то инициализировать?
У вас гранаты не той системы Я вам могу показать кучу источников файлов с иной структурой. Потому и говорю, что задумка вначале была неплохой, но со временем состав файлов менялся, каждый пишет так, как ему заблагорассудится. И самое главное, что ни одна из версий не является ошибочной с точки зрения микроконтроллера.
Касательно SDRAM - в нее вообще нерекомендовано помещать отдельные переменные или выполнять доступ непакетным образом, из-за сильного снижения скорости. То же самое, но по иной причине, с выключенными блоками SRAM. Их основное назначение - хранение буферов, завязанных на какой-либо модуль периферии. И не не для того придумывалось выключение, чтобы сразу же их безусловно включать. Обычно они включаеются/выключаются вместе с той периферией, с которой в паре используются, и обычно имеют команду аппаратного обнуления. Выключение придумано для энергосбережения. Иначе никто бы не усложнял.
Касательно SDRAM - в нее вообще нерекомендовано помещать отдельные переменные или выполнять доступ непакетным образом, из-за сильного снижения скорости.
Причём тут отдельные переменные? Гигантские буферы тоже надо инициализировать.
При включении питания на адрес 0x00000000 отображается область памяти, заданная на ножках BOOT0 и BOOT1. Это может быть SRAM (0x20000000), FLASH (0x08000000) или SystemFlash (адрес не помню).
То есть без режима прошивки (заданного на ножках BOOT) МК при запуске отображает в ОЗУ память flash, куда мы разместили прошивку с таблицей векторов по адресу 0x08000000 (адрес во флеш или озу памяти?).
то что у вас размещено в коде по адресу 0x080000000 и будет таблицей векторов прерываний. В ней в начале записан адрес стека, затем адреса функций обработки прерываний начиная с Reset_Handler. Посмотрите приложенный файл, он на С, там многое станет понятно.
Разве в коде мы устанавливаем адреса? По моему, код может только обращаться по адресу. Компилятор создаёт бинарный файл, который при прошивке МК мы передаём загрузчику, который размещает его во flash-памяти МК по некоторому адресу. И когда МК стартует он отображает эти данные из flash в озу. И если так, то почему адрес 0x080000000, - ведь он соответствует размеру flash-памяти 519 МБ ? У МК только 256 кБ, не говоря уже о 64 кБ SRAM.
Для кортексов, коими являются чипы STM32, эта опция не имеет особого смысла. Выполнение кода начинается с адреса, указанного в таблице векторов прерывания на месте Reset_Handler.
Да, после того как я нашёл таблицу векторов в коде cmsis/vectors_stm32f401xc.c, соответственно нашёл и Reset_Handler(), который там вызывает _start() который вызывает main().
Таблица даже не смещений, а абсолютных адресов. Вот так она выглядит в самой прошивке микроконтроллера. Второй элемент таблицы - адрес начала работы программного кода
Как я понимаю это отладчик памяти МК. Интересно как в таблице появляются эти абсолютные адреса, да тем более по такому адресу?
Последний раз редактировалось ddr4 Пт авг 26, 2022 19:14:08, всего редактировалось 2 раз(а).
Это (на скрине) не отадчик, это просто просмотр hex-файла прошивки в ST-Link Utility - проге для чтения и заливки прошивки в микроконтроллер. А в таблице эти адреса появляются стараниями линкера в процессе компиляции. Он раскладывает программный код по адресам и адреса функций, реализующих обработку прерываний, помещает в таблицу.
Цитата:
Гигантские буферы тоже надо инициализировать.
Попробуйте проинициализировать побайтно пару мегабайт в SDRAM - расскажете о впечатлениях о скорости непактного доступа Почитайте про SDRAM, расширьте свой кругозор. Вы видимо не так давно с ARM занимаетесь, потому не вкурсах про историю изменений файлов самими ARM. Я то значительно раньше вас начал их изучать.
Последний раз редактировалось MLX90640 Пт авг 26, 2022 20:05:34, всего редактировалось 1 раз.
Под памятью, Вы имеете ввиду бинарный файл, который по ld-скрипту соберёт компоновщик из объектов которые создал компилятор?
Нет, под памятью я подразумеваю всё адресное пространство. Бинарный файл это как бы "слепок" программы. Стартовый код разворачивает из него данные в разные области памяти. В какие-то занулением, в какие-то копированием.
То есть без режима прошивки (заданного на ножках BOOT) МК при запуске отображает в ОЗУ память flash, куда мы разместили прошивку с таблицей векторов по адресу 0x08000000 (адрес во флеш или озу памяти?).
Не в ОЗУ, а в адресное пространство. Если на 0x00000000 отражена SRAM, то запись туда возможна, а если флэш, то соответственно нет.
И если так, то почему адрес 0x08000000, - ведь он соответствует размеру flash-памяти 519 МБ ? У МК только 256 кБ, не говоря уже о 64 кБ SRAM.
Адресное пространство 32-разрядное и может адресовать 4 Гб. В любом месте этого адресного пространства могут быть кусочки по несколько килобайт или мегабайт. Это и разного типа памяти, и регистры периферийных устройств, и всякого рода FIFO для USB или Ethernet и т.д. Цифры о которых мы говорим - начальные адреса этих "кусочков". А между ними "дыры".
это просто просмотр hex-файла прошивки в ST-Link Utility - проге для чтения и заливки прошивки в микроконтроллер. А в таблице эти адреса появляются стараниями линкера в процессе компиляции. Он раскладывает программный код по адресам и адреса функций, реализующих обработку прерываний, помещает в таблицу.
Возможно это какой-то умный просмотрщик. Наверняка он эмулирует отображения памяти в МК, либо берёт адрес из файла. Возможно адрес размещения 0x08000000 где-то явно указан. Либо в коде, либо в компиляторе. Но просто поиск адреса 0x08000000, по коду блинка результата не дал.
Разве в коде мы устанавливаем адреса? По моему, код может только обращаться по адресу.
Ну ещё он может размещаться по адреcу. Размещает по адресу, обычно, линкер в соответствие с правилами заданными в его конфигурационном скрипте.
Линкер скорее задаёт (записывает в бинарный файл) адрес по которому уже МК будет пытаться разместить код в адресном пространстве. Вот только непонятно откуда Линкер или компилятор берёт адрес 0x08000000, в коде блинка я его не нашёл.
И если так, то почему адрес 0x08000000, - ведь он соответствует размеру flash-памяти 519 МБ ? У МК только 256 кБ, не говоря уже о 64 кБ SRAM.
Адресное пространство 32-разрядное и может адресовать 4 Гб. В любом месте этого адресного пространства могут быть кусочки по несколько килобайт или мегабайт. Это и разного типа памяти, и регистры периферийных устройств, и всякого рода FIFO для USB или Eythernet и т.д. Цифры о которых мы говорим - начальные адреса этих "кусочков". А между ними "дыры".
Понятно, то есть адресное пространство больше памяти ОЗУ.
Последний раз редактировалось ddr4 Пт авг 26, 2022 20:43:20, всего редактировалось 1 раз.
Возможно это какой-то умный просмотрщик. Наверняка он эмулирует отображения памяти в МК, либо берёт адрес из файла. Возможно адрес размещения 0x08000000 где-то явно указан. Либо в коде, либо в компиляторе.
Самый обычный просмотрщик стандартного hex-файла. Открываем в блокноте этот файл и читаем первую строчку: :020000040800F2 выделенное - и есть адрес (его старшие байты) начала размещения данных. Последующие строчки содержат заголовок строки и далее сами данные, в конце контрольная сумма строки. :1000000000500020DD0300084503000851030008EC
Последний раз редактировалось MLX90640 Пт авг 26, 2022 20:49:30, всего редактировалось 1 раз.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 19
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения