Zynq UltraScale+平台memset触发未处理对齐错误的问题求助
Zynq UltraScale+上memset操作预留FPGA内存触发对齐错误
问题描述
在运行Linux内核v4.14的Zynq UltraScale+板卡上,通过设备树预留了FPGA端的AXI内存区域,使用/dev/mem映射到用户空间后,所有常规内存操作(如for循环赋值)正常,但调用memset()时触发对齐错误并崩溃,错误信息如下:
[ 2386.395781] test[3691]: unhandled alignment fault (7) at 0x7f9cb88080, esr 0x92000061, in libc-2.27.so[7f9c926000+13b000] ... Bus error (core dumped)
设备树配置
预留内存的设备树片段:
memory@0 { device_type = "memory"; reg = <0x0 0x0 0x0 0x7ff00000>, <0x00000008 0x00000000 0x0 0x80000000>; }; reserved-memory { compatible = "xlnx,reserved-memory"; #address-cells = <2>; #size-cells = <2>; ranges; reserved: buffer@800000000 { no-map; reg = <0x00000008 0x00000000 0x0 0x40000000>; }; };
复现代码
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include "parameters.h" int configure_memory_allocation(size_t size, unsigned int **virtual_addr, uint64_t physical_address) { int memory_handle = open("/dev/mem", O_RDWR | O_SYNC); if (memory_handle < 0) { perror("open"); return 1; } *virtual_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, memory_handle, physical_address); if (*virtual_addr == MAP_FAILED) { perror("mmap"); close(memory_handle); return 1; } close(memory_handle); return 0; } int main() { size_t size = 4096; uint64_t physical_address = 0x800000000; unsigned int *ram_vaddress = NULL; if (configure_memory_allocation(size, &ram_vaddress, physical_address) != 0) { fprintf(stderr, "Failed to configure memory allocation.\n"); return 1; } printf("configure_memory_allocation successfully\n"); // 正常工作的for循环赋值 for (int i = 0; i < size / 4; i++) { ram_vaddress[i] = i; } for (int i = 0; i < 10; i++) { printf("%p ram_vaddress[%d] = %d\n", &ram_vaddress[i], i, ram_vaddress[i]); } // 触发错误的memset memset(ram_vaddress, 0, size); munmap(ram_vaddress, size); return 0; }
原因分析
问题核心是glibc的memset优化实现与FPGA端AXI外设的访问约束不匹配:
- 你预留的内存是FPGA端的AXI接口内存,这类硬件通常只支持固定宽度的对齐访问(比如32位/4字节对齐),不支持更大宽度的非对齐burst访问。
- 标准glibc的memset为了提升效率,会使用ARM NEON/SIMD指令进行16字节甚至更大块的批量写入,这些指令会生成超过AXI外设支持宽度的访问请求,触发硬件对齐错误。
- 你的for循环是按4字节(
unsigned int)逐个赋值,完全符合硬件的访问约束,因此可以正常工作。
解决方案
方案1:使用自定义对齐安全的memset
实现一个基于硬件支持的访问宽度的memset,避免使用SIMD指令:
void my_memset_32(unsigned int *ptr, size_t size) { size_t words = size / sizeof(unsigned int); for (size_t i = 0; i < words; i++) { ptr[i] = 0; } // 处理剩余的非4字节对齐部分 size_t remaining = size % sizeof(unsigned int); unsigned char *byte_ptr = (unsigned char*)&ptr[words]; for (size_t i = 0; i < remaining; i++) { byte_ptr[i] = 0; } }
调用时替换memset(ram_vaddress, 0, size)为my_memset_32(ram_vaddress, size)。
方案2:禁用glibc的SIMD优化
编译时添加编译器选项,强制禁用NEON/SIMD优化,让glibc使用普通指令实现memset:
gcc -o test test.c -mno-neon
方案3:修改FPGA硬件(若允许)
调整FPGA端的AXI外设配置,开启非对齐访问支持(修改AXI接口的unaligned_access属性),或提升外设支持的burst宽度,适配glibc的优化操作。
注意事项
- 你尝试的
memset(&ram_vaddress, 0, size)是错误的,&ram_vaddress是指针变量本身的栈地址,并非映射的内存区域,会导致栈数据被破坏。 memset(&ram_vaddress[0], 0, size)与memset(ram_vaddress, 0, size)完全等价,无法解决问题。
内容的提问来源于stack exchange,提问作者Vinicius Cesar
相关产品推荐
相关产品推荐

