中级会员
 
- 积分
- 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 时序,这两个因素最可能导致数据错乱。 |
|