Как выполнить функцию из оперативной памяти на Cortex-M3 (STM32)?
Я пытаюсь выполнить функцию из ОЗУ на процессоре Cortex-M3 (STM32). Функция стирает и перезаписывает внутреннюю флэш-память, поэтому мне определенно нужно быть в оперативной памяти, но как мне это сделать?
Я попробовал следующее: скопировать функцию в байтовый массив в ОЗУ, используя memcpy (проверка правильности выравнивания), установить указатель функции так, чтобы он указывал на байтовый массив, а затем вызвать функцию (указатель).
Это нормально работает для 10 инструкций (я могу следить за выполнением с помощью отладчика), но затем я получаю ошибку ошибки и процессор перезагружается. Ошибка buss возникает при втором проходе через цикл, поэтому код должен быть в порядке (так как он работает на первом проходе). Я думаю, что более быстрый доступ к ОЗУ каким-то образом портит синхронизацию шины...
В любом случае, есть ли правильный способ сделать это? Как будет выглядеть разбросанный файл, который автоматически помещает функцию в ОЗУ (я использую Keil uVision для Cortex-M3)?
Изменить: Подробнее: Набор инструментов: RealView MDK-ARM V 4.10 Компилятор: Armcc v4.0.0.728 Ассемблер: Armasm v4.0.0.728 Компоновщик: ArmLink v4.0.0.728 Процессор: STM32F103ZE
Бит IMPRECISERR устанавливается в регистре ошибок шины, когда происходит сброс.
4 ответа
Сбой при итерации цикла, вероятно, связан с тем, что функция ветвится по абсолютному адресу и не связана с расположением новой функции в ОЗУ. Приведет ли доступ к исходному коду в этой точке из-за ошибки шины из-за операции удаления флэш-памяти?
Я считаю, что вы можете пометить функцию, которая будет скомпилирована и скопирована в RAM правильно с помощью CARM, добавив __ram
директива к определению функции. Для получения инструкций о том, как сделать то же самое с компилятором RealView, см. Статью технической поддержки ИСПОЛНИТЕЛЬНЫЕ ФУНКЦИИ В ОЗУ:
µ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
приземлился на нечетной границе полуслова, так как инструкция LDRM3 может обрабатывать не выровненные чтения, но четвертая (немного более быстрая и более компактная) версия, использующая LDRM, в таком случае потерпит неудачу; даже если сегодняшняя версия компилятора использует четыре инструкции LDR, будущая версия может использовать LDRM].
С помощью компилятора IAR (я знаю, что ваш вопрос касается Keil, но у меня нет с ним возможности играть), вы можете пометить весь проект или отдельный файл как "независимые от позиции". Использование этого в прошлом с другими процессорами означает, что вы можете переместить его "куда угодно", и он все равно будет работать нормально