跳转到内容
新建笔记

XC7A35T 无 DDR XDMA C2H 直通采集链路

本阶段已经完成一条面向手持式振动分析仪的、不依赖 FPGA 外部 DDR3 的 PCIe 采集基线:

flowchart LR
SRC["确定性四通道测试源<br/>未来替换为 AD7768 接收器"]
FIFO["128 bit × 2048 帧<br/>XPM 异步 FIFO"]
PKT["固定 64 KiB 分包器<br/>64-bit AXI4-Stream"]
XDMA["XDMA C2H Stream<br/>PCIe Gen1 x1"]
RAM["i.MX8MP 内存<br/>软件环形缓冲区"]
SRC --> FIFO --> PKT --> XDMA --> RAM

控制通路为:

flowchart LR
APP["i.MX8MP 应用"]
USER["/dev/xdma0_user"]
LITE["XDMA M_AXI_LITE<br/>1 MiB aperture"]
CSR["自定义 AXI-Lite CSR<br/>启动、停止、计数、溢出状态"]
APP --> USER --> LITE --> CSR

设计目标器件是 XC7A35T-FGG484-2。当前 35T 已完成综合、布局、布线、时序、DRC 和资源门限验证,说明该数据链路可以放入 35T;但产品 PCB 管脚仍未签核,所以脚本故意禁止生成 35T bitstream。

真板验证使用现有的正点原子达芬奇 Pro XC7A200T-FBG484-2。同一套 RTL 和 XDMA 配置已在 A200T 与飞凌 i.MX8MP 间通过完整回归,包括连续传输、启停、清零竞争、反压、强制溢出、恢复和 10,000 包耐久测试。

1.1 必须准确区分的状态

跳转到“1.1 必须准确区分的状态”
范围当前状态
RTL 行为仿真已通过
A35T 综合、布局布线、DRC、时序和资源门限已通过
A35T 产品管脚、精确订货型号和 bitstream未签核,禁止烧录
A200T bitstream已生成并通过真板回归
XDMA AXI-Stream C2H 与 AXI-Lite CSR已在 A200T + i.MX8MP 真板验证
FPGA 外部 DDR3、MIG、SmartConnect、AXI DMA当前方案不使用
AD7768 数据接收和 SPI 控制尚未接入
光电键相/转速输入尚未接入
i.MX8MP 产品级环形缓冲和振动算法不属于本阶段 FPGA 范围

因此,本页证明的是:35T 资源和时序可行,A200T 上的等价 PCIe 流式链路已通过硬件测试。它还不能证明 AD7768 真板采样或 35T 产品板已经完成。

2. 为什么从 DDR3 方案切换到直通方案

跳转到“2. 为什么从 DDR3 方案切换到直通方案”

早期基线采用 XDMA AXI-MM -> SmartConnect -> MIG -> FPGA DDR3,用于证明 PCIe、XDMA 和板载 DDR3 可读写。之后也实现过 AD7768 -> AXI DMA S2MM -> DDR3 -> XDMA 的 A200T 方案。两者仍然是有价值的历史回归基线,但不再是 35T 手持产品的主线。

2.1 带宽并不是主要矛盾

跳转到“2.1 带宽并不是主要矛盾”

按四通道、每通道 32 bit、每秒约 256 k 帧计算:

256000 frame/s × 4 channel × 4 byte = 4.096 MB/s

当前测试时钟的精确值为:

frame rate = 62.5 MHz / 244 = 256147.541 frame/s
payload = 256147.541 × 16 = 4.098361 MB/s
application stream = payload + 64-byte packet headers ≈ 4.102367 MB/s

4.102367 MB/s 是包含自定义 64-byte 包头的 AXI/C2H 应用层数据率,不包含 PCIe TLP、DLLP 和 8b/10b 编码开销,不能称为 PCIe 物理线速。

这远低于 PCIe Gen1 x1 的有效吞吐能力。只要 i.MX8MP 采集进程持续提交 C2H 请求,没必要为了约 4.1 MB/s 的数据流,在 35T 产品上再增加 MIG、外部 DDR、AXI DMA 和多主设备互连。

2.2 “i.MX8MP 始终在线”的工程含义

跳转到“2.2 “i.MX8MP 始终在线”的工程含义”

这里不是说处理器永远不会掉电,而是说在一次有效采集会话中:

  1. i.MX8MP 已启动 Linux 和采集服务;
  2. PCIe 链路保持建立;
  3. 用户态或内核态缓冲持续提交 C2H 读取;
  4. 大容量缓存放在 i.MX8MP DRAM 中;
  5. FPGA 只吸收短时 PCIe/软件抖动,不承担离线记录。

如果产品要求 SoC 睡眠时 FPGA 仍持续采集、PCIe 长时间不可用仍不得丢样,或者断电前必须保存大段波形,那么无 DDR 直通方案的前提就不成立,需要重新评估 FPGA 外部内存或独立存储。

2.3 35T 方案的收益与代价

跳转到“2.3 35T 方案的收益与代价”

收益:

  • 去掉 MIG、AXI DMA、DDR 数据互连和 FPGA 外部 DDR;
  • 降低器件资源、PCB 复杂度、功耗、启动校准和故障定位成本;
  • 数据只经过一次 FIFO 和分包,不需要 DDR 写入后再读出;
  • 大容量环形缓冲、文件写入和算法均由 i.MX8MP 统一管理。

代价:

  • FPGA 的 32 KiB FIFO 只能吸收约 8 ms 的反压;
  • 软件必须连续挂起 C2H 请求;
  • 当前协议没有“中止半包”命令;
  • 发生长时间 PCIe 停顿时会明确计数丢帧,而不是继续无限缓存。

3.1 数据、控制和验证三条路径

