TDC,Time-to-Digital Converter,时间数字转换器。如果你正在搜这个词,多半是激光测距、PET、粒子探测、高速仪器校时之类的项目;如果你还是个FPGA学习者,那我建议先把这篇读完再动手,它能帮你省下大半年走弯路的时间。
说白了,TDC要解决的就是皮秒级时间测量:用数字电路把两个电信号沿之间的时间间隔量化成数字码。这个场景的边界条件通常很苛刻,比如测距终端要求单发分辨率做到几十皮秒、通道数量要够多、死时间要够短,这些条件凑在一起,通用MCU完全无能为力,ASIC又贵又得等流片周期。FPGA凭借可重构、高并行、成熟的厂商生态,成了做TDC原型验证和中小批量设备最靠谱的载体。
这篇文章会从TDC的原理讲起,重点落在FPGA上的抽头延迟链实现,包括RTL怎么写、布局怎么约束、校准怎么做、常见坑怎么避。适合有一定FPGA基础、正准备做高速时间测量相关项目的工程师,也适合想理解TDC底层逻辑的硬件研发。
1. 从应用场景看TDC:为什么需要皮秒级时间测量
1.1 先把TDC到底是什么说清楚
TDC输入的是时间,输出的是数字。它和ADC是平级的概念——ADC把连续变化的电压变成数字码,TDC把连续变化的时间差变成数字码。时间差精度一旦做到皮秒量级,任何环节的抖动、偏斜、温度漂移都会直接叠加进结果里。
我常给第一次接触TDC的同事打一个比方:用普通计数器测量时间,就像拿着一把分米尺去量头发丝,量出来永远是“0”,没有任何意义。TDC的工作就是把分米尺打碎成更细的刻度,逼近皮秒量级。但物理上不存在无限密的尺子,所以核心工程问题就是——用有限的逻辑资源和带宽,把时间分辨率尽量往下压,同时还得保证测量结果可重复、可校准。
理解这段之后,你就明白TDC为什么不能靠简单堆高频时钟解决。就算给你一个1GHz的时钟,计数分辨率也只有1ns,离皮秒还差着两个数量级。真要硬上高频PLL,时钟抖动本身就会把精度吃掉,板级设计难度还会成倍上升,性价比极低。
1.2 真实场景拆解:哪类项目离不开TDC
TDC覆盖的领域比很多人想象中广,我挑四个最典型的场景来说:
- 激光雷达和ToF相机。发射光脉冲和回波光脉冲的时间差,乘以光速再除以2就是目标距离。1ps时间误差对应0.15mm距离误差,想做毫米级测距,时间测量误差就得控制在几十ps以内。这是消费电子和车载雷达里TDC需求量最大的方向。
- PET探测器。正电子湮灭会产生一对反向的伽马光子,探测器收到两个光子信号的时间差用于符合判选,时间分辨率直接影响重建图像的信噪比。现在高性能PET系统的时间分辨率已经从纳秒级往百皮秒级压缩,TDC是核心测量电路。
- 高能物理与核子实验。飞行时间谱仪(TOF)、快前沿脉冲分析这类系统,对通道密度和时间精度要求都很高。一套实验设备里动辄几十上百个通道,每个通道都要独立的时间测量,FPGA并行优势在这里体现得最明显。
- 高速仪器的内部校准。比如示波器里做时间交织采集时,需要对多片ADC的采样时钟做精确校相,这也是TDC的活。
这些场景对精度的要求不尽相同,但共同点是:时间差必须可测、可复现、可校准。这就是TDC存在的意义,也是FPGA方案能站住脚的原因。
1.3 不同实现方案的横向对比
做TDC之前,先把手上的方案盘一遍,别一上来就写代码。常见实现路径大概有四类,放到一张表里看得清楚:
| 实现方案 | 典型分辨率 | 资源/复杂度 | 适用场景 |
|---|---|---|---|
| 直接计数法 | 10ns@100MHz | 极低 | 粗时间戳、秒表逻辑 |
| 抽头延迟链(TDL) | 10~30ps | 中 | FPGA主流方案,本文重点 |
| 游标法 | 皮秒级 | 高 | 专用芯片、实验室仪器 |
| 时间放大法 | 皮秒级 | 高 | 单通道极致精度设备 |
你打开Xilinx官方应用笔记XAPP523,本质上就是TDL思路的FPGA化。业内做激光雷达TDC芯片的公司,很多也是TDL架构的变种。所以这篇文章后面的内容,基本就是沿着TDL这条主线往下走。
2. TDC主流实现原理:四种方案的取舍
2.1 直接计数法:入门简单但天花板明显
直接计数法的逻辑很直白:让一个计数器以固定时钟周期累加,START信号到来时锁存一次计数值,STOP到来时再锁存一次,两次计数值之差乘以时钟周期,就是时间间隔。整个过程跟秒表没区别。
但它的性能瓶颈有三个。第一,时间分辨率直接等于时钟周期,100MHz时钟对应10ns分辨率,想要更高只能拼命砌高时钟频率。第二,计数器的位宽决定了最大可测时间,做大范围测量就得加宽寄存器,但高频计数的功耗和时序收敛都会变麻烦。第三,输入信号和采样时钟是异步的,计数器锁存瞬间存在亚稳态窗口,触发沿离时钟沿越近,锁存越不可靠,这就是孔径抖动。
所以直接计数法在工程里基本不会单独承担高精度任务,它更适合做“粗测通道”:大范围计数,保证不漏测、不溢出。精测部分再用延迟链补上。
2.2 抽头延迟链(TDL):FPGA里最实用的一招
TDL的核心思想是把一个时钟周期“切”成很多份。信号进入一串延迟单元,每个延迟单元的输出都接一个D触发器的数据端,所有D触发器由同一个采样时钟同时锁存。当信号在延迟链里传播时,采样时钟到来那一刻,信号走到第几个延迟单元,锁存出来的结果就会在第几个位置出现翻转,这个翻转位置就对应信号边沿与采样时钟边沿之间的时间偏移。
用生活例子来解释:一根水管上每隔一厘米开一个小孔,水从一头灌进去,你站在远处同时看所有小孔,水从哪个孔喷出来,你就知道水已经流了多远。延迟链就是那根水管,D触发器阵列就是那一排小孔,采样时钟就是那声“看”。
FPGA里最常用来做延迟单元的资源是进位链。Xilinx 7系列的CARRY4每级有4个输出,级与级之间典型延迟在十几皮秒到几十皮秒,具体参数取决于速度等级、电压和温度。UltraScale/UltraScale+ 架构则提供CARRY8,级延迟特性和7系列不同,要实测确认。
TDL方案的优点非常直观:纯数字逻辑资源就能实现,不需要模拟器件;并行度高,一个时钟沿就能把所有抽头锁存下来;布局可控,多通道做起来相对容易。代价是bin宽度不均匀,所以必须要校准,校准方法后面单独讲。
2.3 游标法和时间放大法:什么时候才用它们
游标法的名字来自游标卡尺。两个振荡器频率略有不同,一个由START事件驱动,另一个由STOP事件驱动,通过两个计数器的差值把时间差按比例放大。两个振荡器频率差越小,放大倍数越大。理论上分辨率可以无限细,但振荡器起振时间、相位噪声、温度漂移都会破坏精度,在FPGA里实现难度非常高,更多出现在专用TDC芯片里。
时间放大法更偏模拟:把时间差先转换成电容充电量,再用低速TDC去量化。精度上限高,但模拟电路对PCB布局、电源纹波、温度变化都非常敏感,调试周期长。
结论很直接:除非你的应用场景明确要求单通道极致性能、且你手上有足够的模拟调试预算,否则FPGA项目优先TDL。后面所有实操内容都以TDL为例展开。
3. FPGA实现TDC的核心架构设计
3.1 器件选型思路与资源评估
选型阶段我一般按四个维度卡:
- 延迟单元:优先选CARRY4/CARRY8体系成熟的器件。Xilinx 7系列资料多、案例多,新手练手首选Artix-7,性价比高;上量之后可以考虑国产FPGA,但CARRY结构是否适合做TDL必须实测,别只看手册。
- 通道数:每个通道需要一组延迟链和一组触发器阵列,资源量不太大。举例来说,我做过一个Artix-7 35T上的4通道TDC,每通道128个bin,配套激光测距逻辑,整体资源占用不超过20%,所以不需要为TDC本身选大容量芯片。
- 采样时钟:TDC的采样时钟定了测量范围和触发模式。通常用内部PLL/MMCM输出低抖动时钟,Artix-7内部的MMCM足够满足要求。
- 板级信号链:输入信号在进入FPGA前要调理干净,高速比较器、施密特触发器、ESD保护和阻抗匹配都得做。很多TDC精度差的问题不在逻辑,而在模拟前端。
3.2 模块划分与数据流设计
我在工程里习惯把TDC系统拆成5个模块,边界清晰,后续换芯片或者换校准算法都只改局部:
- 输入调理:差分转单端、去毛刺、电平整形。
- 延迟链采样:TDL抽头阵列加D触发器,完成皮秒级粗量化。
- 编码模块:把温度计码转成二进制数值。
- 校准模块:查表修正bin宽度不均匀。
- 统计输出:FIFO缓存结果,或累加直方图后上传上位机。
数据流方向是:外部触发信号进输入调理,经过延迟链传播;采样时钟把所有抽头值同时锁存;编码模块把锁存结果转成一个数值;该数值作为地址查校准表得到对应时间值;最后由状态机打包上传或者交给后续测距/符合模块。整个链路看起来长,但每个模块都能独立测试,这在后面调试时会省很多事。
3.3 布局约束:为什么必须管物理位置
很多FPGA工程师写普通逻辑时不关心物理位置,交给工具摆就行,但TDC完全不是这么回事。延迟链抽头之间的延迟误差,除了进位链本身的级延迟,还有走线延迟、LUT输入延迟、时钟偏斜。如果CARRY4散落在不同的CLB列,抽头的真实延迟会掺入大量布线延迟,bin宽度会乱到没法校准。
两个必须注意的点:
- 用Pblock把延迟链约束在同一个区域,最好是在同一列CLB上串下来,让进位链传播方向与CLB列方向一致,相邻抽头落在相邻CARRY4上,走线延迟基本可控。
- 所有采样触发器尽量放在同一个时钟区域,避免跨BufG/BufH导致时钟偏斜差异。理论上时钟偏斜可以通过校准修正,但能避免就别给自己挖坑。
4. 关键RTL设计与约束实现
4.1 延迟链原语例化与注意事项
TDL的RTL写法跟普通逻辑不一样,不能靠always块推断,必须手动例化原语。Xilinx 7系列的核心是CARRY4,每个CARRY4的S端口接1,DI端口接0,CIN接上一级的COUT,O[3:0]就是4个抽头,每一级O端口接到FDRE的D端,FDRE由同一个采样时钟统一触发。
给你一段风格参考,实际项目里需要按器件和位宽适配:
(* DONT_TOUCH = "TRUE" *) wire [3:0] tap; (* DONT_TOUCH = "TRUE" *) wire [3:0] dout; CARRY4 chain_u ( .CO(), .O(tap), .CI(ci[0]), .CYINIT(1'b1), .DI(4'b0), .S(4'b1) ); genvar i; generate for (i = 0; i < 4; i = i + 1) begin : fdre_inst FDRE samp_reg ( .C(clk_samp), .CE(1'b1), .R(1'b0), .D(tap[i]), .Q(dout[i]) ); end endgenerate三个关键注意点。第一,DONT_TOUCH和KEEP属性一定要加在链和抽头信号上,否则综合器觉得“这些节点输出固定不变”,直接给你优化掉,延迟链就废了。第二,链路两端信号也要打上DONT_TOUCH,防止综合器对首尾做常量传播。第三,综合完成后打开报告,搜索CARRY4实例数量,确认链还在,再往下走。
4.2 温度计码转二进制的工程做法
延迟链采样出来的结果是温度计码,形态是一串连续的1后面跟着一串连续的0,翻转沿位置对应时间差。编码模块的任务就是把翻转位置找出来,输出一个二进制数。
最直接的实现是优先编码器:从高位往低位找,碰到第一个0就输出对应地址。但实际效果不好,因为温度计码在翻转沿附近经常有毛刺,会出现孤立的0或1,直接找第一个翻转位置容易跳变。
工程上更稳的做法:
- 先对128个抽头做一次“找最长连续1段”的处理,用移位寄存器统计连续簇长度,取连续段的中点作为翻转位置。
- 或者采用Wallace树多级编码,输入多bit找第一个0,逻辑层级可控,时序更容易收敛。
- 如果延迟链宽度较大,可以把bin分成高低两组,每组分别编码后用LUT6合并,减少关键路径长度。
编码逻辑写完后,最好在仿真里灌入扫频激励验证所有bin都能正确编码,不要只测几个点。我用ILA实测的时候发现,有一类编码错误是特定码型才会触发,仿真全覆盖可以提前暴露。
4.3 时钟网络与异步信号处理
TDL采样必须用统一的全局时钟,绝对不要用数据选择出来的时钟。全局时钟路径经过专用布线资源,偏斜可控,这是TDC精度的底线。
另一个容易犯错的地方在异步信号处理。输入脉冲和采样时钟天然是异步的,如果你先做两级触发器同步,再同步后的信号进延迟链,那TDC测到的就不再是真实到达时间,而是经过同步逻辑后的信号沿,额外插入了几个纳秒固定延迟和几十皮秒抖动。正确做法是:真实信号直接进延迟链做异步采样,锁存结果再用同步逻辑读出,时间重建放在后处理里。
这样做会带来亚稳态风险,但通过后面温度计码的毛刺处理和后端平均,能把影响压到可接受范围。
4.4 Pblock的约束脚本参考
布局约束在XDC里写,先建Pblock,再把延迟链相关的实例加进去。以一个2x2时钟区域为例:
create_pblock pblock_tdc add_cells_to_pblock pblock_tdc [get_cells {tdc_inst/tdl_chain[*]}] resize_pblock pblock_tdc -add {CLOCKREGION_X0Y0:CLOCKREGION_X0Y1}如果延迟链资源更多,可以扩大约束范围。Pblock只是给工具划了边界,真正要精细控制每一级CARRY4的位置,就用set_property LOC指定坐标。但坐标约束维护成本高,换了器件版本就全废。我的经验是:先用Pblock圈出大区域,让工具自己在里面摆放,实测bin宽度和温度计码质量,如果某个区域存在系统性偏差,再针对那一段追加LOC约束。
5. 测试校准与性能分析
5.1 码密度校准:把不均匀的bin拉直
TDL的bin宽度天生不均匀,原因包括进位链自身工艺偏差、布局差异、时钟偏斜。如果不做校准,直接拿bin序号当时间值,误差会达到几十ps甚至更大。工程里最常用的校准法是码密度法。
原理是这样:给TDC输入大量时间差均匀分布的“随机”信号,统计落入每个bin的计数。如果TDC完全线性,每个bin的计数应该近似相等;但实际情况是,宽bin计数多、窄bin计数少,计数比例正好反映bin宽度比例。于是每个bin的相对宽度可以通过计数比算出来:
bin宽度比例 = 该bin计数 / 总样本数
总测量范围可以用外部已知时间间隔标定,比如用时间间隔发生器产生一个精确的固定时间差,或者用另一个高精度仪器做基准,把计数比例换算成绝对时间。校准样本量要够大,几十万次起步,才能把统计噪声压到1%以内。
校准表建立之后,每个测量值都查表累加bin宽度,得到最终时间值。这样处理完,TDC的线性度会明显改善。
5.2 单发分辨率、DNL和INL怎么测
测试TDC性能分三步走:
- 用精密信号源产生周期性脉冲,经过步进可调的精密延时线改变延时,对每个延时点采集TDC输出,画出转移曲线。
- 根据转移曲线计算每个bin实际宽度,再与码密度法结果交叉验证。
- 统计单次测量的标准差作为单发分辨率,不要只看bin宽度平均值。硬件噪声、时钟抖动、比较器抖动都会让单发噪声比单个bin宽好几倍。
DNL和INL这两个指标也要会读。DNL是相邻bin宽度相对于理想bin宽度的偏差百分比,INL是累计偏差对应的皮秒数。画直方图时,如果累计曲线歪得厉害,大概率是进位链的进位点分布不均或者布局跨了CLB列,需要回去调Pblock。
5.3 死时间优化与双链结构
死时间是指TDC完成一次测量之后,到下一次能重新开始测量的最短时间。单链结构下,编码、复位、校准都要占时间,典型死时间在几十纳秒到几百纳秒之间。对单发测量没问题,但对连续脉冲流测距或者粒子物理事件率高的情况,死时间直接限制系统吞吐量。
工程解决方案是双链乒乓结构:两套延迟链交替工作,A链测量时B链在编码复位,B链测量时A链在编码复位,两组输出用多路选择器切换。代价是双倍延迟链资源,以及两条链之间要做对齐校准。
另一个被忽略的点是复位信号的处理。复位路径越短越好,复位释放时刻要和采样时钟相位对齐,否则复位后第一拍可能出现异常采样值,浪费一个测量周期。
5.4 温度漂移问题与在线校准策略
FPGA内部延迟随温度变化非常明显。实测下来,温度每升高10度,CARRY4链的bin宽度可能漂移1%~3%,累积误差到几百皮秒,完全能毁掉测量精度。所以一次性校准的TDC很难长期稳定,工程上必须做在线校准。
我的做法有三个层次:
- 板上放温度传感器,软件定时读数,用线性系数或查表修正bin宽度。
- 定期注入已知时间间隔的校准脉冲,重新跑码密度校准,更新bin宽度查找表。周期可以按秒到分钟量级调,根据系统对稳定性的要求权衡。
- 如果系统有稳定的参考时钟,还可以用TDC测参考时钟周期,反推出总测量范围,实现闭环标定。
6. 常见问题与排查实录
6.1 温度计码毛刺与亚稳态问题
现象是在ILA里看采样结果,温度计码不是干净的连续1和连续0,中间出现孤立的0或1。常见原因有两个:输入信号边沿恰好和采样时钟沿重合,触发器进入亚稳态;或者延迟链信号边沿在相邻bin之间扫描时,锁存沿不正交。
处理方式:
- 编码逻辑不要简单找第一个翻转位置,改成找最长连续1段的中心,毛刺影响大幅下降。
- 多通道多次采样求平均,随机噪声可以被压掉一部分。
- 用两个相位错开的采样时钟交叠采样,两路结果互相校验,鲁棒性更好。
- 约束文件里把TDC相关路径设为异步路径或伪路径,避免时序分析误报,也避免工具为了满足时序悄悄挪位置。
6.2 综合优化把延迟链“优化”没了怎么办
第一次写TDL的朋友很容易踩这个坑:例化完CARRY4跑综合,发现资源面板里CARRY4数量很少,或者仿真波形完全不对。原因通常是综合器认为这些单元输出固定,不值得保留,直接把逻辑简化掉了。
解决办法是给延迟链所有关键信号加上KEEP和DONT_TOUCH属性,同时在综合策略里关闭“相同寄存器合并”。综合完成后,在报告里搜索CARRY4实例数量,确认链的级数符合预期,再继续布局布线。这一步是最容易被忽略、一旦出错后面全白做的环节。
6.3 测量值随温度漂移的稳定化处理
前面提过温度漂移,这里讲排查顺序。先确认PCB电源稳定,温度变化往往伴随电源负载变化,电源纹波变大后TDC精度会明显劣化。再检查模拟前端,比较器的失调电压随温度变化,有些时间漂移问题根本不在FPGA内部。
排除外部因素后,用双通道方法定位FPGA内部问题:两个TDC通道测同一个时间差,如果两通道差值的漂移不大,说明是公共路径漂移,可在软件里做差分消除;如果两通道漂移方向不一致,就要回去检查布局是否跨了温度梯度不同的区域。最后再考虑在线校准闭环。
这里还有一个长期稳定性的经验:不要追求一次性把校准表做到极致,而是让系统每次上电自动校准一次、运行中定期校准一次,配合温度传感器做粗补偿。很多产品级TDC能稳定工作,靠的都不是出厂校准精度,而是这种闭环维护机制。
最后再分享一点个人经验
我这几年的习惯是,每次调TDC都先写一个最小系统:一路输入、一级采样时钟、128级延迟链、一个ILA。把所有噪声和时序问题在这个最小系统里看清了,再加通道、加协议、加校准。这个方法帮我省下大量排查时间。
另一个心得是,不要一上来就追求几十皮秒的极限分辨率。先把码密度校准跑通,把一个bin的统计噪声做到足够小,再考虑提升分辨率或者压缩死时间。每一步都有可验证的中间结果,最后系统联调时才不会崩溃。做硬件测量这行,稳一点比快一点重要得多。