约 2 分钟 阅读 31

仪器主控的数据面答案不是一个现成 IP,而是一套自己写的流水线:槽 FIFO → 自研 SG DMA 引擎 → AXI HP → DDR 环形缓冲。这篇记录它从"机制验证"到"连续流零失配"的四刀过程——重点是中间抓到的那些 bug,它们比成功本身更有记录价值。

目标与边界

Zynq 的 R5 实时核要拿到 PL(FPGA 侧)采集的数据:PL 把数据经 AXI HP 口搬进 PS DDR,R5 和 A53 两个消费端各自校验。四刀按依赖切分:

  1. 机制:先用 Xilinx 官方 AXI DMA 跑通"PL→HP→DDR"这条路本身;
  2. 自研引擎:换成按自己契约写的 SG(分散-聚集)DMA 引擎,描述符链驱动;
  3. 环形流:一次性传输升级为连续环形,写指针发布 + overrun 检测;
  4. 真帧源:8b/10b 链路层 + 仪器数据帧格式(同步字/序列号/时标/CRC16)替换测试码发生器。

结果先报

  • 单段 3×4KB 逐字校验零失配;
  • 连续环形流:A53 消费端跟随 150 圈 / 1.85MB 零失配;LVDS 真帧源后拉到 59.1MB 零失配
  • 人为制造消费停滞:引擎 overrun 标志、空洞计数正确置位,恢复后跳读的数据仍然全对。

值钱的在中间:六个实捕的 bug

1. 测试码发生器首字偏移。 装载拍没门控,首发字用了旧计数器值——整条数据流偏一个字。教训:源头的第一拍和最后一拍是边界 bug 最密集处。

2. 写使能遇满丢数还撒谎。 寄存器版 wr_en 在 FIFO 满时丢数据但计数照走——数据少了、计数说没少。改成组合逻辑 !full 门控。教训:计数器必须数"真正写进去的",不是数"想写的"。

3. FIFO 在途读竞态。 count==1 时多发了一拍读,数据成对重复。修:可用量要扣掉在途读。这类"流水线上还有一拍在路上"的账,波形不瞪大眼睛看是发现不了的。

4. 环头写 W 通道抢 AW 通道。 AXI 写通道握手不严格,千余迭代后互联矩阵永久撤回 wready——死锁。改严格 AW→W 顺序。教训:AXI "两个通道独立"不等于"可以乱序发车"。

5. R5 长阻塞命令被看门狗腰斩。 一个 >1.2s 的忙等命令冻住了心跳,安全监控核判定 R5 死亡并重启了它——测试到一半板子自己复位了。修:长命令拆成非阻塞的"开/停"两条。教训:调试命令也得遵守实时域的心跳纪律。

6. 消费端追尾竞态。 读指针用了一份冻结快照,LVDS 段速比预期快一个量级,读指针滑出安全窗读到"超前一圈"的覆写段。修:每段消费前重读最新写指针。

两个顺手的认知修正

  • 文档里一直记着"A53 读不了保留内存窗"——实测是 Python mmap 的参数坑(默认读写权限撞上只读 fd),加 O_SYNC 的非缓存映射完全可读,还天然解决了 cache 一致性。旧结论划线作废。
  • PL 到 R5 的中断线目前不通(中断域被硬件墙隔开),轮询先顶着——44µs 量级够用,但不是终态,记档留后续。

还没做的

引脚级 1.25Gbps 环回(现在是 fabric 级环回,等背板回环卡)、多槽聚合、AXI 加宽到 128bit、中断化。架构占位构建已经证明资源宽裕(LUT 占用 16.7%),剩下的都是工程量。

标签: none

添加新评论