跳转到“3.1 数据、控制和验证三条路径”
flowchart TB
subgraph DATA["数据路径"]
S["capture_test_source<br/>4 × 32-bit"] --> F["capture_async_fifo<br/>128-bit × 2048"]
F --> P["capture_packetizer<br/>64 KiB / packet"]
P --> C["XDMA S_AXIS_C2H_0<br/>64-bit"]
end
subgraph CONTROL["控制路径"]
U["XDMA M_AXI_LITE"] --> R["capture_axil_csr"]
R -->|"start / rate / safe clear"| S
F -->|"level / full / overflow"| R
P -->|"frames / packets"| R
end
subgraph HOST["i.MX8MP"]
DEVU["/dev/xdma0_user"]
DEVC["/dev/xdma0_c2h_0"]
TEST["capture_hw_test.c"]
TEST --> DEVU
TEST --> DEVC
end
U --- DEVU
C --- DEVC

XDMA 在 Gen1 x1、64-bit AXI4-Stream 配置下输出 axi_aclk = 62.5 MHz。当前测试源和 AXI4-Stream 读侧都暂时使用该时钟,但 FIFO 仍然使用真正的 xpm_fifo_async,因为未来 AD7768 接入后:

  • 写时钟会变成 ADC 数据时钟域;
  • 读时钟仍是 XDMA axi_aclk;
  • 128-bit 帧数据只能通过双时钟 FIFO 跨域;
  • 计数器通过 Gray 码同步,单 bit 状态通过两级触发器同步。

这使测试基线与未来真实采样的跨时钟结构一致,避免接入 ADC 时重写整条数据通路。

4. 工程资产和开发方式

跳转到“4. 工程资产和开发方式”

下载完整 FPGA 可复现源码包

源码包 SHA-256 在第 17 节给出。包内不含 Vivado 缓存、运行目录、报告和 bitstream;这些输出必须由脚本重新生成。

adfpga/
├── README.md
├── adfpga.xpr
├── constraints/
│ ├── a200t/davinci_pro.xdc
│ └── a35t/provisional_pcie.xdc
├── ip/
│ └── capture_xdma/capture_xdma.xci
├── rtl/
│ ├── capture_top.sv
│ ├── capture_datapath.sv
│ ├── capture_test_source.sv
│ ├── capture_async_fifo.sv
│ ├── capture_gray_sync.sv
│ ├── capture_packetizer.sv
│ └── capture_axil_csr.sv
├── sim/
│ ├── tb_capture_datapath.sv
│ ├── tb_capture_axil_csr.sv
│ └── tb_capture_block_size.sv
├── scripts/
│ ├── create_project.tcl
│ ├── run_sim.ps1
│ ├── program_a200t.tcl
│ └── check_project.tcl
└── tools/
└── capture_hw_test.c

4.3 VS Code 与 Vivado 的分工

跳转到“4.3 VS Code 与 Vivado 的分工”

推荐工作方式是:

  • VS Code 编辑 rtl/、sim/、constraints/、scripts/ 和 C 测试工具;
  • Vivado 2026.1 负责 XDMA IP、仿真、综合、实现、时序、DRC 和下载;
  • scripts/create_project.tcl 是器件、IP 参数和构建门限的权威来源;
  • adfpga.xpr 便于图形界面打开同一组源文件,但不应成为唯一配置来源。

使用 VS Code 不影响 Vivado 打开工程。修改源文件后,Vivado 会读取同一文件;若增删源文件或修改 XDMA 参数,应同步更新 Tcl 脚本,并用脚本重建工程。

Vivado 版本固定为 2026.1。脚本会检查版本并拒绝其他版本,以避免 IP 升级或生成结果无声变化。

脚本创建 DMA/Bridge Subsystem for PCI Express 4.2,关键参数如下:

参数值作用
ModeBasic / DMAPCIe DMA Endpoint
AXI interfaceAXI Stream直接传输采集流,不走 AXI-MM DDR
PCIe linkGen1 x12.5 GT/s、Lane 0
AXI stream width64 bit对应当前 XDMA 接口
AXI clock62.5 MHzXDMA 产生 axi_aclk
C2H channels1FPGA 到 i.MX8MP 数据流
H2C channels1XDMA 要求保留,设计中接收后丢弃
AXI-Lite masterEnabled暴露自定义 CSR
AXI-Lite aperture1 MiB/dev/xdma0_user 访问范围
Device ID10ee:7011Linux 枚举标识
MSI1 vectorXDMA DMA 完成中断
MSI-XDisabled当前不使用
User interrupttied low当前没有独立 usr_irq_req
PLLCPLL与板级 PCIe 基线一致

对应的 Tcl 配置核心如下:

set_property -dict [list \
CONFIG.mode_selection {Basic} \
CONFIG.xdma_axi_intf_mm {AXI_Stream} \
CONFIG.axi_data_width {64_bit} \
CONFIG.pl_link_cap_max_link_width {X1} \
CONFIG.pl_link_cap_max_link_speed {2.5_GT/s} \
CONFIG.axisten_freq {62.5} \
CONFIG.xdma_pcie_64bit_en {true} \
CONFIG.plltype {CPLL} \
CONFIG.xdma_wnum_chnl {1} \
CONFIG.xdma_rnum_chnl {1} \
CONFIG.axilite_master_en {true} \
CONFIG.axilite_master_size {1} \
CONFIG.axilite_master_scale {Megabytes} \
CONFIG.pf0_device_id {7011} \
CONFIG.pf0_msi_enabled {true} \
CONFIG.pf0_msi_cap_multimsgcap {1_vector} \
CONFIG.pf0_msix_enabled {false} \
] $xdma_ip

虽然 H2C 不是采集所需方向,XDMA 仍会生成 M_AXIS_H2C_0。顶层把 m_axis_h2c_tready_0 固定为 1,持续接收并丢弃数据,避免未使用的 H2C 通道反向阻塞 IP。

顶层完成以下工作:

  1. 用 IBUFDS_GTE2 接收 PCIe 100 MHz 差分参考时钟;
  2. 实例化 XDMA;
  3. 将 XDMA C2H AXI4-Stream 接到采集数据通路;
  4. 将 XDMA AXI-Lite Master 直连 CSR;
  5. 用四级复位流水线产生采集逻辑本地同步复位;
  6. 将 user_lnk_up 输出到板载链路指示灯;
  7. 将独立用户中断固定为 0。

复位释放代码:

(* ASYNC_REG = "TRUE" *) reg [3:0] capture_resetn_pipe = 4'b0000;
always @(posedge axi_aclk) begin
if (!axi_aresetn)
capture_resetn_pipe <= 4'b0000;
else
capture_resetn_pipe <= {capture_resetn_pipe[2:0], 1'b1};
end
wire capture_aresetn = capture_resetn_pipe[3];

6.2 确定性测试源 capture_test_source.sv

跳转到“6.2 确定性测试源 capture_test_source.sv”

测试源每帧产生四个 32-bit 字:

channel0 = {8'hA0, source_sequence[23:0]};
channel1 = {8'hA1, source_sequence[23:0]};
channel2 = {8'hA2, source_sequence[23:0]};
channel3 = {8'hA3, source_sequence[23:0]};
frame = {channel3, channel2, channel1, channel0};

四通道使用相同的 24-bit 源序号,因此软件可以同时检查:

  • 通道顺序是否正确;
  • 128 bit 拆成两个 64-bit beat 后是否错位;
  • AXI4-Stream 反压期间数据是否保持;
  • FIFO 溢出时,硬件丢帧计数是否与 payload 序号缺口一致;
  • 24-bit 序号回绕是否正确处理。

RATE_DIVIDER 在启动时锁存。寄存器为 0 时使用默认值 244;应在停止状态下修改,再启动新会话。

为了使每次安全停止都落在完整包边界,测试源收到停止请求后会完成当前 4092 帧块,不会在半包位置突然停下。

安全清理采用计数 baseline 实现会话统计归零,不会复位 payload 中的 24-bit 源序号。该序号跨会话连续,约 65.498 秒回绕一次;cycles 和 race 测试正是按模 2^24 检查跨会话连续性。

6.3 异步 FIFO capture_async_fifo.sv

跳转到“6.3 异步 FIFO capture_async_fifo.sv”

关键参数:

参数值
Primitivexpm_fifo_async
写/读宽度128 bit
深度2048 帧
容量32 KiB
存储资源Block RAM
读模式FWFT
CDC stages2
prog_full深度减 16
ECCDisabled

核心实例:

xpm_fifo_async #(
.CDC_SYNC_STAGES(2),
.FIFO_MEMORY_TYPE("block"),
.FIFO_READ_LATENCY(0),
.FIFO_WRITE_DEPTH(2048),
.PROG_FULL_THRESH(2032),
.READ_DATA_WIDTH(128),
.READ_MODE("fwft"),
.RELATED_CLOCKS(0),
.SIM_ASSERT_CHK(1),
.WRITE_DATA_WIDTH(128)
) fifo_i (...);

2048 帧在默认测试速率下只能覆盖:

2048 / 256147.541 ≈ 7.995 ms

所以它用于时钟域隔离和短时反压,不是大容量波形缓存。

6.4 Gray 码计数同步 capture_gray_sync.sv

跳转到“6.4 Gray 码计数同步 capture_gray_sync.sv”

源域先把二进制计数转换为 Gray 码并寄存,再通过带 ASYNC_REG 属性的两级触发器进入目标域,最后转换回二进制。这样避免二进制计数器多个 bit 同时翻转造成撕裂。

这些计数用于运行状态和诊断,不替代未来的采样时间戳。接入 AD7768 后,如果要求精确定位丢失样点,数据帧本身仍应包含硬件采样序号或时间戳。

6.5 64 KiB 分包器 capture_packetizer.sv

跳转到“6.5 64 KiB 分包器 capture_packetizer.sv”

分包器状态机:

stateDiagram-v2
[*] --> IDLE
IDLE --> HEADER: FIFO 出现第一个帧
HEADER --> PAYLOAD_LOW: 8 个 header beat 已发送
PAYLOAD_LOW --> PAYLOAD_HIGH: 取出一个 128-bit 帧
PAYLOAD_HIGH --> PAYLOAD_LOW: 未到第 4092 帧
PAYLOAD_HIGH --> IDLE: 最后一帧高 64 bit + TLAST

AXI4-Stream 的任何状态变化都以 TVALID && TREADY 为准。TREADY=0 时,TDATA、TKEEP、TLAST 和状态机保持不变。

分包器不会等 FIFO 预存完整 4092 帧才发送 header;只要第一个帧到达就开始一包,后续帧不足时在 payload 阶段等待。运行中的测试源会补齐完整块,安全停止也会先补齐当前块,因此正常会话最终仍以完整包结束。

每个输入帧是 128 bit,而 XDMA 是 64 bit,所以:

  • PAYLOAD_LOW 输出 CH0、CH1;
  • PAYLOAD_HIGH 输出 CH2、CH3;
  • 只有高 64-bit beat 被接受后,该帧才计入 transmitted;
  • 最后一帧的高 64-bit beat 同时置 TLAST=1。

TKEEP 始终为 8'hFF。

6.6 AXI-Lite CSR capture_axil_csr.sv

跳转到“6.6 AXI-Lite CSR capture_axil_csr.sv”

AXI-Lite 的 AW 和 W 通道没有规定必须同周期到达。CSR 分别锁存地址和数据,只有两者都收到后才执行一次写操作;B/R 响应在对端未 ready 时保持稳定。仿真覆盖 AW 先到、W 先到、同时到达、写 strobe 和响应反压。

版本 1.1 还修复了 clear/start 竞争:清理进行中时,新的 start 命令被拒绝,防止旧会话尚未排空就开始新会话。

核心门控逻辑:

if (wdata_hold[1]) begin
test_enable <= 1'b0;
if (!datapath_status[6])
clear_stats <= 1'b1;
end else if (!wdata_hold[0] || !datapath_status[6]) begin
test_enable <= wdata_hold[0];
end

所有寄存器通过 /dev/xdma0_user 访问,均为 32-bit little-endian。

偏移属性名称含义
0x00ROMAGIC0x31424956,字节序显示为 VIB1
0x04ROVERSION0x00010001
0x08ROBUILD_ID0x20260816
0x0CRW/命令CONTROLbit 0 启动;bit 1 停止并请求安全清理
0x10RWRATE_DIVIDER测试源分频;0 等价于 244
0x14ROSTATUS链路、运行、清理、FIFO 和溢出状态
0x18ROSOURCE_FRAMES已产生帧数,含未写入 FIFO 的帧
0x1CROACCEPTED_FRAMES成功写入 FIFO 的帧数
0x20RODROPPED_FRAMESFIFO 满导致的丢帧数
0x24ROTRANSMITTED_FRAMES已被 XDMA AXI-Stream 接受的完整帧数
0x28ROPACKET_COUNTTLAST 已被 XDMA 接受的 64 KiB 包数
0x2CROFIFO_LEVEL当前 FIFO 帧数
0x30ROFIFO_HIGH_WATERMARK本会话 FIFO 最高水位

CONTROL.bit1 是写命令而不是可保持配置位;读回 CONTROL 只反映 bit 0 的当前启动状态。

TRANSMITTED_FRAMES 和 PACKET_COUNT 在 TVALID && TREADY 时递增,只证明 XDMA 已接受数据,不单独证明 PCIe TLP 已发送或数据已经写入 i.MX8MP 用户缓冲。端到端完成必须以主机 65,536-byte read 返回并通过 payload 校验为准。

STATUS 位定义:

bit含义
31PCIe user_lnk_up
6stop/clear 事务待完成
5数据源正在运行或补齐当前块
4FIFO 读侧处于 reset busy
3FIFO 写侧处于 reset busy
2FIFO 溢出/丢帧粘滞标志
1FIFO prog_full
0FIFO 中存在数据

7.1 安全停止和清理协议

跳转到“7.1 安全停止和清理协议”
sequenceDiagram
participant H as i.MX8MP 软件
participant C as CSR
participant S as 测试源
participant P as FIFO/分包器
H->>C: CONTROL = 0,停止继续产生新块
S->>S: 补齐当前 4092 帧块
H->>P: 保持 65536-byte C2H 读取
P-->>H: 排空最后一个完整包
H->>C: CONTROL = 2,请求清理
C->>P: 等待 source quiet、FIFO empty、packetizer idle
P-->>C: pipeline idle
C->>S: 跨域 clear toggle
S-->>C: clear acknowledge
C-->>H: STATUS.bit6 = 0,所有会话计数为 0

注意:清理可能在两次软件轮询之间迅速完成,软件不应要求先观察到 STATUS.bit6=1。正确做法是写入 clear 后轮询到 bit 6 为 0,并再次确认所有会话计数为 0。

64-byte header + 4092 frame × 16 byte/frame = 65536 byte

每包正好 8192 个 64-bit AXI4-Stream beat,便于 XDMA 和 Linux 应用固定长度读取。

头部由 8 个 little-endian 64-bit beat 组成:

Beatbits 31:0bits 63:32
0magic 0x31424956version/header 0x00010040
1session ID 1packet sequence
2first output-frame indexreserved 0
3frames 4092payload bytes 65472
4source frames snapshotaccepted frames snapshot
5dropped frames snapshotFIFO high watermark
6status snapshotbuild ID 0x20260816
7first output-frame index mirrorpacket sequence mirror

其中 first output-frame index 是已发送数据流中的帧偏移,不是 ADC 的绝对采样时间戳。

每帧 16 字节:

帧内字节偏移内容
+0x00CH0:{8'hA0, source_sequence[23:0]}
+0x04CH1:{8'hA1, source_sequence[23:0]}
+0x08CH2:{8'hA2, source_sequence[23:0]}
+0x0CCH3:{8'hA3, source_sequence[23:0]}

Linux 解析时按 little-endian 32-bit 字读取。序号按模 2^24 递增,默认速率下约 65.498 秒回绕一次,测试程序已覆盖回绕。

XDMA 自身的 C2H DMA 完成机制负责唤醒阻塞读取;当前 usr_irq_req 固定为 0,未使用独立 user interrupt,也不以 /dev/xdma0_events_0 作为本协议的完成条件。

8.4 长时间无缝波形的计数边界

跳转到“8.4 长时间无缝波形的计数边界”

当前 CSR 计数、包序号和 first output-frame index 均为 32 bit。在默认帧率下,帧计数约在以下时间回绕:

2^32 / 256147.541 ≈ 16767.5 s ≈ 4.658 h

测试载荷内的 24-bit 源序号更早在约 65.498 秒回绕。测试程序能按模数校验这些回绕,但单次 stream 命令会拒绝总帧数超过 0xffffffff 的请求。当前 159.754 秒耐久测试不能推出产品已具备数小时或数天的无缝长波形能力。

产品协议至少应升级为 64-bit 采样序号和 64-bit 时间戳,并做跨 32-bit 边界、跨小时、文件分段、应用重启和环形缓冲覆盖测试。

打开 Vivado 2026.1 命令行环境,确认以下命令可执行:

终端窗口
vivado -version
xvlog -help
xelab -help
xsim -help

先检查根工程没有残留旧 Block Design、器件和 XDMA 模式正确:

终端窗口
vivado -mode batch -source scripts\check_project.tcl

必须出现:

CAPTURE_ROOT_PROJECT_PASS

该检查确认根工程为 xc7a200tfbg484-2、顶层为 capture_top、Block Design 数量为 0、XDMA 使用 AXI-Stream,且 AXI-Lite Master 已启用。

然后运行三项行为仿真:

进入源码包根目录执行:

终端窗口
powershell -ExecutionPolicy Bypass -File scripts\run_sim.ps1

脚本依次运行三个测试:

测试平台覆盖内容
tb_capture_datapath.sv测试源、异步 FIFO、跨域计数、反压、分包、停止与清理
tb_capture_axil_csr.svAXI-Lite AW/W 独立握手、R/B 反压、WSTRB、clear/start 竞争
tb_capture_block_size.sv生产配置 65536 字节、4092 帧、8192 beat、TLAST 位置

通过标志必须完整出现:

CAPTURE_DATAPATH_TEST_PASS
CAPTURE_AXIL_CSR_TEST_PASS
CAPTURE_BLOCK_SIZE_TEST_PASS bytes=65536 frames=4092
CAPTURE_ALL_SIMULATIONS_PASS

只看到仿真进程退出不算通过;必须检查全部四个标志。

10. 生成工程、实现和 bitstream

跳转到“10. 生成工程、实现和 bitstream”

create_project.tcl 接受两个目标和四种动作:

target: a35t | a200t
action: create | synth | impl | build
  • create:只创建工程和 XDMA IP;
  • synth:运行综合并生成利用率报告;
  • impl:运行到 route,不生成 bitstream;
  • build:A200T 运行到 bitstream;A35T 因临时管脚仍只运行到 route。

脚本会删除自己管理的生成目录。运行前应关闭 Vivado GUI,避免工程锁或 GUI 中未保存的修改被丢失。

10.2 创建 A200T 图形工程

跳转到“10.2 创建 A200T 图形工程”
终端窗口
vivado -mode batch -source scripts\create_project.tcl -tclargs a200t create

随后可用 Vivado 2026.1 打开根目录的 adfpga.xpr。顶层是 capture_top,本设计没有 Block Design、MIG 或 AXI DMA,可在 RTL Analysis -> Open Elaborated Design 查看原理图。

终端窗口
vivado -mode batch -source scripts\create_project.tcl -tclargs a35t impl

预期末尾包含:

CAPTURE_A35T_NOTICE=route-only; product pinout is still provisional
CAPTURE_IMPLEMENTATION_PASS target=a35t

脚本对 A35T 设置工程门限:

  • LUT 不超过 17,680,即器件的 85%;
  • BRAM 不超过 40 tiles,即器件的 80%;
  • WNS 和 WHS 不得为负;
  • DRC Error 和 Critical Warning 必须为 0;
  • no_clock 和 unconstrained_internal_endpoints 必须为 0。
终端窗口
vivado -mode batch -source scripts\create_project.tcl -tclargs a200t build

生成文件:

artifacts/a200t/capture_top.bit

每次重新构建都会改变“已验证产物”的身份。只有重新完成第 12 节全部真板回归后,才能把新哈希标记为硬件验证通过。

11. A200T 下载与 PCIe 重新枚举

跳转到“11. A200T 下载与 PCIe 重新枚举”
信号管脚/约束说明
pcie_ref_clk_p[0]F6PCIe 100 MHz 差分参考时钟正端
pcie_mgt_rxp[0]D9GTP Lane 0 RX 正端
pcie_mgt_txp[0]D7GTP Lane 0 TX 正端
pcie_rst_nN15, LVCMOS33PCIe PERST#
lnk_up_ledW7, LVCMOS15user_lnk_up 指示

时钟及例外约束:

create_clock -period 10.000 -name pcie_ref_clk \
[get_ports {pcie_ref_clk_p[0]}]
set_false_path -from [get_ports pcie_rst_n]
set_false_path -to [get_ports lnk_up_led]

差分负端由对应的专用差分资源确定,不能把普通 GPIO 任意指定为负端。

先确认 JTAG 链上只有一个 xc7a200t,再执行:

终端窗口
vivado -mode batch -source scripts\program_a200t.tcl

下载脚本会检查器件数量;发现 0 个或多个候选器件时会停止,不会模糊选择。

  1. 断电连接 PCIe,确认两板供电和公共地;
  2. 给 FPGA 上电并通过 JTAG 下载新 bitstream;
  3. 硬复位或重启 i.MX8MP;
  4. 等待 Linux 重新枚举 Endpoint;
  5. 检查链路和设备节点;
  6. 再运行采集测试。

修改 XDMA 的 BAR、AXI-Stream 引擎或 IP 配置后,只执行 modprobe -r xdma; modprobe xdma 不足以让 Root Complex 重新分配资源。

终端窗口
lspci -nn
lspci -vv -s 01:00.0 | grep -E 'LnkCap:|LnkSta:|MSI:|Kernel driver'
modprobe xdma
ls -l /dev/xdma*
dmesg | grep -Ei 'pcie|pci|xdma|aer|timeout|link' | tail -n 120

当前测试至少需要:

/dev/xdma0_user
/dev/xdma0_c2h_0

预期设备为 10ee:7011、链路为 2.5GT/s x1、MSI 启用、驱动为 xdma。

源码包中的 tools/capture_hw_test.c 是完整的独立回归程序。目标根文件系统有 gcc 但没有 libc 开发头文件,因此程序在源码中声明了所需的最小 ABI,不依赖目标头文件。

本次记录环境为 Linux 5.4.70-2.3.0、XDMA 驱动日志版本 v2025.2.0。换内核或驱动后必须重新执行本节,不能直接继承 user BAR 文件语义和板测结论。

有网络时从源码包根目录传入板端:

终端窗口
scp tools/capture_hw_test.c root@<board-ip>:/tmp/capture_hw_test.c

只有串口时可在已登录的板端 shell 中使用 ZMODEM:

终端窗口
cd /tmp
rz -y

无论采用哪种方式,都在板端校验:

终端窗口
sha256sum /tmp/capture_hw_test.c

预期 SHA-256:

ae049dcde68fea89e03ebc7044eeb61516888ab6883e1333470621531d03a03b

把文件放到板端 /tmp/capture_hw_test.c 后执行:

终端窗口
gcc -O2 -Wall -Wextra /tmp/capture_hw_test.c -o /tmp/capture_hw_test

编译必须无 error。warning 也应逐项检查,不要直接忽略。

12.2 当前 /dev/xdma0_user 的特殊行为

跳转到“12.2 当前 /dev/xdma0_user 的特殊行为”

当前实测 XDMA 驱动的 user 字符设备:

  • 拒绝 lseek;
  • 每次 read 只前进一个 32-bit BAR word;
  • 测试工具每次重新 open,从偏移 0 逐字读到目标寄存器;
  • 写目标寄存器前也先顺序读到对应位置。

因此不能照搬旧 DDR 教程中的 lseek + read/write 方法,也不要用 dd skip/seek 猜寄存器偏移。若替换驱动版本,应先重新验证 user BAR 的文件操作语义。

开始回归前保存内核日志基线:

终端窗口
dmesg > /tmp/dmesg.before.txt
终端窗口
/tmp/capture_hw_test info

必须出现:

IDENTITY_PASS magic=31424956 version=00010001 build=20260816 ...

并确认:

  • STATUS.bit31=1;
  • CONTROL 为 0;
  • FIFO 为空;
  • source、accepted、dropped、transmitted、packet 计数均为 0。

如果版本不是 0x00010001,停止测试,先确认 bitstream 和重新枚举过程。

12.4 100 次启停、排空和清理

跳转到“12.4 100 次启停、排空和清理”
终端窗口
/tmp/capture_hw_test cycles 100

该测试每次运行两个完整包,验证启动、停止、补齐当前块、C2H 排空和清零。通过标志:

CYCLES_PASS cycles=100 packets=200 frames=818400
HW_TEST_RC=0

12.5 100 次 clear/start 竞争

跳转到“12.5 100 次 clear/start 竞争”
终端窗口
/tmp/capture_hw_test race 100

测试在第二个包待处理时发出 clear,紧接着尝试 start。版本 1.1 必须拒绝清理期间的 start。通过标志:

CLEAR_START_RACE_PASS cycles=100 packets=200 frames=818400
HW_TEST_RC=0
终端窗口
/tmp/capture_hw_test stream 1000

通过条件:

  • 每包恰好 65,536 字节;
  • 包序号连续;
  • 每帧 A0/A1/A2/A3 标签正确;
  • 24-bit 源序号连续;
  • dropped 为 0;
  • 停止后 accepted 等于 transmitted;
  • 安全清理后所有计数归零;
  • 最终 HW_TEST_RC=0。
终端窗口
/tmp/capture_hw_test pause 4000

测试先启动数据源,再故意让 C2H 读取暂停 4000 微秒。预期:

BACKPRESSURE_PASS pause_us=4000 dropped=0 hwm=<诊断值>
HW_TEST_RC=0

硬判据是 dropped 为 0;高水位用于记录裕量,不要求固定数值。本次实测为 1204/2048。它验证 32 KiB FIFO 能覆盖设计目标内的短时主机抖动。

12.8 50 ms 强制溢出与恢复

跳转到“12.8 50 ms 强制溢出与恢复”
终端窗口
/tmp/capture_hw_test overflow 50000

50 ms 明显超过 FIFO 覆盖时间,测试故意制造溢出。这里“发生丢帧”是预期行为,真正的验收点是:

  • STATUS.bit2=1;
  • DROPPED_FRAMES > 0;
  • FIFO 高水位不低于 prog_full 门限 2032;
  • payload 中统计的源序号缺口等于硬件 dropped 计数;
  • 安全清理后可以重新启动并正确接收;
  • 输出 OVERFLOW_RECOVERY_PASS 和 HW_TEST_RC=0。

前述测试全部通过后执行:

终端窗口
/tmp/capture_hw_test stream 10000

该测试传输 655,360,000 字节、40,920,000 帧,并跨越多次 24-bit 源序号回绕。

12.10 测试后的内核日志

跳转到“12.10 测试后的内核日志”
终端窗口
dmesg > /tmp/dmesg.after.txt
diff -u /tmp/dmesg.before.txt /tmp/dmesg.after.txt \
| grep '^+' | grep -v '^+++' \
| grep -Ei 'xdma.*timeout|aer.*error|completion timeout|link.*down'

命令无输出才表示本轮相对基线没有新增匹配项。本轮测试期间不得新增 XDMA timeout、PCIe AER、completion timeout 或 link-down。若系统没有 diff,至少记录开始前的 dmesg 行数,并只检查此后新增行,不能用一次全量 grep 区分历史错误和新错误。

13. C2H 操作中最重要的顺序

跳转到“13. C2H 操作中最重要的顺序”

第一次 C2H 读取必须遵循:

sequenceDiagram
participant APP as capture_hw_test
participant XDMA as /dev/xdma0_c2h_0
participant CSR as /dev/xdma0_user
participant FPGA as FPGA 数据源
APP->>XDMA: 先提交 65536-byte 阻塞 read
APP->>CSR: CONTROL = 1
CSR->>FPGA: 启动测试源
FPGA-->>XDMA: 64 KiB AXI4-Stream + TLAST
XDMA-->>APP: read 返回 65536
APP->>APP: 校验 header、标签、序号和计数

不要先启动数据源再慢慢准备 DMA。FIFO 只有约 8 ms 缓冲,错误顺序会把软件启动延迟误判成硬件吞吐问题。

如果进程在收到半个 C2H 包后退出,当前 FPGA 没有 force-abort 命令。恢复方法只有:

  1. 继续完成该包读取;或
  2. 重新配置/复位 Endpoint,并让 i.MX8MP 重新枚举。

以下结果来自 2026-08-16 工程回归记录。原始串口 transcript 未作为独立附件发布,因此这些数据应理解为工程记录摘要,而不是第三方可重复审计的原始日志。

测试结果
IdentityMAGIC=31424956、VERSION=00010001、BUILD_ID=20260816
100 次 cycles200 包、818,400 帧,通过
100 次 clear/start race200 包、818,400 帧,全部拒绝过早 start
1000 包连续流65,536,000 字节、4,092,000 帧、15.976 s、4.102 MB/s、0 丢帧
4 ms 反压0 丢帧,FIFO 高水位 1204/2048
50 ms 强制溢出dropped 10,922,payload gaps 10,922,高水位 2048,恢复通过
10000 包耐久655,360,000 字节、40,920,000 帧、159.754 s、0 丢帧
内核日志无新增 XDMA timeout、AER、completion timeout、link-down

最终清理后的 CSR 状态为空,证明停止、排空、清理和重启形成闭环。

15. 实现结果与 35T 产品迁移

跳转到“15. 实现结果与 35T 产品迁移”

Vivado 2026.1 记录结果:

目标LUTFFBRAMDSPWNSWHS状态
XC7A35T-FGG484-210,519 / 20,80012,670 / 41,60025 / 500 / 90+4.108 ns+0.030 nsroute-only
XC7A200T-FBG484-210,514 / 134,60012,670 / 269,20025 / 3650 / 740+2.253 ns+0.036 nsbitstream + 真板通过

两个目标均记录为:

  • DRC Error = 0;
  • DRC Critical Warning = 0;
  • 无未加时钟寄存器;
  • 无未约束内部 endpoint;
  • 自定义采集逻辑无未解释 CDC;
  • 剩余警告位于生成的 XDMA IP 内部。

A35T 的时序和 DRC 数字只适用于当前临时管脚 XDC。最终 PCB pinout 会改变布局和路由,必须重新实现;当前正时序不能作为正式管脚的时序签核。

35T 已使用约 50.6% LUT、50% BRAM,仍可接入 AD7768 接收、SPI、键相捕获、时间戳和少量预处理,但后续每次扩展都必须重新检查资源、路由拥塞和时序。不要只看“还有 LUT”,BRAM、时钟资源、GTP、IO bank 和 PCB 管脚同样可能先成为限制。

从层级利用率看,XDMA 本身约占 9,674 LUT、11,169 FF 和 17.5 BRAM;采集 datapath 约占 825 LUT、1,385 FF 和 7.5 BRAM。35T 的主要固定成本是 PCIe/XDMA,而不是测试源或分包器。按当前工程门限还剩 7,161 LUT 和 15 BRAM 的余量,适合增加采样接收、键相边沿捕获、时间戳和控制逻辑,但不应据此承诺把角度重采样、FFT 和完整动平衡算法全部放入 FPGA。

15.2 为什么目前不生成 35T bitstream

跳转到“15.2 为什么目前不生成 35T bitstream”

constraints/a35t/provisional_pcie.xdc 中的管脚只是用于 place/route 可行性分析,不能代表产品板。脚本设置 allow_bitstream=0,即使使用 a35t build 也只运行到 route。

产品化前必须确认:

  1. 精确订货型号是 XC7A35T 还是车规 XA7A35T;
  2. 封装是否确实为 FGG484;
  3. 速度级和温度等级;
  4. PCIe Lane 0 的 GTP RX/TX 管脚;
  5. 100 MHz REFCLK 差分输入及其专用资源;
  6. PERST# 电平标准和上电时序;
  7. 配置 Flash、JTAG、模式脚和电源配置;
  8. AD7768 DCLK 是否进入合适的时钟能力管脚;
  9. AD7768、SPI、键相输入的 IO bank 电压;
  10. 根据 ADC 手册和 PCB 走线补齐输入延迟约束。

只有这些项目签核后,才能替换临时 XDC、允许 35T 写 bitstream。还必须新增只接受精确产品器件的下载脚本,例如 program_a35t_product.tcl;现有 program_a200t.tcl 会严格拒绝 35T,不能复用。完成正式 XDC、产品构建脚本和 35T 下载脚本后,再在真实产品板上重复仿真、工程检查、实现、下载、枚举和第 12 节板端回归。

接入真实 ADC 时保留 packetizer、CSR 和 XDMA,替换测试源前端:

flowchart LR
ADC["AD7768-4<br/>DCLK / DRDY / DOUT"]
RX["TDM 接收与帧对齐<br/>4 × 32 bit"]
META["采样序号 / 时间戳 / 状态"]
FIFO["128-bit async FIFO"]
PKT["64 KiB packetizer"]
XDMA["XDMA C2H"]
ADC --> RX --> META --> FIFO --> PKT --> XDMA

四个 32-bit ADC 字已经占满当前 128-bit FIFO 帧,因此不能在“仍保持 16-byte 帧”时直接再塞入序号和时间戳。产品协议必须先明确以下方案之一:

  1. 扩宽内部帧,例如 256 bit,携带四通道数据、64-bit 序号和 64-bit 时间戳;固定 64 KiB 包将相应改为 64-byte header + 2046 × 32-byte frame,并升级协议版本;
  2. 保持 16-byte 数据帧,只在块头记录 64-bit 首样序号、首样时间戳和丢帧增量,同时把溢出策略改成整块丢弃,才能明确定位缺口边界;
  3. 使用独立元数据流,但必须定义它与数据包的原子对应、复位和丢失恢复规则。

在真实 AD7768 接入前应选定一种并完成带宽、BRAM、异常恢复和软件解析评审。不能同时承诺当前 128-bit 帧、当前每包 4092 帧和新增的逐帧 128-bit 元数据都保持不变。

建议分步验证:

  1. 保留测试源回归,确认 PCIe 基线未破坏;
  2. 只接入 AD7768 SPI,完成芯片 ID/复位值读回;
  3. 用仿真模型替换测试源,验证 DRDY、DCLK、MSB first 和四通道顺序;
  4. 用 ILA/示波器确认真板采样边沿;
  5. 在每帧加入硬件采样序号,验证丢样定位;
  6. 再加入键相边沿时间戳;
  7. 最后由 i.MX8MP 做角度重采样、阶次谱和动平衡矢量计算。

FPGA 不需要在 35T 内完成完整阶次谱或动平衡算法。它更适合保证确定性采样、键相时间戳、分包、丢帧可检测和 PCIe 传输;复杂浮点计算及用户界面放在 i.MX8MP。

检查:

  • FPGA 是否已下载包含 XDMA 的正确 bitstream;
  • 下载后是否硬复位/重启 i.MX8MP;
  • 100 MHz REFCLK、PERST#、RX/TX 方向和公共地;
  • 两板电源是否稳定。

此时不要先调 xdma.ko,因为 PCIe 枚举发生在驱动绑定之前。

16.2 有 PCIe 设备但没有 /dev/xdma0_user

跳转到“16.2 有 PCIe 设备但没有 /dev/xdma0_user”

检查:

  • 是否加载了当前 AXI-Stream bitstream,而不是旧 AXI-MM DDR bitstream;
  • XDMA AXI-Lite Master 是否启用;
  • Root Complex 是否在下载后重新枚举;
  • xdma.ko 的架构、vermagic 和运行内核是否匹配。

VERSION=0x00010001 才包含 clear/start 竞争修复。版本不符时不要继续压力测试;先检查 bitstream SHA-256、JTAG 下载结果和 PCIe 重新枚举。

检查顺序:

  1. 是否先提交 65536-byte C2H read;
  2. 再写 CONTROL=1;
  3. STATUS.bit31 是否为 1;
  4. STATUS.bit5 是否变为 1;
  5. source/accepted 是否增长;
  6. FIFO level 是否增长;
  7. XDMA C2H engine 是否存在 timeout。

16.5 能收到数据但长度不是 65536

跳转到“16.5 能收到数据但长度不是 65536”

重点检查:

  • header 是否为 64 字节;
  • 每包是否恰好 4092 帧;
  • TLAST 是否只在最后一帧高 64-bit beat;
  • TKEEP 是否始终 0xFF;
  • 主机 read 长度是否正好 65536。

检查:

  • FIFO 是否实际综合成 2048 × 128 bit BRAM;
  • C2H read 是否提前挂起;
  • 测试速率是否被改高;
  • TREADY 恢复后 packetizer 是否继续握手;
  • FIFO reset busy 是否意外出现。

16.7 overflow 的 dropped 与 payload gap 不一致

跳转到“16.7 overflow 的 dropped 与 payload gap 不一致”

这意味着统计或数据路径有不一致,不能把它当成“溢出本来就会丢数据”而忽略。检查源序号、FIFO 写使能、Gray 计数同步、payload 解析和 24-bit 回绕处理。

16.8 clear 后无法重新启动

跳转到“16.8 clear 后无法重新启动”

检查:

  • 软件是否继续读取并排空最后一个完整包;
  • STATUS.bit6 是否回到 0;
  • FIFO 是否为空;
  • accepted 是否等于 transmitted;
  • 所有会话计数是否归零;
  • 是否遗弃了半个 C2H 包。

16.9 不要混用旧 DDR 测试

跳转到“16.9 不要混用旧 DDR 测试”

旧的 AXI-MM DDR 基线使用 H2C/C2H 对同一 FPGA 地址写入、读回和比较。当前方案是 AXI4-Stream C2H 固定包协议:

  • 没有 FPGA DDR 地址;
  • 不支持用 dd 写随机数据再读回;
  • 不使用 AXI DMA S2MM;
  • 必须解析 64-byte header 和 payload。

两套 bitstream、设备访问方式和验收标准必须分别保存。

17. 产物、哈希和复现闭环

跳转到“17. 产物、哈希和复现闭环”
file: adfpga-xdma-stream-source-20260816.zip
contents: RTL、仿真、Tcl、XDC、XDMA XCI、A200T XPR、真板 C 工具
bitstream: 不包含
Vivado cache/runs: 不包含

源码包 SHA-256:

1404194a6ebfb92888ccb77c61356f298299cf8e6f46cc3274472cb5f9114b03

下载后可在 PowerShell 验证:

终端窗口
(Get-FileHash -Algorithm SHA256 .\adfpga-xdma-stream-source-20260816.zip).Hash

该 ZIP 及其 SHA-256 是本教程的公开、不可混淆源码快照;外部 adfpga 开发分支不作为本页复现依据。发布后应以包含本页和该 ZIP 的 VitaLogos Git commit 固定版本,避免把后续工作区内容误认为本次已验证源码。

17.2 已验证 A200T bitstream 记录

跳转到“17.2 已验证 A200T bitstream 记录”

bitstream 不在公开源码包中,必须由 Vivado 2026.1 重建。已完成板测的本地产物记录为:

file: artifacts/a200t/capture_top.bit
size: 2,059,153 bytes
SHA-256: 36AD9007714DE422B334BE9CC1480A64404BAC69FE0A00713FD785AA47DB73A7
CSR: VERSION=0x00010001, BUILD_ID=0x20260816

重新构建的 bitstream 即使 RTL 未改,也应重新计算哈希并执行真板回归,不能直接继承这份硬件验证结论。

终端窗口
# 1. 仿真
powershell -ExecutionPolicy Bypass -File scripts\run_sim.ps1
# 2. 根工程结构与 XDMA 模式
vivado -mode batch -source scripts\check_project.tcl
# 3. 35T route-only 可行性
vivado -mode batch -source scripts\create_project.tcl -tclargs a35t impl
# 4. A200T bitstream
vivado -mode batch -source scripts\create_project.tcl -tclargs a200t build
# 5. A200T JTAG 下载
vivado -mode batch -source scripts\program_a200t.tcl

下载后硬复位或重启 i.MX8MP。

终端窗口
lspci -nn
modprobe xdma
ls -l /dev/xdma0_user /dev/xdma0_c2h_0
sha256sum /tmp/capture_hw_test.c
gcc -O2 -Wall -Wextra /tmp/capture_hw_test.c -o /tmp/capture_hw_test
dmesg > /tmp/dmesg.before.txt
/tmp/capture_hw_test info
/tmp/capture_hw_test cycles 100
/tmp/capture_hw_test race 100
/tmp/capture_hw_test stream 1000
/tmp/capture_hw_test pause 4000
/tmp/capture_hw_test overflow 50000
/tmp/capture_hw_test stream 10000
dmesg > /tmp/dmesg.after.txt
diff -u /tmp/dmesg.before.txt /tmp/dmesg.after.txt \
| grep '^+' | grep -v '^+++' \
| grep -Ei 'xdma.*timeout|aer.*error|completion timeout|link.*down'

最终验收标准:仿真、A35T 实现门限、A200T build、身份检查、全部板端回归和内核日志检查同时通过。任何一项缺失,都只能声明相应子阶段通过,不能扩大成“35T 产品采集系统已经完成”。