OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 942|回复: 2

使用DMA 通过GPMC读取FPGA数据,数据错乱

[复制链接]

1

主题

1

帖子

0

精华

新手入门

积分
6
金钱
6
注册时间
2025-10-15
在线时间
1 小时
发表于 2026-5-19 19:57:36 | 显示全部楼层 |阅读模式
2金钱
参考的正点原子的官方demo,环境如下所示。
开发板:ATK-DLAM62XB
参考资料如下所示

    dma_cap_mask_t mask;
    dma_cap_zero(mask);
    dma_cap_set(DMA_MEMCPY, mask);

   channel = dma_request_channel(mask, NULL, NULL);
使用上述方式申请的DMA 通道;

    struct dma_device *dev = data->m_channel->device;
    struct dma_async_tx_descriptor *tx = NULL;
    struct dma_slave_config cfg;

    memset(&cfg, 0, sizeof(cfg));
    cfg.device_fc = false;
    cfg.dst_maxburst = 1;
    cfg.src_maxburst = 1;
    cfg.direction = DMA_MEM_TO_MEM;
    cfg.src_addr = data->m_src_addr;
    cfg.dst_addr = data->m_dst_addr;
    cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
    cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
    ret = dmaengine_slave_config(data->m_channel, &cfg);

    tx = dev->device_prep_dma_memcpy(data->m_channel,
                                    dst_addr,
                                    src_addr,
                                    data->m_data_len * sizeof(u16),
                                    DMA_CTRL_ACK | DMA_PREP_INTERRUPT);

    tx->callback = fpga_dma_complete;
    tx->callback_param = data->m_wait;

    dmaengine_submit(tx);
    dma_async_issue_pending(data->m_channel);
    wait_for_completion(data->m_wait);
使用上述方式进行搬运数据,发现从FPGA的BRAM读取的数据时混乱的,有老师能提供指导吗。


回复

使用道具 举报

0

主题

64

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
290
金钱
290
注册时间
2026-1-29
在线时间
24 小时
发表于 2026-5-20 16:46:20 | 显示全部楼层
针对您使用 DMA 通过 GPMC 读取 FPGA 数据时出现数据错乱的问题,结合提供的代码和环境信息,以下是系统性的分析和解决方案:

一、核心问题定位
根据现象描述(数据错乱)和代码逻辑,最可能的原因是 DMA 传输方向配置错误,其次是 GPMC 时序/地址对齐问题。具体分析如下:

1. DMA 传输方向错误(关键原因)
代码矛盾:您使用 DMA_MEM_TO_MEM 方向(内存到内存),但实际场景应为 从 GPMC 外设(FPGA BRAM)到内存(即 DMA_DEV_TO_MEM)。
后果:DMA_MEM_TO_MEM 会将 src_addr(可能是 CPU 映射的 GPMC 地址)当作普通内存地址拷贝,而非通过 GPMC 总线发起真实读操作,导致数据无效或错乱。
2. GPMC 时序配置不匹配
FPGA BRAM 的访问时序(如建立时间、选通脉冲宽度)需与 GPMC 控制器配置严格匹配。若时序过紧/过松,会导致 FPGA 无法正确响应或数据采样错误。
3. 地址对齐问题
GPMC 访问 FPGA BRAM 时,若地址未按总线宽度(如 32 位)对齐,可能导致数据错位(例如 32 位总线上访问 16 位地址边界)。
4. 缓存一致性问题
如果目标缓冲区位于缓存中,DMA 写入后未刷新缓存,CPU 读取时可能命中旧数据。
二、解决方案
步骤 1:修正 DMA 传输方向
将 DMA_MEM_TO_MEM 改为 DMA_DEV_TO_MEM,并明确指定 GPMC 设备:

      

// 修改前
cfg.direction = DMA_MEM_TO_MEM;

// 修改后
cfg.direction = DMA_DEV_TO_MEM; // 从设备(GPMC)到内存

      
同时确保 src_addr 是 GPMC 寄存器的物理地址(通过 ioremap 映射):

      


