Как выполнить функцию из оперативной памяти на Cortex-M3 (STM32)? - PullRequest
7 голосов
/ 15 июня 2010

Я пытаюсь выполнить функцию из ОЗУ на процессоре 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 ]

7 голосов
/ 15 июня 2010

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

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

µVision позволяет найти модули в определенные области памяти, которые введено в диалог Проект - Опции - Цель . Для этого правильно нажмите на исходный файл (или файловую группу) и откройте диалоговое окно Параметры - Свойства . Затем выберите память области под Назначение памяти .

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

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

2 голосов
/ 05 июня 2013

Поскольку 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 окажется на границе нечетного полуслова, поскольку инструкция L3 M3 может обрабатывать невыровненные чтения, но четвертая (немного более быстрая и более компактная) версия, использующая LDRM, в таком случае потерпит неудачу;даже если сегодняшняя версия компилятора использует четыре инструкции LDR, будущая версия может использовать LDRM].

2 голосов
/ 15 июня 2010

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

1 голос
/ 22 октября 2012

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

...