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

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

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

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

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

Edit: дополнительная информация: Цепочка инструментов: RealView MDK-ARM V 4.10 Компилятор: Armcc v4.0.0.728 Ассемблер: Armasm v4.0.0.728 Линкер: стойки В4.0.0.728 Процессор: STM32F103ZE

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

4 ответов


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

Я считаю, что вы можете отметить функцию, которая будет скомпилирована и скопирована в ОЗУ правильно с CARM, добавив __ram директива для определения функции. Для инструкция о том, как сделать то же самое с Компилятор RealView видит ВЫПОЛНЕНИЕ ФУНКЦИЙ В ОЗУ статья службы технической поддержки:

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

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

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


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


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


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