Это ошибка генерации кода компилятора 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-битным и выровненным.