Области памяти, в которые я могу и не могу писать, архитектура ARM Cortex-M

Я надеюсь, что мой заголовок верен терминологически. Я работаю (учусь) с платой обнаружения STM32F4, на которой установлен микроконтроллер STM32F407VGTx. Я очень стараюсь найти ответы в справочнике , но иногда действительно сложно найти даже где искать. Может быть, из-за того, что это 1700+ страниц...

Итак, вот ситуация, во время моих тестов (код C, компилятор arm-none-eabi-size) я понял, что могу записать любое значение в периферийные регистры, например в регистры GPIOD. Но используя тот же код (для записи по адресу), я не могу записать, например, адрес 0x58 (действительно 0x00000058). В случае периферийных регистров в документе четко указано, какие регистры/биты разрешены для записи, а какие доступны только для чтения с такими обозначениями, как «r», «rw». Однако для адреса 0x58 я не смог найти причину, по которой я не могу писать на него.

Любое руководство или объяснение будут оценены, спасибо.


обновлять:

Встречный вопрос: почему вы должны иметь возможность писать на этот адрес? Что-то сопоставляется с этим? — Маркус Мюллер

Хорошо, это немного интересно. Я только начал изучать внешние прерывания, и я хочу делать все (таким образом, включая внешние прерывания) на уровне регистров во время моего обучения. Вот почему я не использовал никаких функций из HAL, SPL или CMSIS, а также ни один из этих файлов не присутствует в каталоге проекта. Мне вроде как это удалось, так что у меня был правильный регистр EXTI, ожидающий запуска, но мне не удалось найти способ связать функцию обратного вызова с прерыванием, которая будет определять процесс, который я хочу выполнить в случае прерывания. Проверка таблицы NVIC (стр. 372) из ​​справочного руководства показала, что каждое прерывание связано с адресом памяти в последнем столбце. Итак, я подумал, может быть, просто идея, что эти места будут содержать адрес памяти (указатель) на функции обработчика прерываний. Так, Затем я решил определить функцию, а затем записать адрес этой функции в ячейку памяти 0x58. Так что, когда придет прерывание, микроконтроллер будет смотреть на 0x58, который перенаправит его на местонахождение интересующей функции.

Да, это обновление само по себе могло быть совершенно другим вопросом. Извините за беспорядок. Я думаю, что на этот вопрос можно ответить и без этой истории, но комментарий заставил меня также добавить это...

Встречный вопрос: почему вы должны иметь возможность писать на этот адрес? Что-то сопоставляется с этим?
@MarcusMüller, немного интересно, почему мне понадобилось, чтобы я мог писать по этому адресу, это было просто гипотетически в моей голове, и я, вероятно, увидел бы, что это не так, как я думал, если бы мне удалось пишите на этот адрес. Позвольте мне добавить историю к моему ответу, и когда я закончу, я уведомлю вас.
Я не знаю, имеет ли значение история, вопрос в том, есть ли что-то, на что вы можете написать, сопоставленное с этим адресом.
хорошо, я предположил, что вы знаете, почему я не могу написать туда и задать реторический вопрос. Если это не так, я думаю, что мое обновление не даст вам много информации о том, «Что-то сопоставлено с этим», потому что оно объяснит, почему я думал, что собираюсь что - то записать в этот адрес памяти. Как я уже сказал, я точно не знаю, где-то в этой области, содержащей 0x58, я должен иметь возможность что-то писать или нет. Я мог бы говорить о совершенно бессмысленных вещах, но мое обновление прояснит, по крайней мере, почему я пришел к идее написать на этот адрес в первую очередь.

Ответы (4)

Вам нужно взглянуть на конфигурацию памяти процессора. Cortex-M4 реализует архитектуру ARMv7-M. Вот фрагмент верхней половины карты памяти (к сожалению, в справочном руководстве M4 она разбита на две страницы ).

введите описание изображения здесь

Как видите, область 0x00000000 - 0x1FFFFFFFвыделена для Code. Реализация STM не будет включать доступный путь на системной шине AHB к этой области, поэтому вы не можете писать по этому адресу.

а, ладно, так что этот регион — это какой-то жесткий диск наших обычных компьютеров, после того, как код C будет скомпилирован и преобразован в двоичный код, он будет загружен в этот регион, и микроконтроллер будет читать оттуда инструкции. Я хоть близко к делу?
Компоновщик используется для указания областей памяти и их атрибутов. Взгляните на это, это может помочь keil.com/support/man/docs/armlink/armlink_pge1406297881263.htm Это может быть запутанной темой, но часть удовольствия от использования микро, а не полных систем :)

