XCZU3EG 模块化仪器平台

计量级模块化数据采集与测控系统 · 研发手记

单芯片主控 + CPCI-S 背板 10 仪器槽 + 分级智能仪器卡, Keel 框架贯穿主控与卡端。这里记录架构取舍、模拟设计、固件开发与板级实测—— 包括失败的案例。

最新文章 ↓ 关于本站

内容系列

主控

XCZU3EG:A53 应用域 + R5F 实时域 + PL 数据面

背板

CPCI-S 10 仪器槽,管理/控制/数据/同步/供电五面分离

仪器卡

示波器 / DMM / 桥路 / 信号源 / 功率计 + 扩展谱系

方法论

一切结论板上实测,推翻自己的假设也照实写

最新文章

仪器主控的数据面答案不是一个现成 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%),剩下的都是工程量。

给 ZynqMP 的 R5 核写驱动,第一次访问 PS 的 I2C0 控制器,整台板子死了——不是 R5 死,是整板:A53 四核失联、串口冻结、ping 不应,连 JTAG DAP 都一起瘫掉。这篇记录从锁死到结案的完整取证过程。

现场

新固件推进去,R5 正常启动,自检一路 PASS——直到第一个 I2C 传输开始,R5 再没输出过一个字符。控制台、SSH、ICMP 全部失联。

第一反应是 R5 崩了拖累系统?不对。JTAG 读 DDR 里的共享内存环:R5 固件好好的,自检输出停在那条 I2C 用例上,没有 fault 报告。halt R5 和 A53——全部超时。JTAG 复位——目标选择直接挂死。连调试器的路都死了,只能物理断电。

取证

恢复后抓到决定性证据(锁死前一刻的系统状态):

  • ff020000.i2cruntime_status=suspendedpower/control=auto
  • 时钟树里 i2c0_ref enable_count=0——I2C0 的时钟被门控了;对照组 UART0 是 1。

然后做了一次"自杀式"复现:A53 上用 /dev/mem 直接读 I2C0 寄存器(绕开驱动)——整板再次锁死。机制坐实。

根因

一句话:Linux 内核的 cdns-i2c 驱动在运行时 PM 下把空闲的 I2C0 时钟关了;任何绕过驱动的直接访问(R5 固件、A53 /dev/mem)命中一个"没有时钟的 APB 从机",LPD APB 互连永不应答——访问它的 CPU 核死在一条 ldr 指令上,而互连锁死又拖死了整片 LPD 外设和 JTAG DAP。

两个推论值得记住:

  1. 软件超时救不了你。R5 卡在总线等待里,ldr 永不退休,超时判断代码永远执行不到。所有"先访问再说、错了超时兜底"的写法在这种故障面前全部失效——唯一有效的防护是"确保从机可被访问"。
  2. 内核自己的 i2cdetect/i2cget 反而是安全的:走驱动路径会先 runtime resume 把时钟打开。所以故障只在"裸机核直访"或"/dev/mem"这种绕开驱动的路径上触发——这也是它为什么潜伏期这么难被发现。

修复(三层,纵深防护)

措施
根治(Linux 侧)power/control=on,systemd 持久化(keep-i2c0-clock.service),时钟永不门控
驱动侧R5 的 I2C 驱动每次传输前重断言时钟使能位(CRL CLKACT)——防 Linux 中途再关
代码门禁驱动加"武装"标志:未武装时收发一律拒绝(-EPERM),boot 自动路径在代码层不可能碰到 I2C0;调试命令默认拦截该地址区,要加 force 才放行

板验:时钟常开后同一地址连读连过,i2csmoke 5/5(EEPROM 读写回环/页写/RTC/NAK 负路径),boot 自检全绿。

带走的经验

  • 时钟门控 × 绕过驱动的直访 = 致命组合。只要系统里有一个核/一条路径绕开 Linux 驱动直接摸硬件,就必须把对应外设的运行时 PM 钉死为常开。
  • 互连锁死级的故障,取证窗口只有"锁死前那一刻"——runtime_status 和时钟计数器是我们能抓到的最后现场。
  • 防护要分层:根治一层、驱动一层、代码门禁一层。任何一层失守都不至于再回到"物理断电"。

