You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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外设的访问约束不匹配:

  1. 你预留的内存是FPGA端的AXI接口内存,这类硬件通常只支持固定宽度的对齐访问(比如32位/4字节对齐),不支持更大宽度的非对齐burst访问。
  2. 标准glibc的memset为了提升效率,会使用ARM NEON/SIMD指令进行16字节甚至更大块的批量写入,这些指令会生成超过AXI外设支持宽度的访问请求,触发硬件对齐错误。
  3. 你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:45:53