Это ошибка генерации кода компилятора ARM?

Я работаю над встроенной системой, которая включает в себя процессор ARM Cortex-M4 и несколько периферийных устройств. Одна из периферийных устройств содержит блоки SRAM, которые доступны со стороны ЦП (через шину AHB), но доступ должен быть транзакцией размера слова (с использованием LDR). Если выполняется байтовая транзакция (LDRB), генерируется исключение.

В моем коде я читаю значение из массива в этой памяти и присваиваю его локальной переменной. Объявления такие:

typedef enum
{
     eType0 = 0,
     eType1 = 1,
} type_t;

type_t    arr_type;
uint32_t *array  = BUF_ADDR; // array on periph. memory
uint32_t  offset = 0;

arr_type = (type_t) array[offset]; // exception!

При запуске этого кода я получаю исключение при чтении памяти. Бывает, что это назначение генерирует код сборки:

LDRB R1, [R2, R3, LSL #2]; // R2=array, R3=offset

Это также верно, даже когда я добавляю круглые скобки и явно приводю выражение:

type = (uint32_t) (array[offset]);

Чтобы решить эту проблему, нужно было объявить arr_type как uint32_t вместо type_t, Теперь код:

LDR R1, [R2, R3, LSL #2];

Это ожидаемое поведение? Я бы предположил, что скобки и приведение (если не естественный тип array указатель) заставит компилятор генерировать и LDR инструкция. Следовательно, это выглядит как ошибка.

2 ответа

Решение

Компилятор обычно может использовать любые инструкции загрузки, которые он хочет, до тех пор, пока он работает, с точки зрения самой программы, как если бы 32-битная загрузка подразумевалась array[offset] был выполнен. Так как загруженное значение в конечном итоге обрезается до 8 бит при сохранении в arr_type, это не меняет значение, хранящееся в arr_type если вместо этого компилятор использует 8-битную загрузку.

Чтобы сообщить компилятору, что размер доступа к памяти важен и имеет видимые эффекты вне самой программы, вы должны использовать volatile Классификатор.

type_t    arr_type;
uint32_t volatile *array = BUF_ADDR;
uint32_t  offset = 0;

arr_type = (type_t) array[offset]; 

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

Если вы скомпилируете с флагом --enum_is_int, ваш доступ к enum будет 32-битным и выровненным.

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