ADC 没入库却触发了?FPGA 降采样时序复核

2026-10-052

把示波器时基拉慢,ADC 仍在连续吐出数据,但波形存储器只挑选其中一部分样本。如果触发器盯的是“每个时钟的原始数据”,存储器认的却是“降采样后的有效数据”,就可能出现一种很别扭的情况:一个没有进入波形记录的短尖峰,先把采集状态机叫醒了。

这次从一套双通道 FPGA 示波器公开工程入手,只审查 采样有效、边沿判断和 RAM 写入是否对齐。以下波形来自本地 RTL 仿真,不是整机测量,也不代表模拟前端已被修好。

从探头到触发器,中间隔着两次选择

底板右侧是输入接口,中间是调理和 ADC 区,数字总线再走向 FPGA 开发板接口。看 PCB 顶层图可以先定位这条路径;底层图用于核对它并非一根从接口直接通往 FPGA 的“理想导线”。这些图在对应项目的嘉立创 EDA 编辑器中重新截取,并非原介绍里的缩略图。

底板 PCB 顶层:右侧输入接口、中部调理和 ADC、左侧开发板接口

底板 PCB 底层:同一块板的背面走线视图

原理图把硬件边界说得更清楚:输入先经耦合与衰减、放大,再由差分驱动送入双通道 AD9288;ADC_D0A 至 ADC_D7A 和 B 通道对应总线才进入数字逻辑。这里的图只用于确认 RTL 样本从何而来,不能从 PCB 截图推出带宽、噪声或输入保护的实测结果。

EDA 原理图局部:一路输入的耦合、继电器和衰减网络

EDA 原理图局部:差分驱动、AD9288 和两组 8 位数据总线

到了 FPGA,公开的 top.v 将两路 ADC 数据送进 data_handler。这个模块一边按时基产生 deci_valid,一边寻找触发边沿,并把被选中的样本写进 RAM。问题恰好出在“被选中”的定义没有贯穿三件事。

一次不会入库的尖峰,为何能改变触发位置

检查 V1.1 的 data_handler.v:前一比较值仅在 deci_valid 有效时更新,当前比较值却直接取实时 ADC 总线;边沿标志每个时钟都可能锁存。等待触发的状态又没有用 deci_valid 限制触发分支,于是比较的是原始时钟样本,写入期望的是降采样样本。自动触发超时也有类似的时序边界:超时请求可能先于下一个可写样本到达。

我把原文件放入最小测试台,使用 10 ns 仿真时钟、四分频有效样本、阈值码 100。上一有效样本为 80;在无效周期把 ADC 输入短暂改为 130,再降回 80。图里的时间是测试台相对时间,不是硬件带宽指标。

原逻辑仿真:190 ns 的无效样本尖峰产生边沿事件,随后状态进入采集

sample valid 一直为低,edge event 却跟着尖峰升高;稍后 capture state 和 RAM write 也升高。测试台检查到状态从等待触发的 2 进入后续采集的 3,触发地址变为 11。这是对这段 RTL 的复现,不是“示波器实物一定如此表现”的断言。

修正的核心:同一个样本决定三件事

修订副本把有效采样定义为降采样脉冲、时基稳定和触发通道稳定同时成立。前一值只保存被接受的样本;当前值与前一值在同一个有效时钟沿比较。自动触发超时先挂起,等到可接受样本才消费。等待触发、环形预存和后续写入都使用同一门控,write_enable 在非写入周期回到低电平。

对外附件给的是独立编写的最小模块,关键判断如下;config_changed 要在时基或触发通道变化时置位,让第一份新配置样本只建立比较基线:

wire accepted = sample_valid && !config_changed;
wire crossed = falling
    ? (previous_sample >= level && sample < level)
    : (previous_sample <= level && sample > level);

assign edge_event = armed && accepted && have_previous && crossed;
assign capture_event = armed && accepted &&
    (edge_event || (auto_mode && (auto_request || auto_pending)));

