Как выполнить функцию из ОЗУ на Cortex-M3 (STM32)?

Я пытаюсь выполнить функцию из ОЗУ на процессоре Cortex-M3 (STM32). Функция стирает и перезаписывает внутреннюю вспышку, поэтому я определенно должен быть в ОЗУ, но как это сделать?

Я пробовал это: Скопируйте функцию в массив байтов в ОЗУ с помощью memcpy (проверяя, что он правильно выровнен), установив указатель на функцию, чтобы указать на массив байтов, а затем вызвать функцию (указатель).

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

Во всяком случае, есть ли правильный способ сделать это? Как бы выглядел файл рассеяния, который автоматически устанавливает функцию в ОЗУ (я использую Keil uVision для Cortex-M3)?

Редактировать: Дополнительная информация: Toolchain: RealView MDK-ARM V 4.10 Компилятор: Armcc v4.0.0.728 Ассемблер: Armasm v4.0.0.728 Линкера: ArmLink v4.0.0.728 Процессор: STM32F103ZE

Бит IMPRECISERR устанавливается в регистре ошибок шины, когда происходит сброс.

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

Я считаю, что вы можете пометить функцию, которая будет скомпилирована и скопирована в ОЗУ правильно с помощью __ram добавив директиву __ram в определение функции. Инструкции по тому, как сделать то же самое с компилятором RealView, см. В статье технической поддержки EXECUTING FUNCTIONS IN RAM :

μVision позволяет находить модули в определенных областях памяти, которые вводятся в диалоговом окне Project – Options – Target . Для этого щелкните правой кнопкой мыши на исходном файле (или группе файлов) и откройте диалоговое окно « Параметры – Свойства» . Затем выберите области памяти в разделе « Назначение памяти» .

В папке ARMExamplesRAM_Function есть пример .

Это должно генерировать код запуска, чтобы позаботиться о копировании функции в ОЗУ и правильно подключить вызовы к этому местоположению. В противном случае, если вам нужно динамически копировать произвольные функции в ОЗУ, загляните в компиляционный независимый код (PIC) с RealView.

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

Поскольку ARM имеет ограниченную способность загружать немедленные данные, утилит, которые генерируют код для ARM, часто сопоставляют код и данные. Например, утверждение типа

 void myRoutine(void) { myVar1=0x12345678; myVar2=0x87654321; } 

может закончиться чем-то вроде:

 myRoutine: ldr r0,=myVar1; Load the address of _myVar ldr r1,=0x12345678 str r1,[r0] ldr r0,=myVar1; Load the address of _myVar ldr r1,=0x87654321 str r1,[r0] bx lr which would get translated into: ldr r0,dat1 ldr r1,dat2 str r1,[r0] ldr r0,dat3 ldr r1,dat4 str r1,[r0] bx lr ... followed some time later by dat1 dcd _myVar1 dat2 dcd 0x12345678 dat3 dcd _myVar2 dat4 dcd 0x12345678 or perhaps even something like: mar r0,dat1 ldrm r0,[r1,r2,r3,r4] str r2,[r1] str r4,[r3] bx lr ... followed some time later by dat1 dcd _myVar1 dat2 dcd 0x12345678 dat3 dcd _myVar2 dat4 dcd 0x12345678 

Обратите внимание, что _myVar и 0x12345678 могут быть размещены сразу же после кода для подпрограммы, в которой они отображаются; если вы попытаетесь определить длину подпрограммы с помощью метки, которая следует за последней инструкцией, такая длина не сможет включить дополнительные данные.

Еще одна вещь, которую следует отметить с помощью ARM, заключается в том, что по историческим причинам кодовые адреса часто имеют наименее значимый бит, даже если код действительно начинается с границ полуслов. Таким образом, инструкция, адрес которой равен 0x12345679, будет занимать либо два, либо четыре байта, начиная с 0x12345678. Это может усложнить расчет адресов для таких вещей, как memcpy.

Моя рекомендация – написать небольшую рутину на ассемблере, чтобы сделать то, что вам нужно. Это всего лишь несколько инструкций, вы можете точно знать, что делает код и какие у него есть зависимости, и вам не придется беспокоиться о том, что будущие версии компилятора меняют ваш код таким образом, чтобы что-то сломать (например, третье версия вышеуказанного кода не будет иметь проблем, даже если dat1 приземлился на нечетной границе полуслова, так как инструкция LDR M3 может обрабатывать невыровненные чтения, но четвертая (немного более быстрая и компактная) версия с использованием LDRM в этом случае потерпит неудачу; даже если сегодняшняя версия компилятора использует четыре инструкции LDR, в будущей версии может использоваться LDRM].

С компилятором IAR (я знаю, что ваш вопрос касается Keil, но у меня его нет), вы можете отметить либо весь проект, либо отдельный файл, который будет «независимым от положения». Из использования этого в прошлом с другими процессорами это означает, что вы можете переместить его «в любом месте», и он все равно будет работать нормально