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

ZYNQ MPSoC A53首次解引用指针值错误后续读取正常

问题背景

在ZYNQ Ultrascale+ MPSoC Genesys ZU开发板上运行应用程序,PL端部署的加速器通过简易AXI DMA与PS端连接,DMA经PS端普通非一致性FPD从端口读写DDR内存,应用运行在PS端的其中一个A53核心上。

已通过ILA验证PL侧写入AXI从端口的数据正确,但PS端软件读回的部分数据存在错误。此前排查发现部分问题源于A53的缓存机制,作为临时方案已在程序起始处调用Xil_DCacheDisable()禁用D-cache,理论上可排除缓存干扰,但仍出现异常:首次打印/读取DMA接收的数组数据时会得到错误值,后续读取同一地址均返回正确值。通过Vitis调试器内置的内存查看器已确认,DMA写入完成后,目标内存地址中存储的实际数据是正确的。

核心简化代码

已移除无关逻辑的核心程序如下:

#define CACHE_LINE_SIZE 64

int main(void)
{
    Xil_DCacheDisable();

    // DMA初始化逻辑
    ...

    // 通过DMA向加速器发送数据,该环节无异常
    ...
    
    float* outputCorrelation;
    const size_t outputCorrelationSizeBytes = sizeof(*outputCorrelation) * 80;
    outputCorrelation = aligned_alloc(CACHE_LINE_SIZE, outputCorrelationSizeBytes);
    if(outputCorrelation == NULL) {
        printf("Aligned Malloc failed\n");
        return XST_FAILURE;
    }

    // 先启动DMA数据接收传输
    int result = XAxiDma_SimpleTransfer(&axiDma,(UINTPTR) outputCorrelation, outputCorrelationSizeBytes, XAXIDMA_DEVICE_TO_DMA);
    if(result != XST_SUCCESS) {
        return result;
    }

    // 启动DMA发送,assembledData分配逻辑无问题故省略
    result = XAxiDma_SimpleTransfer(&axiDma,(UINTPTR) assembledData, sizeof(*assembledData) * inLen, XAXIDMA_DMA_TO_DEVICE);
    if(result != XST_SUCCESS) {
        return result;
    }

    // 等待DMA传输完成中断
    ...

    for(size_t x = 0; x < 80; x++) {
        printf("[%zu]\t%f\n", x, outputCorrelation[x]);
    }
}
异常表现

程序预期输出为数组所有元素值为4,实际全速运行时输出如下:

[0] -nan
[1] 4.000000
[2] 4.000000
[3] 4.000000
[4] 4.000000
...
[79] 4.000000

如果在for循环前提前读取/打印数组的任意元素,首个元素会变为正确值,循环内所有输出均正常。

已完成排查记录
  • 曾怀疑编译器优化消除了对未被CPU写入的数组的读操作,尝试将输出缓冲区标记为volatile,异常现象无变化;
  • 将PL加速器连接至PS的LPD端口,换用RPU(Cortex-R5)而非APU(Cortex-A53)运行完全相同的代码,可得到预期正确结果,初步判断即使禁用D-cache,A53端运行时仍存在内存访问一致性相关问题;
  • 单步执行代码时问题不会复现,使用调试器但直接全速运行关键代码段时问题可稳定复现。
根因分析

该问题由Cortex-A53核心的内存访问预取/流水线缓冲残留旧数据导致,和D-Cache本身的缓存一致性无直接关系:

  1. Xil_DCacheDisable()仅会关闭D-Cache的tag匹配查询逻辑,不会自动清空已经填充在缓存行、L1/L2内存访问流水线、读预取单元、Load/Store队列里的旧数据,也不会同步关闭A53核心默认开启的读预取、写合并缓冲机制。
  2. 代码中outputCorrelation缓冲区是刚通过aligned_alloc申请的堆内存,若该内存页此前被CPU访问过,对应首地址所在的64字节缓存行(对应前16个float元素,首元素恰好位于该行起始位置)的旧数据会残留在CPU的Load缓冲区/预取队列中。DMA完成DDR写入后,CPU第一次读取该地址时,会优先返回缓冲区里的残留旧值,第一次读操作同时会触发对应缓冲区的无效刷新,因此后续读取会直接从DDR拿到正确数据。
  3. 单步调试时,调试器会频繁读取内存触发缓冲区刷新,提前清空残留旧数据,因此问题不会复现;Cortex-R5核心的内存访问流水线深度远小于A53,没有复杂的乱序读预取缓冲机制,因此换RPU运行不会出现该问题。
可行解决方案
  • 方案1(推荐,符合Xilinx官方开发规范):不要依赖全局禁用D-Cache解决一致性问题,在DMA传输前后主动做对应地址范围的缓存维护。启动DMA接收传输前,调用Xil_DCacheInvalidateRange((UINTPTR)outputCorrelation, outputCorrelationSizeBytes),主动将对应地址范围的缓存、预取缓冲全部标记为无效,强制CPU后续读操作直接从DDR获取最新数据。注意调用该接口时地址需64字节对齐,长度需向上对齐到64字节的整数倍,当前代码已经做了64字节对齐,仅需补充长度对齐逻辑即可。
  • 方案2(临时验证用):如果坚持全局禁用D-Cache,在调用Xil_DCacheDisable()后立刻调用Xil_DCacheInvalidate()刷空整个D-Cache及关联的访问缓冲,同时配置A53的SCTLR寄存器关闭指令/数据预取、写合并功能,避免残留数据影响内存访问。
  • 方案3(性能最优):将DMA连接到PS端的ACP(加速器一致性端口),由硬件自动维护PL与PS之间的缓存一致性,不需要软件做任何缓存刷写操作,从根源上避免这类一致性问题。

内容的提问来源于stack exchange,提问作者Christopher Moore

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 19:29:00