HardFault при запуске кода из оперативной памяти в CortexM0

В настоящее время я разрабатываю прошивку для микроконтроллера Arm Cortex-M0+ и столкнулся с довольно интересной проблемой. Я не ищу никаких ответов, а скорее хотел бы поделиться проблемой с другими разработчиками, чтобы я мог (надеюсь) пролить свет на проблему, с которой я столкнулся. Я опишу это ниже:

У меня есть программа, которая динамически загружает (правильно скомпилированный и связанный) код с внешнего флэш-чипа, который должен выполняться прямо из ОЗУ MCU. Интересно то, что я могу идеально выполнить загруженный в RAM код при пошаговом запуске (через отладчик), но он всегда будет падать [формально HardFault] при свободном запуске. Я пытался отключить все прерывания, я дважды проверил инструкции, адреса памяти, выравнивание байтов, все, но я все еще не могу точно определить причину исключения.

Есть ли у кого-нибудь из вас намеки на то, что может произойти? Мне очень интересно узнать больше о вашем опыте! Спасибо,

Обновление 1 (30/05)

Свободный пробег в данном случае означает, что точка останова не устанавливается непосредственно перед переходом в ОЗУ. Всякий раз, когда я вхожу в ветку и выполняю инструкции в ОЗУ, она будет работать правильно и возвращаться. Везде, где точки останова нет (и, таким образом, MCU масштабирует ветку), наблюдается HardFault. Обратите внимание, что он будет аварийно завершать работу даже при загрузке с включенным отладчиком, но без установленной точки останова.

Обновление 2 (30/05)

Я использую Cypress S6E1C3 Series Arm Cortex M0+ FM0+ Микроконтроллер.

Обновление 3 (30/05)

Покопавшись и поиграв с кодом, я смог заставить его работать правильно! Однако он вызвал у меня больше вопросов, чем ответов. Читая официальную документацию ARM об инструкции BLX (BLX), я обнаружил, что LSBit адреса ветки определяет режим инструкций процессора (1 заставляет ее работать в режиме Thumb). Явно установив этот бит, У меня код работает всегда, даже в свободный режим. Дело в том, что код в ОЗУ не был скомпилирован в режиме Thumb, и нет причины очевидный, по которой пошаговое выполнение кода с помощью отладчика может привести к изменению режима инструкций... Есть идеи?

К.

Поддерживает ли ваш чип спекулятивную предварительную выборку? Я так не думаю, но если бы это было так, это могло бы быть Обходной путь предварительной выборки ARM там, где они пренебрегли тем, чтобы сделать ввод-вывод или другие области физического адресного пространства недоступными для предварительной выборки. (И поэтому спекулятивные загрузки или выборки кода из них приводили к жестким зависаниям.)

Peter Cordes 29.05.2019 22:40

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

user3386109 30.05.2019 03:07

Я проверил техническое описание и руководство и не смог найти никаких упоминаний о спекулятивной предварительной выборке, несмотря на то, что это хорошая отправная точка... Я заметил, что в свободном режиме последний адрес, удерживаемый ПК перед HardFault, точно первый адрес в оперативной памяти, который MCU пытался выполнить.

Kyle Oliveira 30.05.2019 06:50

Я очень сомневаюсь, что этот отлаживать загрузочный код с боковой загрузкой, упомянутый пользователем 3386109, является причиной ошибок. Я так говорю, потому что Я вижу сбой, даже когда я загружаюсь с отладчиком. Это работает только в том случае, если я установил точку останова прямо перед инструкцией ветвления и ссылки, которая заставляет MCU работать в пространстве ОЗУ., что мне кажется весьма подозрительным...

Kyle Oliveira 30.05.2019 06:54

Обратите внимание, что я бы помог, если бы вы указали фактический MCU, а не просто сказали «ARM Cortex-M0+». У многих продавцов есть известные причуды, которые могут объяснить ваше поведение.

Turbo J 30.05.2019 11:42