做仪器背板的人多半默认"控制命令走 UART"——我们最初也这么设计,然后在一次架构评审里把它整个推翻了。这篇记录推翻的过程和理由。

控制面要解决什么

一台模块化仪器主机:主控实时核(R5F)要和 10 张仪器卡的 MCU 双向通信——命令下发、配置、状态回传、固件升级分片。数据量不大(每卡 1k cmd/s 量级),但有两个硬要求:

  1. 不能丢、不能错:仪器命令错一个字节,可能就是量程切错、激励加错;
  2. 故障隔离:一张卡抽风,不能把其余 9 张卡的通信带走。

旧方案:LVDS 星型 + 裸 UART

最初的设计是每槽分配 LVDS 差分对,卡上 UART 直连,CRC-8 校验。看起来够用,但评审时摆出事实:

  • 异步串口没有硬件 ACK、没有自动重传——错了只能靠上层超时重试;
  • CRC-8 只检不纠,链路层健壮性跟数据面(8b/10b + CRC16 + 流控)严重不对称
  • 每槽要占 2~4 对 LVDS,10 槽就是 20~40 对差分线,挤压本就紧张的数据面引脚。

一句话:把最不可靠的链路层,用在了最不能出错的业务上。

为什么是 CAN-FD

换型候选里,CAN-FD 几乎是为这个场景定制的:

特性对控制面的意义
硬件 ACK + 自动重传链路层自己保证送达,上层不用补窟窿
多主仲裁 + 位填充10 卡共总线无需轮询调度
错误状态自隔离(bus-off)坏节点自己退出总线,天然满足故障隔离
车规级生态收发器/调试工具/工程经验全部现成

拓扑上做两段(CAN0/CAN1 各挂约 5 槽):一段物理短路只影响半台机器,不会全瘫。副产品是释放了原来控制面占的全部 LVDS 对——差分对预算从"超支"变"宽裕"。

怎么承载我们的协议

控制面跑的是自研 Keel IPC 帧(24B 头 + ≤256B 载荷 + CRC-8),比 CAN-FD 单帧的 64B 大,所以加一层分段传输:

  • 整帧按 59B 分页,每段 = 5B 段头 + 数据,收端按段序号重组(≤8 段);
  • 完整性双保险:CAN 帧自带 CRC15/17,重组完成后再验 IPC 头 CRC-8;
  • 11bit CAN ID 直接编码路由:优先级 | 槽号 | 方向,硬件仲裁器顺手把优先级调度也做了。

诚实声明与退路设计

背板多节点条件下的误码率我们还没实测——这是后续里程碑第一个月的头号行动项。但设计时就把"测不过怎么办"想好了,降级阶梯一次到位:

5M FD → 2M FD → 1M 经典 CAN(纯软件退守,硬件零改动)→ M-LVDS/RS485 兜底总线

配套选型约束:CAN-FD 收发器必须选 FD-SIC 兼容型号(主动抑制多 stub 振铃),PCB 一次到位、速率可退。架构不能被单点绑死——这是这次评审最大的收获。

边界纪律

最后明确两条"什么不走 CAN":

  • 触发不走 CAN——走 TRIG 硬线,纳秒级确定性;
  • 波形/采样数据不走 CAN——走 LVDS 数据面,CAN 只跑命令与状态,流量占比控制在 20% 以内。

总线选型不是比峰值速率,是比故障域隔离链路层对称性。这两条想清楚,答案自己就出来了。

做仪器背板的人多半默认"控制命令走 UART"——我们最初也这么设计,然后在一次架构评审里把它整个推翻了。这篇记录推翻的过程和理由。

控制面要解决什么

一台模块化仪器主机:主控实时核(R5F)要和 10 张仪器卡的 MCU 双向通信——命令下发、配置、状态回传、固件升级分片。数据量不大(每卡 1k cmd/s 量级),但有两个硬要求:

  1. 不能丢、不能错:仪器命令错一个字节,可能就是量程切错、激励加错;
  2. 故障隔离:一张卡抽风,不能把其余 9 张卡的通信带走。