// 示例:假设 GPMC 基地址为 0x80000000
void __iomem *gpmc_base = ioremap(0x80000000, SZ_4K);
cfg.src_addr = gpmc_base + FPGA_BRAM_OFFSET; // FPGA BRAM 在 GPMC 空间中的偏移

      
步骤 2:检查 GPMC 时序配置
在 DTSI 文件或板级初始化代码中,调整 GPMC 时序参数以匹配 FPGA 需求。例如:

      

      
&gpmc {
    pinctrl-names = "default";
    pinctrl-0 = <&gpmc_pins>;
    status = "okay";
    /* 关键参数示例 */
    ti,chip-select = <0>;
    ti,burst-length = <4>;     /* 突发长度(根据 BRAM 宽度调整) */
    ti,access-time = <2>;       /* 访问时间(单位:时钟周期) */
    ti,page-release = <true>;   /* 允许页释放优化 */
};

      
关键参数说明:
access-time:从地址有效到数据有效的延迟(需 ≥ FPGA BRAM 的 tACC)。
burst-length:若 BRAM 支持突发读取,设置为对应值;否则设为 1。
步骤 3:强制地址对齐
确保 src_addr 和 dst_addr 按总线宽度对齐(如 32 位对齐):


// 示例:保证 src_addr 是 4 字节对齐
uint32_t aligned_src = ALIGN(cfg.src_addr, 4);
uint32_t aligned_dst = ALIGN(cfg.dst_addr, 4);

      
并在 DMA 提交时使用对齐后的地址:


tx = dev->device_prep_dma_memcpy(data->m_channel,
                                aligned_dst,
                                aligned_src,
                                data->m_data_len * sizeof(u16),
                                DMA_CTRL_ACK | DMA_PREP_INTERRUPT);

      
步骤 4:禁用 CPU 缓存(临时验证)
若怀疑缓存问题,可临时禁用目标缓冲区的缓存:

      

// 在分配 DMA 缓冲区时使用 dma_alloc_coherent
void *dma_buf = dma_alloc_coherent(&pdev->dev, size, &dma_handle, GFP_KERNEL);
// 确保后续操作使用 dma_buf 而非普通 kmalloc 分配的内存

      
步骤 5:增加调试手段
直接验证 GPMC 读操作:
绕过 DMA,用 readl() 直接读取 GPMC 地址空间,确认是否能获得正确数据:

      

        
u32 val = readl(gpmc_base + FPGA_BRAM_OFFSET);
printk("Direct read: 0x%08x\n", val); // 观察输出是否符合预期

      
若直接读取也出错 → 问题在 GPMC 配置或硬件连接。
若直接读取正常 → 问题在 DMA 配置。
抓取总线波形:
使用逻辑分析仪监测 GPMC 的 CS#、WE#、RD# 及数据线,确认:

片选信号持续时间是否足够。
读写时序是否符合 FPGA BRAM 手册要求。
数据线上的信号是否稳定无毛刺。
三、其他注意事项
DMA 通道独占性:
确保 dma_request_channel 成功获取通道,且未与其他驱动冲突:

      


if (!channel) {
    pr_err("Failed to request DMA channel\n");
    return -EBUSY;
}

      
完成回调与同步等待:
您的代码中使用了 wait_for_completion,需确保 fpga_dma_complete 回调确实调用了 complete():

      

        
void fpga_dma_complete(void *param) {
    struct completion *wait = (struct completion *)param;
    complete(wait);
}

      
数据长度单位:
确认 data->m_data_len 的单位是 字数(word) 还是 字节数(byte)。若为字数,则无需乘 sizeof(u16);若为字节数,则需除以 sizeof(u16)。

四、总结
优先级        检查项        解决方法
高        DMA 方向配置        改为 DMA_DEV_TO_MEM
高        GPMC 时序        根据 FPGA BRAM 手册调整 access-time、burst-length 等参数
中        地址对齐        使用 ALIGN() 确保地址按总线宽度对齐
低        缓存一致性        使用 dma_alloc_coherent 分配非缓存缓冲区
可选        硬件信号完整性        用示波器/逻辑分析仪测量 GPMC 信号质量
请优先尝试 修正 DMA 方向 和 调整 GPMC 时序,这两个因素最可能导致数据错乱。
回复

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
19
金钱
19
注册时间
2026-3-24
在线时间
2 小时
发表于 2026-5-22 11:41:10 | 显示全部楼层
本帖最后由 hmcjn 于 2026-5-22 13:34 编辑

你现在DMA读FPGA BRAM乱,不一定是FPGA数据乱,可能是DMA访问方式和FPGA/GPMC 能接受的访问方式不匹配。尤其是你以为配置了16位访问,但 DMA_MEMCPY 很可能没理这个配置。
可以先排查:
DMA的读取方向是否正确;
先用CPU ioread16() 连续读BRAM,看数据是不是正常;
DMA 目标 buffer 改成 dma_alloc_coherent();
源地址确认用的是GPMC/BRAM物理地址,不是ioremap()地址;
FPGA里放递增数据,比如0x0000,0x0001,0x0002...,看乱法是字节反了、半字反了、固定错位,还是随机错位。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-6-16 18:08

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表