Хороший вопрос, Turbo J, сейчас я работаю с Cypress S6E1C3 Series Arm Cortex M0+ FM0+ Микроконтроллер.

Kyle Oliveira 30.05.2019 12:19

Есть ли какой-нибудь механизм, который позволил бы узнать куда, что произошел HardFault? Это в самой инструкции ветвления (или выборке кода для инструкции после)? Или он работает некоторое время, прежде чем сбой?

Peter Cordes 30.05.2019 12:39

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

Peter Cordes 30.05.2019 12:40

Привет, Питер Кордес, я не уверен, где происходит сбой, но если бы мне пришлось угадывать, я бы сказал, что это происходит после выполнения ветки. Я говорю так, потому что ПК (R15) всегда будет указывать на адрес первой инструкции в ОЗУ. и я также вижу, что регистр ссылок (LR) всегда имеет правильный адрес возврата (во Flash). Сбой происходит всегда сразу, и это не похоже на то, что он работал какое-то время, а затем случайно зависал. Я не уверен, что причиной может быть аппаратный сбой, потому что я вижу ту же проблему при запуске этого кода на втором устройстве.

Kyle Oliveira 30.05.2019 12:51

Питер Кордес, есть ли у вас какие-либо сведения о конвейере и времени выполнения инструкций в Arm?

Kyle Oliveira 30.05.2019 12:51

У меня обновление...!

Kyle Oliveira 30.05.2019 18:28

Чтобы уведомлять людей, когда вы отвечаете, используйте @username.

Peter Cordes 30.05.2019 22:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
12
1 337
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Cortex-M Только поддерживает режим Thumb, а не режим ARM, поэтому указание вашему компилятору компилятору для Cortex-M0+ гарантирует, что он создаст код Thumb2.

Вот почему вам нужно установить младший бит вашего целевого адреса.

https://en.wikipedia.org/wiki/ARM_Cortex-M#Instruction_sets

Only Thumb-1 and Thumb-2 instruction sets are supported in Cortex-M architectures; the legacy 32-bit ARM instruction set isn't supported.

Если вы на самом деле посмотрите на байты кода в памяти, вы увидите, что это инструкции Thumb2.

Вопрос только в том, как ваш отладчик умудрился сделать это ошибкой нет. Возможно, в любом случае он должен специально обрабатывать BLX и не эмулировать отказ при переключении в режим ARM в поведении микроархитектур только для большого пальца. Или, может быть, просто обработка одноэтапного прерывания заканчивается правильным возвратом в режиме Thumb.


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

Ответ принят как подходящий

Проблема была в адресе ветки (как правильно указал @PeterCordes). Переход к оперативной памяти выполнялся по следующему коду (слегка скорректированному под эту аудиторию):

// Prepare address and Function Pointer
uint32_t codeBufferAddress = reinterpret_cast<uint32_t>(&buffer) + offset;
void(*methodFromRAM)(void*) = reinterpret_cast<void(*)(void*)>(codeBufferAddress | 0x01);

// Branch to RAM
// thisPointer holds an object byte-stream...
(*methodFromRAM)(reinterpret_cast<void*>(thisPointer));

Обратите внимание, что в строке 3 codeBufferAddress объединяется с 0x01 (codeBufferAddress | 0x01), что гарантирует, что ветвь будет выбрана в Режим большого пальца. К тому моменту, когда я опубликовал этот вопрос, значение, хранящееся в codeBufferAddress, было 0x20003E40, что явно не устанавливает LSBit и, таким образом, заставит MCU работать в Режим ОХРАНЫ.

Я думаю, что компилятор не сможет вывести и настроить режим ветвления, потому что адрес динамически генерируемый (хотя я думаю, что это может быть немного умнее и заставить все ветки работать в Режим большого пальца, поскольку мой MCU может декодировать только Thumb).

В любом случае, при работе в пошаговом режиме я не мог наблюдать никаких изменений в целевом адресе, и пока могу только догадываться, что заставляет MCU работать в Режим большого пальца... но это только предположение. Есть идеи?

К.

Другие вопросы по теме