旧方案:LVDS 星型 + 裸 UART

最初的设计是每槽分配 LVDS 差分对,卡上 UART 直连,CRC-8 校验。看起来够用,但评审时摆出事实:

  • 异步串口没有硬件 ACK、没有自动重传——错了只能靠上层超时重试;
  • CRC-8 只检不纠,链路层健壮性跟数据面(8b/10b + CRC16 + 流控)严重不对称
  • 每槽要占 2~4 对 LVDS,10 槽就是 20~40 对差分线,挤压本就紧张的数据面引脚。

一句话:把最不可靠的链路层,用在了最不能出错的业务上。

为什么是 CAN-FD

换型候选里,CAN-FD 几乎是为这个场景定制的:

特性对控制面的意义
硬件 ACK + 自动重传链路层自己保证送达,上层不用补窟窿
多主仲裁 + 位填充10 卡共总线无需轮询调度
错误状态自隔离(bus-off)坏节点自己退出总线,天然满足故障隔离
车规级生态收发器/调试工具/工程经验全部现成

拓扑上做两段(CAN0/CAN1 各挂约 5 槽):一段物理短路只影响半台机器,不会全瘫。副产品是释放了原来控制面占的全部 LVDS 对——差分对预算从"超支"变"宽裕"。

怎么承载我们的协议

控制面跑的是自研 Keel IPC 帧(24B 头 + ≤256B 载荷 + CRC-8),比 CAN-FD 单帧的 64B 大,所以加一层分段传输:

  • 整帧按 59B 分页,每段 = 5B 段头 + 数据,收端按段序号重组(≤8 段);
  • 完整性双保险:CAN 帧自带 CRC15/17,重组完成后再验 IPC 头 CRC-8;
  • 11bit CAN ID 直接编码路由:优先级 | 槽号 | 方向,硬件仲裁器顺手把优先级调度也做了。

诚实声明与退路设计

背板多节点条件下的误码率我们还没实测——这是后续里程碑第一个月的头号行动项。但设计时就把"测不过怎么办"想好了,降级阶梯一次到位:

5M FD → 2M FD → 1M 经典 CAN(纯软件退守,硬件零改动)→ M-LVDS/RS485 兜底总线

配套选型约束:CAN-FD 收发器必须选 FD-SIC 兼容型号(主动抑制多 stub 振铃),PCB 一次到位、速率可退。架构不能被单点绑死——这是这次评审最大的收获。

边界纪律

最后明确两条"什么不走 CAN":

  • 触发不走 CAN——走 TRIG 硬线,纳秒级确定性;
  • 波形/采样数据不走 CAN——走 LVDS 数据面,CAN 只跑命令与状态,流量占比控制在 20% 以内。

总线选型不是比峰值速率,是比故障域隔离链路层对称性。这两条想清楚,答案自己就出来了。

这里是 XCZU3EG 模块化仪器平台的研发手记。

我们在做什么

一套计量级模块化数据采集与测控系统:

  • 单芯片主控:XCZU3EG(A53 应用域 + R5F 实时域 + PL 数据面)
  • 背板:CPCI-S 10 仪器槽,五面分离(管理 / 控制 / 数据 / 同步 / 供电)
  • 仪器卡:示波器 / 数字万用表 / 应变桥 / 信号源 / 功率计 + 扩展卡谱系
  • 软件底座:Keel 框架,主控与卡端 MCU 同构部署

内容系列

系列内容
模块化架构槽接口契约、背板通信选型(为何 CAN-FD、为何否决 PCIe)
多斜 ADC多斜原理、积分器/基准设计、校准流程
Keel 框架R5F 裸金属 IPC、驱动设计、ksmoke 测试纪律
开发日记板级实测记录、实捕实录(I2C 锁死、门铃挂死……)

为什么写这个

仪器研发里,所有结论都应该是板上实测出来的,不是数据手册抄出来的。
我们会把失败的案例也写出来——包括那些让我们推翻自己假设的实捕。

—— 研发团队