1. 最终结论
跳转到“1. 最终结论”本阶段完成了振动分析仪 FPGA 到 i.MX8MP 应用之间的本机采集 SDK,形成以下产品数据路径:
flowchart LR ADC["AD7768-4<br/>4 通道 24-bit"] FPGA["FPGA 采样与 64 KiB 组包"] XDMA["PCIe XDMA<br/>Streaming C2H"] DEV["/dev/xdma0_c2h_0<br/>/dev/xdma0_user"] DAEMON["vib-acqd<br/>唯一硬件所有者"] IPC["Unix Domain Socket<br/>IPC 2"] LIB["libvib_acq_client.so.2<br/>C ABI 2"] APP["Qt GUI / 算法 / 记录工具"]
ADC --> FPGA --> XDMA --> DEV --> DAEMON --> IPC --> LIB --> APP候选版本信息:
| 项目 | 值 |
|---|---|
| SDK 版本 | 0.2.0 |
| SDK 分支 | codex/complete-sdk |
| 已验收提交 | f2da2c364f9892387c28e3a59d996bc23030b2f3 |
| 公开 C ABI | 2 |
| daemon IPC | 2 |
| 动态库 SONAME | libvib_acq_client.so.2 |
| 目标系统 | OK8MP,Linux 5.4.70-2.3.0,glibc 2.30 |
| FPGA 协议 | 0x00010002 |
| FPGA build | 0x20260818 |
| 通道与采样率 | 4 通道,256000 frame/s/channel |
已经可以交付 Qt/算法团队的是:
vib-acqd本机采集服务;libvib_acq_client.so.2公开 C ABI;- CMake package
VibAcq::client和pkg-config元数据; - 固定四通道交织
int32_tblock 格式; - 数据不连续、FPGA 丢帧、坏包、断序和主机缓存溢出的显式错误语义;
- systemd 服务、部署流程和实机验收工具。
必须准确保留两个独立结论:
- FPGA packetizer -> XDMA -> daemon -> C ABI 的 30 分钟传输通过。 坏包、断序、标签错误、FPGA dropped frame、FPGA ingress drop 和 host overrun 都为 0。
- 完整 AD7768 输入链路未达到零错误。
同一次长测中
fpga_frame_errors=19,表示 128-bit TDM 帧未接收完成时又检测到下一次DRDY下降沿。下一步应排查DCLK、DRDY、DOUT0的输入时序和信号完整性。
版本提醒:目标板已经部署并验证 ABI 2 产物,但截至本记录复核时,SDK 自身仓库的
codex/complete-sdk尚未合入其远端main。跨电脑继续开发前,应先按代码仓库流程 推送或合并该提交,不能只依赖某一台电脑上的工作区和目标板已安装二进制。
FPGA 侧当前产品基线和 bitstream 构建见 XC7A35T 无 DDR XDMA C2H 直通采集链路。本页只负责 i.MX8MP 本机 SDK、应用契约和验收,不重复 FPGA RTL 细节。
2. 为什么需要独立 SDK
跳转到“2. 为什么需要独立 SDK”直接让 Qt 程序打开 /dev/xdma0_c2h_0 看起来简单,但会把以下问题全部推入 GUI:
- FPGA 启停、清零和状态寄存器访问;
- XDMA 阻塞读、超时和短传输恢复;
- 64 KiB packet 校验和四通道标签校验;
- 4092-frame packet 到 4096-frame 应用 block 的重组;
- 多线程停止、客户端断开和 systemd 关闭;
- 数据丢失统计、错误传播和版本兼容;
- GUI、记录器和诊断程序竞争同一个 C2H 数据流。
因此最终采用“单一硬件所有者 + 本机 IPC + 稳定 C ABI”:
产品运行时: vib-acqd 独占 /dev/xdma0_user 和 /dev/xdma0_c2h_0
普通应用: Qt / C / C++ -> libvib_acq_client.so -> /run/vib-acqd.sock
底层诊断例外: 停止 vib-acqd -> ad7768_xdma_monitor 独占 XDMA -> 恢复 vib-acqd这样 GUI 团队只处理稳定的 block、状态和错误码,不需要理解 XDMA 描述符、FPGA CSR 或 packet header。
3. 边界与未包含内容
跳转到“3. 边界与未包含内容”本 SDK 负责:
- FPGA packet 的读取、校验和重组;
- FPGA CSR 的身份、配置、启停、清零和状态访问;
- 有界内存缓存;
- Unix Socket 服务;
- C ABI、示例程序和原始数据记录;
- 数据完整性统计、错误传播和恢复。
本 SDK 不负责:
- AD7768 SPI 寄存器方案和模拟前端精度;
- VREF、IEPE 前端增益、传感器灵敏度和零偏标定;
- FFT、包络、阶次跟踪、动平衡等算法;
- Qt 页面、绘图和交互;
- 多设备硬件同步和绝对时间基准;
- 多个独立客户端同时订阅全速数据。
SDK 保存的是 ADC integer code。工程量换算和标定属于上层:
volts = code * vref_volts / 8388608.0默认 VREF=4.096 V 时约为 0.48828125 µV/LSB,但该数字不等于整机精度。
4. 软件模块和所有权
跳转到“4. 软件模块和所有权”SDK 源码根目录记为 <sdk_src>,即产品仓库中的 interfaces/vib_acq_sdk。
<sdk_src>/├── include/vib_acq_client.h # 公开 C ABI├── src/common/ # FPGA 协议、解析、重组、Ring Buffer├── src/xdma/ # XDMA 字符设备封装├── src/fpga/ # FPGA CSR 控制├── src/daemon/ # AcquisitionService、IPC server、vib-acqd├── src/client/ # Unix Socket client 和 C ABI 实现├── tools/ # 示例、raw 记录、独占诊断├── tests/ # 无硬件单元和端到端测试├── systemd/ # vib-acqd.service 模板└── docs/ # 部署、集成和实机验收记录依赖方向固定为:
flowchart TD COMMON["common<br/>协议 / 重组 / Ring Buffer"] XDMA["xdma<br/>设备访问"] FPGA["fpga<br/>CSR 控制"] DAEMON["daemon<br/>状态机 / DMA worker / IPC"] CLIENT["client<br/>公开 C ABI"] TOOLS["client tools"] MONITOR["direct monitor"]
FPGA --> XDMA DAEMON --> COMMON DAEMON --> FPGA DAEMON --> XDMA CLIENT --> COMMON TOOLS --> CLIENT MONITOR --> COMMON MONITOR --> FPGA MONITOR --> XDMA关键所有权规则:
- DMA worker 是 C2H 唯一读取者;IPC thread 不直接读 DMA;
vib-acqd是产品运行时唯一 XDMA 所有者;- FPGA CSR 顺序访问通过同一个 mutex 串行化;
- daemon 当前只接受一个活动客户端;
vib_acq_block_t.samples始终由调用者分配和释放;vib_acq_close()不能与同一 handle 的其他 API 并发。
5. FPGA packet 与应用 block 契约
跳转到“5. FPGA packet 与应用 block 契约”5.1 FPGA packet
跳转到“5.1 FPGA packet”每次 C2H 读取固定为 65536 字节:
| 区域 | 大小 | 内容 |
|---|---|---|
| packet header | 64 B | magic、协议、序号、计数和状态 |
| payload | 65472 B | 4092 个四通道 frame |
| 合计 | 65536 B | 一次完整 XDMA transfer |
每个 frame 是 16 字节:
CH0 word, CH1 word, CH2 word, CH3 word每个 32-bit channel word:
[31:24] 通道标签:00 / 01 / 02 / 03[23:0] signed 24-bit 二补码 ADC codeSDK 必须验证标签和 packet 连续性,再将 24-bit code 符号扩展为 int32_t。
5.2 为什么 packet 是 4092 frame,应用 block 是 4096 frame
跳转到“5.2 为什么 packet 是 4092 frame,应用 block 是 4096 frame”64 KiB packet 需要保留 64-byte header,因此 payload 只能容纳:
(65536 - 64) / 16 = 4092 frameGUI 和算法更适合使用 2 的幂长度,所以 SDK 通过跨 packet 重组输出 4096-frame block:
4096 frame * 4 channel * 4 byte = 65536 byte/application block在 256000 frame/s 下:
一个 block = 4096 / 256000 = 16 ms每秒 block = 62.5原始吞吐 = 256000 * 4 * 4 = 4,096,000 byte/s重组器必须保持连续 sample index,不能在 packet 边界伪造连续性。
5.3 Ring Buffer
跳转到“5.3 Ring Buffer”默认 --ring-blocks 256:
256 * 65536 byte = 16 MiB256 * 16 ms = 4.096 s队列满时拒绝新 block 并增加 host_overruns,不覆盖尚未消费的旧数据。这样应用能明确
知道“发生了数据缺口”,而不是拿到看似连续但实际已覆盖的数据。
6. C ABI 和 Qt 接入
跳转到“6. C ABI 和 Qt 接入”6.1 生命周期
跳转到“6.1 生命周期”标准调用顺序:
vib_acq_t *acq = NULL;vib_acq_open(&acq);vib_acq_get_info(acq, &info);vib_acq_configure(acq, &config);vib_acq_start(acq);vib_acq_read_block(acq, &block, 2000);vib_acq_get_status(acq, &status);vib_acq_stop(acq);vib_acq_close(acq);调用者至少准备:
4096 frame * 4 channel = 16384 个 int32_t 元素sample_capacity 的单位是元素,不是字节。
6.2 DATA_LOSS 不是普通成功
跳转到“6.2 DATA_LOSS 不是普通成功”VIB_ACQ_ERROR_DATA_LOSS 的语义:
read_block()返回时可能仍带有一个完整 block,并设置“此前不连续”flag;stop()返回时表示停止和清理已完成,但最终排空或 FPGA 诊断发现本会话有数据丢失;- 应用必须保存诊断、标记记录不连续,并用新会话继续;
- 不得把它等同于
VIB_ACQ_OK,也不应把它误解成 daemon 未能停止。
工具退出码约定:
| 退出码 | 含义 |
|---|---|
| 0 | 完整成功 |
| 1 | 连接、I/O 或服务错误 |
| 2 | 参数错误 |
| 3 | 检测到数据不连续或非零完整性计数 |
6.3 CMake 和 pkg-config
跳转到“6.3 CMake 和 pkg-config”Qt/C++ 项目:
find_package(Qt6 REQUIRED COMPONENTS Core)find_package(vib_acq_client CONFIG REQUIRED)
target_link_libraries(vibration_gui PRIVATE Qt6::Core VibAcq::client)普通 C 项目:
cc main.c -o acquisition_app $(pkg-config --cflags --libs vib_acq_client)系统目录安装后执行 ldconfig。产品部署不要依赖临时 LD_LIBRARY_PATH。
6.4 Qt 线程模型
跳转到“6.4 Qt 线程模型”vib_acq_read_block() 是阻塞调用,不能放在 GUI 线程:
flowchart LR WORKER["采集 worker / QThread<br/>read_block 全速读取"] DSP["算法线程<br/>FFT / 包络 / 统计"] GUI["GUI 线程<br/>降采样波形 / 状态"]
WORKER --> DSP WORKER --> GUI建议:
- 一个 worker 持有唯一
vib_acq_t; - worker 连续读取完整 block;
- 算法线程消费全速 integer code;
- GUI 只接收降采样波形、频谱结果和状态;
- GUI、记录器和示例不能同时各自打开 handle;
- 如果未来需要 GUI 与记录器并行,必须增加进程内分发或服务端发布/订阅,不能让多个 进程竞争 C2H/Ring Buffer。
7. SDK 实现阶段
跳转到“7. SDK 实现阶段”阶段 1:建立可测试的数据层
跳转到“阶段 1:建立可测试的数据层”先在不依赖硬件的层中完成:
- FPGA packet 常量和 header 定义;
- 64 KiB packet 解析;
- 通道标签和 signed 24-bit 转换;
- 4092 -> 4096 frame 重组;
- 固定容量 Ring Buffer;
- Windows/Linux 主机单元测试。
这样可以在接触真实 XDMA 前锁定数据格式和边界条件。
阶段 2:封装 XDMA 和 FPGA CSR
跳转到“阶段 2:封装 XDMA 和 FPGA CSR”建立两层:
src/xdma只负责字符设备、顺序读写和系统错误;src/fpga负责 identity、配置、STOP、CLEAR、START 和状态快照。
FPGA 协议版本不匹配、link down、clear timeout 都必须阻止启动,不能降级成警告。
阶段 3:实现 daemon 状态机
跳转到“阶段 3:实现 daemon 状态机”AcquisitionService 负责:
- 启动顺序;
- DMA worker 生命周期;
- packet 校验、block 重组和 Ring Buffer;
- 完整性累计计数;
- 停止、排空和恢复;
- 单客户端资源释放。
阶段 4:实现 IPC 和公开 C ABI
跳转到“阶段 4:实现 IPC 和公开 C ABI”IPC 固定小端编码,不发送带 padding 的 native struct。每个请求包含 magic、version、 opcode、request id 和 payload length,并对长度、版本和 request id 做严格校验。
公开头文件不暴露 STL、Qt、C++ class、异常、文件描述符、socket 或 FPGA packet 类型。
阶段 5:工具、打包与服务化
跳转到“阶段 5:工具、打包与服务化”完成:
vib_acq_example:公开 API 冒烟和统计;ad7768_record_raw:无 header 原始数据记录;ad7768_xdma_monitor:停 daemon 后直接诊断 FPGA/XDMA;- CMake package 和 relocatable pkg-config;
- systemd unit;
- 外部纯 C consumer 构建测试;
- OK8MP 交叉构建、安装包和目标板验收。
8. 主机开发与验证
跳转到“8. 主机开发与验证”8.1 Windows 严格构建
跳转到“8.1 Windows 严格构建”cd <sdk_src>cmake --preset debug -DVIB_ACQ_WARNINGS_AS_ERRORS=ONcmake --build --preset debugctest --preset debuggit diff --check最终结果:Windows 12/12 测试通过。
Windows 构建用于 parser、重组、Ring Buffer、CSR mock、IPC、状态机和工具参数验证。 Windows 运行时不提供 Unix Domain Socket 硬件客户端,不能替代 Linux 端到端测试。
8.2 Linux 原生严格构建
跳转到“8.2 Linux 原生严格构建”cd <sdk_src>
cmake -S . -B build/linux-host -G Ninja \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTING=ON \ -DVIB_ACQ_BUILD_TESTS=ON \ -DVIB_ACQ_WARNINGS_AS_ERRORS=ON
cmake --build build/linux-host -j"$(nproc)"ctest --test-dir build/linux-host --output-on-failure最终结果:Linux strict、ASan/UBSan 和 TSan 均为 16/16。Sanitizer 构建的原始命令没有 在项目文档中固化,本页不凭记忆伪造;下一次发布应把 sanitizer preset 或 CI 命令提交 到仓库,使结果可重复。
8.3 外部消费端验证
跳转到“8.3 外部消费端验证”测试必须实际安装到隔离 staging,再从一个纯 C 工程执行:
find_package(vib_acq_client CONFIG REQUIRED)target_link_libraries(package_consumer PRIVATE VibAcq::client)另用 pkg-config 构建第二个 consumer。这样能发现以下问题:
- 导出 target 缺依赖;
- 安装 include 路径错误;
.pc文件写死构建机路径;- SONAME 或符号链接错误;
- 头文件 ABI 与动态库不一致。
9. 选择正确的 OK8MP 工具链
跳转到“9. 选择正确的 OK8MP 工具链”9.1 目标兼容性
跳转到“9.1 目标兼容性”目标板:
Linux 5.4.70-2.3.0glibc 2.30AArch64最终使用:
/opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-linuxGCC 9.2glibc 2.30 sysroot不能使用:
/opt/fsl-imx-wayland/6.18-wrynose/environment-setup-armv8a-poky-linuxGCC 15.2glibc 2.43 sysroot原因不是“新编译器不好”,而是用 glibc 2.43 sysroot 链接的产物可能要求目标 rootfs
不存在的新符号,导致板上加载失败。最终 AArch64 产物检查到的最高 glibc 要求为
GLIBC_2.17,兼容当前目标板。
9.2 Zeus 环境中的 CMake 版本陷阱
跳转到“9.2 Zeus 环境中的 CMake 版本陷阱”加载 Zeus 环境后,PATH 前面的 CMake 是 3.15.3,而项目要求至少 3.16。解决办法是:
- 保留 Zeus 提供的编译器、sysroot 和
OE_CMAKE_TOOLCHAIN_FILE; - 显式使用构建主机
/usr/bin/cmake3.28.3; - 不要直接调用环境中排在前面的旧
cmake。
先检查:
source /opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-linux
echo "$CC"echo "$OE_CMAKE_TOOLCHAIN_FILE"/usr/bin/cmake --version10. OK8MP 交叉构建
跳转到“10. OK8MP 交叉构建”在 Linux SDK 构建主机执行:
source /opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-linux
sdk_src=/path/to/vib_acq_sdkbuild_dir="$sdk_src/build/ok8mp"staging_dir="$(mktemp -d)"host_cmake=/usr/bin/cmake
"$host_cmake" --version
"$host_cmake" -S "$sdk_src" -B "$build_dir" -G "Unix Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_LIBDIR=lib \ -DBUILD_TESTING=OFF \ -DVIB_ACQ_BUILD_TESTS=OFF \ -DVIB_ACQ_BUILD_TOOLS=ON \ -DCMAKE_TOOLCHAIN_FILE="$OE_CMAKE_TOOLCHAIN_FILE" \ -DVIB_ACQ_WARNINGS_AS_ERRORS=ON
"$host_cmake" --build "$build_dir" -j"$(nproc)"DESTDIR="$staging_dir" "$host_cmake" --install "$build_dir"检查安装布局:
find "$staging_dir" -type f -o -type l
file -L \ "$staging_dir/usr/sbin/vib-acqd" \ "$staging_dir/usr/lib/libvib_acq_client.so" \ "$staging_dir/usr/bin/vib_acq_example" \ "$staging_dir/usr/bin/ad7768_record_raw" \ "$staging_dir/usr/bin/ad7768_xdma_monitor"
readelf -d "$staging_dir/usr/lib/libvib_acq_client.so.0.2.0" | grep SONAME预期至少存在:
/usr/sbin/vib-acqd/usr/lib/libvib_acq_client.so*/usr/include/vib_acq_client.h/usr/bin/vib_acq_example/usr/bin/ad7768_record_raw/usr/bin/ad7768_xdma_monitor/usr/lib/pkgconfig/vib_acq_client.pc/usr/lib/cmake/vib_acq_client//usr/lib/systemd/system/vib-acqd.service预期 ELF 为 AArch64,SONAME 为 libvib_acq_client.so.2。
11. 生成安全部署包
跳转到“11. 生成安全部署包”部署归档只允许包含 usr/...,并统一为 root:root 和规范权限:
release=/tmp/vib-acq-sdk-0.2.0-ok8mp-zeus.tar.gz
tar \ --owner=0 \ --group=0 \ --numeric-owner \ --mode='u+rwX,go+rX,go-w' \ -C "$staging_dir" \ -czf "$release" \ usr
tar -tzf "$release" | sed -n '1,40p'sha256sum "$release"最终发布包 SHA-256:
21e8ecc1371be9e7a82bb3c0e26f868559eeccb18a69d3bd8eb9371b2d9df165目标板自包含测试包 SHA-256:
81686090f34cb55ad46576b2234950e96faf30cec0dc057c13d700e3437f1fa1为什么必须归一 owner:如果 staging 归档保留构建用户 UID/GID,再由 root 从 / 解包,
可能把 /usr 或服务文件变成普通用户所有。普通用户随后可以替换 root daemon,形成提权
风险。不能只保证最终二进制是 root owner,父目录也必须检查。
12. 部署到 OK8MP
跳转到“12. 部署到 OK8MP”先确认设备节点:
test -c /dev/xdma0_usertest -c /dev/xdma0_c2h_0ls -l /dev/xdma0_*停止旧服务,整体替换同一个 ABI 版本:
systemctl stop vib-acqd
tar --numeric-owner -C / -xzf /tmp/vib-acq-sdk-0.2.0-ok8mp-zeus.tar.gz/sbin/ldconfigsystemctl daemon-reloadsystemctl enable --now vib-acqd检查:
systemctl is-active vib-acqdsystemctl is-enabled vib-acqdsystemctl status vib-acqd --no-pagerjournalctl -u vib-acqd -b -n 100 --no-pagerls -l /run/vib-acqd.sock
grep '^#define VIB_ACQ_ABI_VERSION' /usr/include/vib_acq_client.hreadelf -d /usr/lib/libvib_acq_client.so.0.2.0 | grep SONAME
stat -c '%a %u:%g %n' \ / /usr /usr/bin /usr/sbin /usr/lib /usr/include \ /usr/sbin/vib-acqd \ /usr/lib/libvib_acq_client.so.0.2.0 \ /usr/lib/systemd/system/vib-acqd.service要求:
- ABI 宏为
2U; - SONAME 为
.so.2; - daemon active 且 enabled;
- socket 存在;
- 系统目录和安装对象为
root:root; - 不把 ABI 1 client 与 IPC 2 daemon 混装。
ABI 2 相比 ABI 1 在公开 status 中新增 fpga_dropped_frames,IPC STATUS payload 从
80 字节扩展为 88 字节。升级必须同时替换:
vib-acqdlibvib_acq_client.so.2vib_acq_client.h客户端工具重新编译的 GUI/消费端13. systemd 运行策略
跳转到“13. systemd 运行策略”最终 unit 的关键行为:
[Unit]After=systemd-udev-settle.serviceWants=systemd-udev-settle.serviceStartLimitIntervalSec=0
[Service]Restart=on-failureRestartSec=2TimeoutStopSec=40s解释:
- XDMA 节点可能晚于服务启动;daemon 找不到设备时明确失败,systemd 每 2 秒重试;
StartLimitIntervalSec=0避免多次启动失败后永久停止重试;- daemon 排空期限约 12 秒;
- XDMA C2H 驱动超时约 10 秒;
TimeoutStopSec=40s给阻塞读、排空和 FPGA 清理留出上界;- client 的 START/STOP 事务超时为 30 秒,普通控制请求为 2 秒。
默认 socket 为 0660 root:root。普通 GUI 若不是 root,应建立专用服务组并同步修改
unit Group= 与 GUI 用户组,不能直接把 socket 改成全局可写。
14. 目标板验收顺序
跳转到“14. 目标板验收顺序”14.1 先验证底层独占路径
跳转到“14.1 先验证底层独占路径”systemctl stop vib-acqdfuser /dev/xdma0_user /dev/xdma0_c2h_0ad7768_xdma_monitor --packets 4fuser 不应显示其他读取者。监视器依次执行 identity、STOP、CLEAR、选择 AD7768、
打开 C2H、START、读取和协议校验。
监视器结束后恢复产品服务:
systemctl start vib-acqdsystemctl is-active vib-acqd14.2 公开 API 短采
跳转到“14.2 公开 API 短采”vib_acq_example --blocks 8 --timeout-ms 5000 --summary-every 1本次结果:
delivered_frames = 32768bad_packets = 0sequence_gaps = 0bad_tags = 0fpga_dropped_frames = 0fpga_frame_errors = 0fpga_ingress_drops = 0host_overruns = 014.3 记录 64 个 block
跳转到“14.3 记录 64 个 block”ad7768_record_raw \ --output /tmp/ad7768-64.raw \ --blocks 64 \ --timeout-ms 5000
stat -c '%s bytes' /tmp/ad7768-64.rawsha256sum /tmp/ad7768-64.raw期望文件大小:
64 * 65536 = 4194304 byte本次实测:
| 检查 | 结果 |
|---|---|
| 文件大小 | 4194304 B |
int32_t 样本数 | 1048576 |
| 四通道 frame 数 | 262144 |
| 超出 signed 24-bit 范围 | 0 |
| SHA-256 | 2a3555d2c492dd3c49f0a1528f0b3b38659f43e41318c3335aaaf5922456ae30 |
| CH0 | 5178362 .. 5181550 |
| CH1 | 4282 .. 16943 |
| CH2 | -136 .. 71 |
| CH3 | -119 .. 79 |
这些数据只证明格式、范围和数字传输一致性,不证明模拟精度、噪声或传感器标定。
离线检查示例:
from pathlib import Pathimport numpy as np
raw = Path("/tmp/ad7768-64.raw")assert raw.stat().st_size == 64 * 65536
samples = np.fromfile(raw, dtype="<i4")assert samples.size == 64 * 4096 * 4
frames = samples.reshape(-1, 4)bad = np.count_nonzero((frames < -8388608) | (frames > 8388607))
print("frames:", frames.shape[0])print("out_of_range:", bad)for channel in range(4): print(channel, frames[:, channel].min(), frames[:, channel].max())14.4 慢消费者与恢复
跳转到“14.4 慢消费者与恢复”通过延迟消费故意压满 daemon Ring Buffer:
vib_acq_example \ --blocks 80 \ --timeout-ms 5000 \ --summary-every 80 \ --sleep-ms 100本次得到:
host_overruns = 168exit code = 3这是预期行为:SDK 没有静默覆盖数据,而是显式报告 DATA_LOSS。随后立即验证恢复:
vib_acq_example --blocks 1 --timeout-ms 5000 --summary-every 1下一会话返回 0,所有完整性计数恢复为 0。
14.5 100 次生命周期
跳转到“14.5 100 次生命周期”本次实际使用的等价循环:
: > /tmp/vib-cycle-abi2.log
for cycle in $(seq 1 100); do vib_acq_example \ --blocks 1 \ --timeout-ms 5000 \ --summary-every 1 \ >> /tmp/vib-cycle-abi2.log 2>&1 || { echo "FAIL cycle=$cycle" exit 1 }done
grep -c '^status ' /tmp/vib-cycle-abi2.log结果:100 次 configure/start/read/stop/status 全部通过,每轮完整性计数为 0,最终状态
为 idle,FIFO 已排空。
14.6 客户端异常退出恢复
跳转到“14.6 客户端异常退出恢复”验证方法:启动长采客户端后强制终止,使其不能正常调用 STOP;等待 daemon 自动清理,再 启动新客户端。
本次结果:
强制退出状态 = 137daemon = active后续 1-block 采集 = 成功后续完整性计数 = 全 0这证明客户端消失不会永久占用会话。
14.7 活动采集时停止服务
跳转到“14.7 活动采集时停止服务”启动长采后执行:
systemctl stop vib-acqdsystemctl start vib-acqdvib_acq_example --blocks 1 --timeout-ms 5000 --summary-every 1本次活动停止耗时约 54 ms,重启后 1-block 采集成功,服务恢复 active。
14.8 30 分钟连续采集
跳转到“14.8 30 分钟连续采集”vib_acq_example \ --blocks 112500 \ --timeout-ms 5000 \ --summary-every 112500 \ > /tmp/vib-long-abi2.log 2>&1
long_rc=$?echo "LONG_EXIT=$long_rc"sha256sum /tmp/vib-long-abi2.log112500 block * 4096 frame = 460800000 frame,对应约 30 分钟。
最终状态:
received_frames = 460804212delivered_frames = 460800000bad_packets = 0sequence_gaps = 0bad_tags = 0fpga_dropped_frames = 0fpga_frame_errors = 19fpga_ingress_drops = 0host_overruns = 0received_frames 多出的 4212 frame 来自客户端收到目标 block 后,STOP 流程为安全排空
C2H 而继续读取的数据;应用交付量以 delivered_frames 为准。
因为 fpga_frame_errors=19,STOP 正确返回 DATA_LOSS,工具退出码为 3。不能把退出码 3
简单写成“PCIe 长测失败”;它准确揭示了 AD7768 输入侧帧错误。
日志 SHA-256:
9d9ee994cbc51c62cad68052e56d059dc5b8ed001a7f5055f9244b78f4b9e63e长测后立即执行 1-block 采集成功,所有计数为 0,说明会话清理和恢复正常。
14.9 内核日志差异
跳转到“14.9 内核日志差异”在压力/长测前后分别保存:
dmesg | grep -Ei 'xdma.*timeout|aer.*error|completion timeout' \ > /tmp/vib-dmesg-before.txt || true
# 执行测试
dmesg | grep -Ei 'xdma.*timeout|aer.*error|completion timeout' \ > /tmp/vib-dmesg-after.txt || true
diff -u /tmp/vib-dmesg-before.txt /tmp/vib-dmesg-after.txt本轮没有新增 XDMA timeout、PCIe AER 或 completion timeout。日志中 XDMA 初始化行的
timeout: h2c 10 c2h 10 sec 是驱动配置说明,不是超时故障。
15. 遇到的问题与解决办法
跳转到“15. 遇到的问题与解决办法”15.1 错用了 Wrynose 6.18 工具链
跳转到“15.1 错用了 Wrynose 6.18 工具链”现象:构建机存在更新的 Wrynose SDK,看起来更适合新项目。
根因:目标板仍是 Linux 5.4/glibc 2.30,Wrynose sysroot 是 glibc 2.43。使用它会把 新 glibc 符号需求带入产物。
解决:固定 Zeus 5.4 xwayland 工具链,构建后检查 ELF、动态依赖和目标板加载。
15.2 加载 Zeus 后 CMake 版本反而太旧
跳转到“15.2 加载 Zeus 后 CMake 版本反而太旧”现象:source 环境后默认 CMake 变成 3.15.3,低于项目要求的 3.16。
根因:Yocto SDK 把自身 host 工具放到 PATH 前面。
解决:仍使用 Zeus 的编译器、sysroot、toolchain file,但显式调用主机
/usr/bin/cmake 3.28.3。
15.3 停止后仍有 FPGA 尾部数据
跳转到“15.3 停止后仍有 FPGA 尾部数据”现象:旧停止流程可能提前结束 worker 或关闭 C2H,FPGA accepted/transmitted 计数 不相等,下一会话收到残留数据。
根因:停止“数据源”和停止“DMA 读取者”不是同一动作。先杀 worker 会失去排空者。
解决:
- 先 STOP FPGA source;
- worker 继续读 C2H;
- 等待 source idle、FIFO empty、accepted==transmitted;
- 再 join worker 和关闭设备;
- 排空不完整时返回明确 drain error 或
DATA_LOSS。
15.4 前一会话留下 partial C2H transaction
跳转到“15.4 前一会话留下 partial C2H transaction”现象:watchdog、超时或异常断开后,下一会话首个 64 KiB packet 可能错位或短读。
根因:XDMA streaming transaction 跨会话残留,简单 CLEAR 不能恢复主机侧正在进行的 读事务。
解决:START 前执行:
STOP -> 打开 C2H -> 有界 drain stale stream -> CLEAR -> 选择源/配置 -> STARTDMA buffer 固定为完整 64 KiB 页面并保持对齐;短 packet 不能用下一次 read 拼接。
15.5 partial abort 的 CLEAR 丢失诊断
跳转到“15.5 partial abort 的 CLEAR 丢失诊断”现象:异常恢复时直接 CLEAR 会把 FPGA 计数清零,使本次会话的根因消失;处理顺序不当 还可能污染下一会话 baseline。
解决:
- 先保存 pre-clear snapshot;
- 关闭 C2H 后再 CLEAR;
- CLEAR 后重置 raw counter baseline;
- 保留 64-bit session diagnostic totals;
- 验证传输侧和 ADC 侧计数均已清零。
15.6 清理错误覆盖真正的 C2H 根因
跳转到“15.6 清理错误覆盖真正的 C2H 根因”现象:C2H 已发生短读或 I/O 错误,但随后 STOP、snapshot 或 CLEAR 又失败,最终返回的 错误变成“清理失败”。
根因:多个错误简单覆盖同一个 result。
解决:保存 first/root error;只有没有更早根因时,清理错误才成为最终返回值。
15.7 CSR 并发访问发生交叉
跳转到“15.7 CSR 并发访问发生交叉”现象:DMA worker 和 IPC 控制路径同时读取顺序 user BAR,文件位置和多步读写可能交叉。
根因:该目标的 CSR 访问依赖顺序字符设备语义,不能假定所有 pread/lseek 组合都与
普通文件相同。
解决:所有 FPGA control 操作通过统一 mutex 串行化;XDMA 层只提供受控的顺序访问, CSR offset 规则集中在 FPGA 层。
15.8 32-bit FPGA 计数器会回绕
跳转到“15.8 32-bit FPGA 计数器会回绕”现象:长会话中直接把 FPGA 原始 32-bit counter 暴露给应用会回绕;只读有效 packet header 还可能漏掉无效 packet 期间增加的错误。
解决:
- 约每 64 个完成 DMA read 轮询一次 CSR;
- 无效 packet 也计入轮询节奏;
- 用模
2^32差值扩展成 64-bit session total; - CLEAR 后重新建立 raw baseline,但不丢失会话诊断累计。
15.9 ABI 1 无法公开 FPGA dropped frame
跳转到“15.9 ABI 1 无法公开 FPGA dropped frame”现象:ABI 1 的 STATUS 没有 fpga_dropped_frames,GUI 无法稳定获取 FPGA 传输 FIFO
丢帧累计。
解决:SDK 升级到 0.2.0,并同步升级:
C ABI 1 -> 2IPC 1 -> 2STATUS payload 80 B -> 88 BSONAME .so.0 -> .so.2ABI 1 和 ABI 2 不能混装。
15.10 工具漏掉 STOP 阶段发现的数据丢失
跳转到“15.10 工具漏掉 STOP 阶段发现的数据丢失”现象:工具完成目标 block 后先读 status,再 STOP;如果错误只在 STOP 排空阶段被发现, 工具可能错误返回 0。
解决:先 STOP,再读取最终 status;STOP 返回 DATA_LOSS 或任一完整性计数非零时,
工具退出 3。新增 stop-time-data-loss 端到端测试。
15.11 systemd 的 ConditionPathExists 阻止自动恢复
跳转到“15.11 systemd 的 ConditionPathExists 阻止自动恢复”现象:开机时 XDMA 节点尚未出现,unit 被 condition 跳过,节点稍后出现也不会重试。
根因:ConditionPathExists 导致 systemd 认为服务无需启动,不属于失败重启。
解决:移除 condition,让 daemon 明确失败,配合:
Restart=on-failureRestartSec=2StartLimitIntervalSec=015.12 部署包污染 /usr 所有权
跳转到“15.12 部署包污染 /usr 所有权”现象:从 staging 打包时保留构建用户 UID/GID,root 解包后系统目录或服务文件变成普通 用户所有。
风险:普通用户可以替换 root daemon,属于安全问题,不只是“文件看起来不整齐”。
解决:只打包 usr,归一 owner/group、numeric owner 和权限;部署后对 /、/usr
及安装对象执行 stat 检查。本次已把 /、/usr、/usr/bin、/usr/sbin、/usr/lib
和 /usr/include 恢复为 0755 root:root。
15.13 C2H poll 和跨线程 close 不能作为可靠超时
跳转到“15.13 C2H poll 和跨线程 close 不能作为可靠超时”现象:希望用 .poll 或另一个线程 close(fd) 立即打断 streaming C2H 阻塞读。
根因:当前 XDMA streaming C2H .poll 不能表达需要的完成超时;一个线程关闭 fd 也
不能保证立即中断另一个线程正在进行的 read。
解决:
- 驱动配置有限
c2h_timeout=10s; - daemon 使用有界排空;
- client START/STOP 期限 30 秒;
- systemd stop 期限 40 秒;
- 所有权和停止顺序由 daemon 状态机统一管理。
15.14 测试期望与新累计语义冲突
跳转到“15.14 测试期望与新累计语义冲突”现象:端到端测试先注入 FPGA drop,再把 mock 原始 counter 设回 0,随后仍期待 STOP
成功;实现返回 DATA_LOSS。
根因:ABI 2 保存的是会话累计 64-bit 诊断,不应因为原始 counter 回到 0 就忘记本 会话曾发生 drop。
解决:修正测试期望,STOP 返回 DATA_LOSS 才是正确行为。Windows、Linux、ASan、
UBSan 和 TSan 全套门禁重新通过。
15.15 目标板时间错误导致解包“未来时间”警告
跳转到“15.15 目标板时间错误导致解包“未来时间”警告”现象:目标板系统时间仍显示 2021,构建产物时间是 2026,tar 报 timestamp in the future。
判断:这不等同于文件损坏;应继续校验归档返回码、文件大小和 SHA-256。
解决:验收仍以哈希和 ELF 检查为准;产品系统应配置 RTC/NTP,使日志和产物时间可追溯。
15.16 ldconfig 报 Vulkan 库截断
跳转到“15.16 ldconfig 报 Vulkan 库截断”现象:ldconfig 同时报告目标 rootfs 中已有 Vulkan 库截断。
判断:这些库不属于本 SDK,不能把已有 rootfs 告警误判成 ABI 2 安装失败。
处理:确认 libvib_acq_client.so.2 链接和加载成功,单独记录 Vulkan rootfs 问题,
不在 SDK 任务中擅自删除系统库。
15.17 自包含工具测试缺少必需参数
跳转到“15.17 自包含工具测试缺少必需参数”现象:批量执行目标板测试二进制时,把 vib_acq_tool_stop_data_loss_test 当作无参数
测试运行,得到“expected example and recorder paths”。
根因:该测试需要传入 vib_acq_example 和 ad7768_record_raw 路径;这是测试调用
错误,不是产品缺陷。
解决:按 CTest 定义单独执行并传入两个工具路径,测试通过。自动验收应优先使用 CTest 元数据,避免用通配符盲目执行所有二进制。
16. 验收矩阵
跳转到“16. 验收矩阵”| 层级 | 结果 | 能证明什么 | 不能证明什么 |
|---|---|---|---|
| Windows strict | 12/12 | 可移植协议、状态机、工具参数 | Unix Socket 和硬件 |
| Linux strict | 16/16 | daemon/client/Socket 端到端 | FPGA、XDMA、AD7768 |
| ASan/UBSan | 16/16 | 已覆盖路径无已检出内存/未定义行为 | 未执行硬件路径 |
| TSan | 16/16 | 已覆盖路径无已检出数据竞争 | 所有设备驱动并发行为 |
| Zeus AArch64 build | 通过 | 工具链、架构、SONAME、staging | 板上设备可用 |
| 外部 C consumer | 通过 | CMake package 与 pkg-config 可消费 | 实时采集稳定性 |
| OK8MP 自包含测试 | 11 个通过 | AArch64 测试运行和核心逻辑 | 模拟精度 |
| 8-block 短采 | 全部完整性计数 0 | 公开 API 和真实数据短链路 | 长稳 |
| 64-block raw | 4 MiB,范围/格式通过 | 原始格式和四通道数字传输 | VREF/增益/噪声精度 |
| 慢消费者 | 明确 DATA_LOSS,随后恢复 | 有界缓存和错误可见性 | 永不丢数据 |
| 100 次生命周期 | 全通过 | 重复启停和清理 | 断电恢复 |
| 客户端异常退出 | 后续立即恢复 | daemon 断连清理 | PCIe 物理断线 |
| systemd 活动停止 | 约 54 ms,重启恢复 | 服务关闭路径 | 所有 c2h_timeout 组合 |
| 30 分钟传输 | 460800000 frame,传输计数全 0 | packet/XDMA/daemon/C ABI 长稳 | AD7768 输入零错误 |
| AD7768 输入长稳 | fpga_frame_errors=19 | 输入侧错误可见 | 产品级零错误 |
17. 最终产物和哈希
跳转到“17. 最终产物和哈希”发布归档:
vib-acq-sdk-0.2.0-ok8mp-zeus.tar.gzSHA-256 21e8ecc1371be9e7a82bb3c0e26f868559eeccb18a69d3bd8eb9371b2d9df165测试归档:
SHA-256 81686090f34cb55ad46576b2234950e96faf30cec0dc057c13d700e3437f1fa1目标板关键文件:
| 文件 | SHA-256 |
|---|---|
/usr/sbin/vib-acqd | 473ba01a55589c4e1830e98e3ab9f2dd096b8386211b5d72c09bd9be090139a6 |
/usr/lib/libvib_acq_client.so.0.2.0 | 13410e7de177f027ba5a2a33cb958afea28cc8438bcd2c3a780b75b8be515b2d |
/usr/bin/vib_acq_example | a7433e29549512daa7733f51bdfa76f0ff46e531155991dc0a0e0979fad92272 |
/usr/bin/ad7768_record_raw | a04e7520898e1c909857fedc80f9d901c5962d23575918e2c5cc6447a8e242cd |
/usr/bin/ad7768_xdma_monitor | 30a355727d47e21d384bd0545d97597c3415ecfa7188db0129678b12d713098d |
18. 版本提交脉络
跳转到“18. 版本提交脉络”最终阶段的关键提交按依赖顺序为:
14f029a define and test acquisition IPC protocolb358d15 implement acquisition service and DMA workere1708b9 complete acquisition daemon and client API21bcd97 drain FPGA stream before stopping DMA3c60518 add acquisition tools and package validation566f59b recover and drain FPGA sessions reliablye1573f2 serialize FPGA control operationsaf9a9ce publish durable integrity status through ABI 22c63968 harden tools and package consumptionde42b35 retry vib-acqd when XDMA devices appear late260176b publish ABI 2 integration and OK8MP acceptancef2da2c3 update repository verification baseline这组提交不是“只写几个 API”,而是逐步关闭停止、排空、错误保留、计数回绕、ABI、打包、 systemd 和实机恢复等产品问题。
19. 下次从零复现的最短清单
跳转到“19. 下次从零复现的最短清单”构建机
跳转到“构建机”- 确认 SDK 源码提交为
f2da2c3或其已审核后代 - Windows strict 12/12
- Linux strict 16/16
- sanitizer 门禁通过
- 加载 Zeus 5.4,而不是 Wrynose 6.18
- 显式使用 CMake >= 3.16
- AArch64 Release 严格构建
- staging 布局、ELF、SONAME、glibc 需求检查
- CMake 和 pkg-config 外部 C consumer 通过
- 归档只含
usr、owner/group 0:0、权限规范 - 记录发布包 SHA-256
目标板部署
跳转到“目标板部署”-
/dev/xdma0_user和/dev/xdma0_c2h_0存在 - 停止旧 daemon
- 同步安装 daemon、
.so.2、头文件和工具 -
ldconfig、daemon-reload、enable/start - ABI 2、IPC 2、SONAME
.so.2一致 - 安装对象和父目录为
root:root - socket 权限符合 GUI 运行用户设计
功能验收
跳转到“功能验收”- 独占 monitor 读取 4 个 packet
- 公开 API 8-block 短采
- 64-block raw 大小、哈希、四通道和 24-bit 范围检查
- 慢消费者必须显式 DATA_LOSS
- DATA_LOSS 后下一会话恢复
- 100 次启停
- 客户端异常退出恢复
- 活动 systemd stop/start 恢复
- 30 分钟 112500-block 长测
- 测试前后内核 XDMA/AER 日志差异
- 单独记录传输层计数和 AD7768 输入侧计数
20. 尚未完成的产品工作
跳转到“20. 尚未完成的产品工作”- 使用示波器或 ILA 检查
DCLK、DRDY、DOUT0,消除 30 分钟内 19 次帧错误; - 修复后重新完成 AD7768 输入侧零错误长测;
- 设计并执行受控 PCIe 物理断开/重训练;
- 验证 Linux 运行期间 FPGA 重烧或复位的错误可见性和恢复;
- 验证采集中移除
DCLK、DRDY或DOUT0; - 把 SDK 正式集成到产品 rootfs/package recipe,而不是长期使用手工 tar 部署;
- 为普通 Qt 用户建立专用服务组;
- 若需要 GUI、记录器和远程服务并行消费,设计发布/订阅和每客户端有界队列;
- 引入硬件时间戳或共享时间基准,支持键相、多设备和绝对相位;
- 完成模拟精度、VREF、增益、零偏、噪声和传感器标定,这些不属于本次 SDK 验收。
21. 复盘
跳转到“21. 复盘”本阶段最重要的经验不是“XDMA 能传 4 MB/s”。相对于 PCIe 带宽,这个吞吐很小。真正 决定产品可靠性的因素是:
- 数据由谁拥有;
- 停止时谁负责排空;
- 错误是否会被后续清理覆盖;
- 计数器回绕后是否仍可追踪;
- 慢消费者是否得到明确的数据断点;
- daemon、动态库、IPC 和头文件是否一起版本化;
- 构建工具链是否与目标 rootfs 真正兼容;
- 部署包是否保持系统目录安全;
- 测试结论是否把传输层和 ADC 输入侧分开。
只有把这些问题形成代码、测试、部署和验收闭环,Qt GUI 才能在稳定接口上开发,而不是 把硬件竞态和数据完整性问题留到界面联调阶段。