关于cortex-m内核非对齐访问0x1fffffff出错问题
1.问题发现
通常在类似STM32这种MCU平台开发程序时,因为内存资源有限, 在遇到一些结构体的size不对齐到4字节时,编译器会强制4字节对齐,从而浪费了内存空间,因此常常会有对结构体使用__packed的语法来强制修改对齐方式,这种优化方式可以使非4字节对齐size的结构体占用更小的ram空间,但会导致结构体数据存放地址为非4字节对齐的地址,譬如:0x1FFB0001开始处的1个word为一个32位的数据或指针。
在这个背景下有时候会产生一些莫名奇妙的bug,经过仔细的分析后,最终确认了一个问题cortex-m内核非对齐访问0x1fffffff出错问题。
2.调试定位
一个错误实例就是使用__packed限制结构体对齐方式后,程序中产生了一个异常结果,而使用默认的4字节对齐方式则不会产生错误结果。通过在c代码中产生异常结果值进行断言跟踪,逐步缩小出错范围,对改汇编代码单步调试并检查cpu的通用寄存器,最终发现一处汇编语句出现错误,如下:
ldr r0, [r6]
执行该语句前 r6值为0x1fffffff,执行该语句即从内存中0x1fffffff地址加载一个word数据到r0寄存器内,r0中的数据与内存中0x1fffffff-0x20000003数据不相符合,遂产生异常输出
3.仿真验证
为验证该问题,编写一个测试代码如下:
int main()
{
volatile int *data = (volatile int*)(0x1ffffffc);
data[0] = 0x12345678;
data[1] = 0xfdcba987; //准备一些数据填入0x1ffffffc到0x20000004内存中
__asm volatile("ldr r1, =0x1fffffff");
__asm volatile("ldr r0,[r1]"); //使用ldr指令非对齐加载0x1fffffff
while(1);
}
上述程序运行完成后期望在r0得到数据为0xcba98712,在芯片上实测结果不符。仅低8位正确为0x12,高24位为错误值。
4.资料查找
经过查找在 https://electronics.stackexchange.com/questions/224328/mcu-ram-why-accessing-ram-across-the-boundary-causes-bus-fault 找到一处描述:
Unaligned accesses that cross memory map boundaries are architecturally Unpredictable. The processor behavior is boundary dependent, as follows:
- DCode accesses wrap within the region. For example, an unaligned halfword access to the last byte of Code space (0x1FFFFFFF) is converted by the DCode interface into a byte access to 0x1FFFFFFF followed by a byte access to 0x00000000.
即在cortex-m核内对0x1fffffff进行非对齐访问会读取到0x0地址的数据而不是0x20000000.