Возможно, таблица векторов перемещена ; есть регистр, в котором хранится «0» адрес этой таблицы, и его можно настроить. На самом деле это довольно удобная функция: она позволяет вам настроить таблицу обработчиков прерываний в любом месте ОЗУ, а затем с помощью одной записи (я VTORдумаю, по адресу 0x08) вы можете переключиться на эту новую таблицу.

См. стр.218 руководства по программированию stm32f4 .

позвольте мне попробовать на себе, правильно ли я вас понял или нет. Итак, 0x00000058мне не разрешено изменять его окружение, скажем, с 0x50000000адресом можно работать. Тогда я пишу 0x50000000в VTOR. Теперь таблица NVIC начинается с этого адреса. Тогда, если я напишу адрес моей функции обработчика прерываний, 0x50000058я достигну того, что намеревался? Сейчас попробую, а пока жду вашего комментария. Спасибо за ответ кстати.
@muyustan Вы должны скопировать всю таблицу векторов, иначе другие прерывания не сработают. И обычно вы можете изменить 0x58 во время программирования флэш-памяти.
@Jeroen3 Jeroen3, но как мне тогда узнать адрес функции, которую я определю?
Сценарий компоновщика @muyustan, определяющий это или использующий «метку» в сборке, которую вы также использовали бы для JMP.
@MarcusMüller хорошо, я думаю, мне следует пока оставить это, поскольку предполагаемая вещь выглядит немного выше моих знаний для достижения. Кстати, я попробовал то, что упоминал в комментарии выше, но не смог найти правильные области памяти для использования.
@muyustan подойдет любой регион ОЗУ. В этом прелесть такого гибкого решения.
@MarcusMüller хорошо, я видел в руководстве по программированию часть, связанную с VTOR, в которой говорится, что адрес, который можно поместить в VTOR, ограничен диапазоном от 0x00000080 до 0x3FFFFF80. Итак, ваше «подойдет любой регион ОЗУ» верное утверждение? Я не осуждаю, это прямой вопрос. Что сделало меня неспособным даже попробовать этот процесс, так это то, что я не мог найти, какой диапазон адресов является той частью, которую я могу использовать в качестве ОЗУ. Просто в разных документах (ссылка, прог., техническое описание, ...) много информации, которую я не мог вытащить из этой сложности. Большинство адресов, которые я пытался.......
Произведение операции записи в него привело к сбою, т.е. я не смог записать в них данные, наблюдая за памятью браузера в режиме отладки.
@muyustan, это хороший вопрос. Посмотрите здесь: infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/…
@muyustan Для VTOR вы можете использовать любую внутреннюю область ОЗУ, а не внешнюю. Если вы не используете слабое связывание, прочитайте в документации по компилятору атрибуты адреса связывания, которые вы можете присвоить функциям.
@MarcusMüller, каков минимальный набор требований для понимания этих документов, я имею в виду, я думаю, что у меня есть общие знания о вещах, но эти документы, подобные тому, на который вы только что ссылались, выглядят для меня так, как если бы они были написаны на китайском языке. В любом случае, я проверю его и постараюсь понять. Возможно, я опубликую еще один вопрос о своих заблуждениях, понимании и, что более вероятно, «непонимании» в отношении этих проблем с памятью. Если я это сделаю, я отмечу вас там, я надеюсь. Спасибо за ваши старания.
@muyustan Я думаю, у тебя все хорошо. Современные микроконтроллеры — невероятно сложные компьютеры. В слабом всё не понять :) Продолжай!
@MarcusMüller хорошо, еще одна вещь меня смутила: я никогда не смотрел битовую структуру VTOR до нескольких минут назад, так как я никогда не доходил до того, чтобы писать в VTOR. Впрочем, я только что посмотрел его, прогу. справочную страницу 227 и увидел, что в этом регистре доступен только 21 бит для записи. Так как же можно записать в него 32-битный произвольный адрес?
ты не можешь! а) у вас нет полного 32-битного адресного пространства, как вы сами заметили (от 0x00000080 до 0x3FFFFF80), поэтому старшие биты бесполезны, и б) когда вы читаете абзац над таблицей, вы видите, что младшие биты также невозможно выбрать.
@muyustan Могу я повторить то, что сказал Маркус, у тебя все хорошо! Микроконтроллеры требуют, чтобы вы работали на очень низком уровне, понимая расположение, защиту, прерывания и т. д. Эти вещи абстрагируются при работе в размещенной системе. Кроме того, когда у вас есть что-то вроде Cortex-M4, у вас есть процессор, а затем все периферийные устройства конкретного поставщика, с которыми нужно иметь дело. И хорошие вопросы :)
Еще одно: точно так же, как вы не стали бы пытаться программировать свой компьютер без операционной системы (вы бы сошли с ума, инициализируя что-то, прежде чем вы сможете его использовать, а затем выясняя, как поддерживать его работу), вы действительно не используете такие микроконтроллеры без их HAL. В большинстве случаев эти HAL на самом деле являются «абстракциями с нулевой стоимостью», поскольку они просто давали бы имена адресов ваших регистров для работы или позволяли бы вам вызывать функцию в ПЗУ производителя для записи во флэш-память вместо того, чтобы вы реализовать одну и ту же функцию. Я согласен, это здорово для изучения того, как работает процессор, что вы делаете,
но вы должны понимать, что вы идете по этому пути сложнее, чем пошел бы средний разработчик прошивки MCU. Если вы хотите узнать, как работают ЦП в целом , начните с чего-то более простого, чем 32-битный ЦП ARMv7, такого как этот... который вам действительно нужно продолжать работать на реальном оборудовании (что добавляет уровень удовлетворения, когда светодиод действительно мигает, но также добавляет огромное количество сложности). Если вы хотите узнать, как обращаться с ЦП, возможно, начните с простого ЦП в эмуляторе — это упрощает эксперименты. Он по-прежнему научит вас многим полезным основам!
@awjlogan и Маркус, спасибо вам обоим за ваши комментарии/идеи/предложения, я рассмотрю их подробно.
Что ж, по прошествии десятков часов я могу сказать, что мне удалось переместить таблицу векторов в нужное место в памяти и использовать функции обслуживания прерываний со специальными именами. Однако я кое-что заметил, сброс работал нормально, даже несмотря на то, что я переместил таблицу векторов. Всегда ли обработчик сброса находится по фиксированному адресу и не перемещается вместе с таблицей векторов NVIC?

Итак, я подумал, может быть, просто идея, что эти места будут содержать адрес памяти (указатель) на функции обработчика прерываний.

Вот как это работает. Есть еще ВТОР, как описал Маркус. Однако в простых проектах используется нечто, называемое файлом стартового ассемблера, обычно startup.sв котором стек, куча и векторная таблица размещаются на своих местах.
Таблица векторов startup.sслабо связана с обработчиком по умолчанию, который ничего не делает.
Если вы определяете функцию с точно таким же именем в другом месте, она переопределяет это значение по умолчанию.

Это для Cortex M4, что у вас чип от ST пока не важно.

Я не знаю, какой компилятор вы используете, но вот подробное описание файла запуска ARM: https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/ сообщения/расшифровка-файла-запуска-для-arm-cortex-m4

Спасибо! что вы думаете о моем комментарии под ответом Маркуса? Потому что я не мог добиться того, что задумал.

Этот адрес является программной флэш-памятью. Если вы хотите записать во флэш-память, это возможно, вам нужно сначала стереть ее с помощью периферийного устройства флэш-памяти, и вы не можете стереть выполняемую программу, поэтому не делайте этого. Есть два способа сделать это правильно. Вы можете скопировать таблицу векторов в SRAM и указать NVIC указать таблицу векторов в SRAM, чтобы вы могли свободно изменять вектор. Более распространенным решением было бы использование фиксированного кода прерывания, который берет адрес из переменной и переходит к этому адресу, поэтому вы можете просто изменить, куда указывает переменная.

спасибо, но я не мог точно понять, что вы имели в виду под "фиксированным кодом прерывания"
или, если можно, немного уточните предложение, начинающееся с «более распространенное решение……»,
Пусть векторы остаются во флэш-памяти, укажите вектору код во флэш-памяти, пусть код во флэш-памяти считывает указатель из SRAM, куда вызывать (или переходить). Обновите указатель в SRAM, чтобы он указывал на любую функцию, которая вам нравится. В терминах языка C создайте процедуру прерывания, которая вызывает функцию через указатель функции.