接入采集状态机时,还要让地址、数据和写使能一起随有效样本推进。下面展示的是接入原则,不是未经验证的整机替换文件:

write_enable <= 1'b0;
if (state == WAIT_TRIGGER && sample_accept) begin
    write_addr <= (write_addr + 1'b1) & 14'h3fff;
    write_data_a <= ad_data_a;
    write_data_b <= ad_data_b;
    write_enable <= 1'b1;
    if (capture_event) begin
        trig_pos <= (write_addr + 1'b1) & 14'h3fff;
        state <= POST_CAPTURE;
    end
end

这里还有一个容易被忽略的拍差:非阻塞赋值更新 write_data 和 write_addr 后,同一时钟沿的 RAM 实例看到的仍是旧寄存器值。修订副本用下一拍的写使能完成本次有效样本写入;换成别的 RAM IP 或改写流水线时,必须重新核对 trig_pos 指向的到底是哪一拍,而不是只看状态机跳转。

修订副本仿真:无效尖峰被忽略,下一次有效样本跨阈值才启动采集

面板原工程有 auto、normal、single 三种触发模式按键。它们改变触发策略,却不应该改变“RAM 只写有效样本”这个约束。面板顶、底层图用于定位控制接口和走线,不把它们误作改版实体照片。

面板 PCB 顶层:auto、normal、single 等模式按键位置

面板 PCB 底层:控制排线和按键相关走线

验证做到了哪里

本地对原版和修订副本分别运行了 Icarus Verilog 仿真。原版重现“无效采样尖峰触发”;修订副本通过“无效尖峰忽略、有效边沿触发、写使能回落、自动触发请求等待有效样本”检查。独立附件又验证了配置变化后的重新建基线,以及自动请求被消费后不会重复触发。测试中 RAM 和频率计使用最小替身;没有运行高云综合、时序收敛、ERC/DRC,也没有焊接或测量实物。

因此,这篇文章解决的是采样域内的触发一致性,不是 V1 整机复刻指南。公开项目本身也提示该代设计仍有电源纹波、运放稳定性和部分功能未完成等问题。实际接板前还需逐项复核模拟前端、供电、接口保护和时序约束;不能把一次主机仿真当作电路安全性证明。

附件中的 sample_aligned_trigger.v、测试台和运行脚本组成完整的最小仿真工程。Node.js 18.15+ 环境下运行 npm install、npm test 即可重放四组断言;它不包含原工程、Gowin IP 或板级引脚表,也不声称能直接替换整机固件。

项目与资料来源:LogicPi 双通道 FPGA 数字示波器,原作者 greentor,项目页 https://oshwhub.com/greentor/logicpi-dual-channel-digital-osc 。项目页及 EDA 标示 LGPL-3.0;本次审阅公开的 LogicPi_V1.1_2026.3.9.zip,SHA-256 为 ABAD979324847B1A3E69F0EFBD532759E9A88FFE0CC33B1E2251C7385DD97BEF。在线 EDA 为同一项目页当前关联工程,硬件板丝印仍写 Version 1.0,图仅用于这一公开工程的硬件定位,不推断它与 V1.1 固件已完成所有板级验证。原下载包无独立许可证文件,本文不重新分发原包或厂商加密 IP。

图片与修改:电路和 PCB 图从上述工程的嘉立创 EDA 编辑器重新截取,按版面裁去界面留白,局部图用 Lanczos 保真放大并轻度锐化;没有补绘器件、连线或测量值。两张时序图由本次仿真的 VCD 数据绘制。对原 RTL 的本地修订和验证范围如上;独立最小代码附件按 MIT 许可提供,下载:LogicPi-sample-aligned-trigger-minimal.zip。原工程源码仍从项目页的同名 V1.1 附件获取。相关工具:Icarus Verilog WebAssembly 0.1.4。

技术交流,欢迎关注微信公众号:美男子玩编程。

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -