简体   繁体   English

这是ARM编译器代码生成错误吗?

[英]Is this an ARM Compiler code generation error?

I am working on an embedded system which includes an ARM Cortex-M4 CPU and a few peripherals. 我正在一个包含ARM Cortex-M4 CPU和一些外围设备的嵌入式系统上工作。 One of the peripherals contains blocks of SRAM which are accessible from the CPU side (via the AHB bus), but access must be a word size transaction (using LDR). 外设之一包含SRAM块,可从CPU端(通过AHB总线)访问SRAM块,但是访问必须是字长事务(使用LDR)。 If a byte transaction is performed (LDRB), an exception is generated. 如果执行字节事务(LDRB),则会生成异常。

In my code, I am reading a value from an array in that memory and assigning it to a local variable. 在我的代码中,我正在从该内存中的数组读取一个值,并将其分配给局部变量。 The declarations are like this: 声明如下:

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!

When running this code, I get an exception when reading the memory. 运行此代码时,读取内存时出现异常。 It happens that this assignment generates the assembly code: 碰巧,此分配生成了汇编代码:

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

This is also true even when I add parentheses and explicitly cast the expression: 即使添加括号并显式转换表达式也是如此:

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

The way to resolve this was to declare arr_type as uint32_t instead of type_t . 解决此问题的方法是将arr_type声明为uint32_t而不是type_t Now, the code is: 现在,代码是:

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

Is this an expected behaviour? 这是预期的行为吗? I'd assume that the parentheses and the cast (if not the natural type of the array pointer) would make the compiler generate and LDR instruction. 我假设括号和强制转换(如果不是array指针的自然类型)会使编译器生成LDR指令。 Hence this looks like a bug. 因此,这看起来像个错误。

The compiler can normally use whatever load instructions it wants so long as it works, from perspective of the program itself, as if the 32-bit load implied by array[offset] was performed. 从程序本身的角度来看,编译器通常可以使用所需的任何加载指令,只要它可以工作,就好像执行了array[offset]所隐含的32位加载一样。 Since the value loaded ends up being truncated to 8 bits when stored in arr_type , it doesn't change the value stored in arr_type if the compiler uses an 8-bit load instead. 由于加载的值存储在arr_type时最终会被截断为8位,因此,如果编译器改为使用8位加载,则不会更改arr_type存储的值。

To tell the compiler that size of the memory access is important and has visible effects outside of the program itself, you should use the volatile qualifier. 为了告诉编译器内存访问的大小很重要,并且在程序本身之外具有可见的效果,您应该使用volatile限定符。

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

arr_type = (type_t) array[offset]; 

More generally you should use the volatile qualifier when performing any sort of memory mapped I/O. 通常,在执行任何类型的内存映射I / O时应使用volatile限定符。 It not only ensures that accesses are always performed using the intended size (where possible), it also guarantees that that accesses aren't removed or reordered. 它不仅确保始终使用预期大小执行访问(在可能的情况下),而且还确保访问不会被删除或重新排序。

如果使用标志--enum_is_int进行编译,则对枚举的访问将为32位并对齐。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM