FPGA实现TDC算法

基础知识

1.时间数字转换器

如果需要测量一个物体的长度,我们需要一个足够长的尺子,尺子的精度为我们的测量精度。将时间测量与长度测量方法类比,我们需要一个足够长的测量工具,该工具的刻度为我们的时间测量精度。例如,在FPGA中,如果想要测量两个上升沿之间的时间,如图1所示。最简单的方法就是使用一个稳定的时钟作为“刻度尺”,通过该“刻度尺“测量上升沿时间。
图1

2.时间测量精度的影响

如图2所示,时钟的周期决定了时间测量精度。并且由于时钟与待测信号之间的沿并非固定,因此会引入误差,时钟频率越低,引入的误差越大。
图2
对于有些开发任务而言,时间的测量要求不高,那么使用一个稳定的时钟去计数测量则为最快捷简单的方法。但是对于一些时间精度要求高误差要求低的开发任务,则没有那么适用。在FPGA中,用于内部计数使用的用户时钟最高不超过800M(一般性能高的FPGA芯片只用到500M,再高时序不一定能过得去)。因此,使用简单的时钟计数法就不再是一个可选的方案。

几种实现方案

在上面的描述当中,我们不难发现,提高测量精度的唯一方法就是提高“刻度尺”的刻度精度(让刻度更密一些)。一开始,我们时钟FPGA内部时钟作为“刻度尺”,接下来我们可以考虑换一个刻度尺。对此,有以下几种方法。

1. Serdes接口

SerDes:串行/解串器(串行收发器)。其功能是将并行数据转换为串行数据(oserdese),或者将串行数据转换为并行数据(iserdese)。对于该部分的理解可以查看赛灵思官方文档

  1. LVDS接口:
    待测信号通过LVDS接口接入,在FPGA中使用iserdes的DDR模式,在时钟的上升沿和下降沿采集数据,以此获得待测信号的信息,如图3所示。
    图3
    通过这种方式,我们可以将测量时钟的精度提高一倍(原来只使用时钟的上升沿作为刻度,现在使用时钟的上升沿和下降沿作为刻度)。但是,即使精度提高了一倍,对于某些应用场景下精度仍然无法满足要求。因此可以采用GT接口。
  2. GT接口:
    GT(包括GTX、GTH和GTP):是Xilinx在高速SerDes的基础上,增加了其他模块,如8b/10b编解码等(具体可以看Xilinx相关文档,如ug476)形成的一个高速串行收发器,GT是Gigabit Transceiver的意思,它是实现当下一些高速串行接口的基础:如PCIe、RapidIO等。其结构如图4所示。
    图4
    该方法实现时间测量与LVDS类似。但是不同的地方在于,其不仅仅使用的时钟的上升沿和下降沿作为刻度,相当于将时钟进行了分割,每个分割点都可以作为刻度。根据GT口支持的速度不同,对时钟的分割精度也不同。
    这种方法虽然可以大大提高测量精度,但是对于FPGA来说,GT口的资源较少,不适用于多路测量的情况。并且该方法对接口硬件要求较高,成本也相对会提高。

2.内部进位链

  1. 什么是进位链?
    顾名思义,进位链,进位形成的数据链。其电路结构如图5所示。
    图5
    其中A和B为被加数,S为结果,CIN为上级进位信息,COUT为当前进位结果。以两比特数据相加为例,二进制加法运算如图6所示。
    图6
    总结:多个加法器组成的加法链路,其中包含了进位信息,该电路可称之为进位链电路
  2. 进位链在TDC中的作用
    首先需要明确一点,每次加法运算都需要一定的时间。该时间为进位延时。假设每次的进位延时为t,那么将被加数固定为0和1,将待测信号作为进位信号输入到进位链中,即可还原待测信号的电平。还原过程如图7所示。
    图7
  3. FPGA中的进位链
    在FPGA当中,以Xilixn的芯片为例。其内部包含CARRY4或者CARRY8进位链电路结构,其由4个或者8个加法器组成。其结构如图8所示。
    图8
    简化后的框图接口以及接口说明如图9,图10所示。
    图9
    图10

进位链级联和校准

根据上面的章节描述,将被加数固定为0和1,将待测信号作为进位信号输入到进位链中,即可还原待测信号的电平。因此对于CARYY每个接口的输入都已经固定。CI输入待测信号,CI_TOP在使用CARRY8时输入0(CARRY4输入1),DI输入’h00,S输入’hFF。那么CO输出进位信号,当待测信号为高电平时,CO的值为1,每个1所代表的时间为进位延时t。其时序图如图11所示。
图11

进位链级联

根据图11不难看出,进位链的总时间需要大于等于工作时钟周期,否则在时钟上升沿处便无法获取当前工作时钟周期内的所有进位信息。一个CARRY8的进位延时大约为30ps,而FPGA内部无法提供如此高速的工作时钟,因此需要对进位链进行级联。但是由于级联过程中会引入连接线,线上会有一部分不能忽略的延迟。其级联示意图如图12所示。
图12
因此需要对级联后的进位链进行校准,以此获得准确的进位延时。

进位链的校准

校准一般有两种方法,平均校准和逐位校准。

平均校准

使用固定的工作时钟和校准时钟,校准时钟频率大于工作时钟。在一段时间内查找校准时钟在每次被采集时1的个数。由于时间确定,因此可以计算出平均每个1的时间,该时间即为进位延迟的平均时间。时序图如图13所示。
图13

逐位校准

逐位校准一般使用码密度法。其原理是使用大量的上升沿去填满工作时钟周期的每个地方。统计检测到每个上升沿时1的总数出现的概率,以此计算每个1所代表的时间。例如工作时钟周期为10ns,校准时钟周期为10.001ns。那么实际上校准时钟每经过一个工作时钟周期,其上升沿都会向工作时钟的上升沿移动1ps。当移动10000次后,校准时钟上升沿与工作时钟上升沿重新对齐,此时完成一次遍历。重复遍历多次后,即可以获取每个边沿到达时间的总集。根据该总集获取每个边沿到达时间出现的概率,即可获取每个延迟单元的实际时间。

系统结构框图

图14
TDC_CORE系统框图如图所示。在校准状态下,CARRY_LUT使用cali_clk产生进位链数据,根据该进位链数据,计算每个进位单元的延迟时间。在采样状态下,CARRY_LUT使用pulse_rxp/n产生进位链数据,根据该进位链数据,获取采样信号的延迟单元个数,计算采样信号的边沿时间。

  • CARRY_LUT:
    进位链数据生成模块,其主要作用为输入信号的切换。
  • CARRY_TDC:
    进位链数据处理模块,其主要作用为校准进位链延迟单元,缓存并读取进位数据结果。

TDC_CORE系统的输入输出如下表所示。

用户参数

Name Description
CLK_CYCLE 系统时钟周期,单位ps
CALI_TIME 进位链校准次数 CLK_CYCLE*(2^CALI_TIME)
CHAIN_LEN 进位链长度
CARRY_TAP 进位链每个进位单元TAP个数:1,2,4,8
RAM_LATENCY 相位查找表(RAM)读取延迟

信号接口

Name Bit Width IO Type Description
clk 1 input 系统时钟
rstn 1 input 系统复位
cali_clk 1 input 校准时钟
pulse_rxp/n 1 input 外部输入的带采样的差分信号
cali_en 1 input 校准开始信号
samp_edge 1 input 采样边沿控制信号
samp_en 1 input 采样开始信号
samp_done 1 input 采样结束信号
samp_vld 1 output 采样数据有效信号
samp_dat 32-CALI_TIME output 采样数据

模块设计

CARRY_LUT模块

图15
CARRY_LUT模块内部框图如图所示。外部输入的脉冲信号经过差分转单端后,进入SIG_MUX进行判端。当处于校准状态时,使用cali_clk作为信号源sig_mux;当处于采样状态时,使用pulse_sig作为信号源sig_mux。sig_mux经过CARRY_GEN模块转换成进位链数据并输出。

  • IBUFDS:
    差分转单端模块,Xilinx官方原语。
  • SIG_MUX:
    信号选择功能,根据cali_en和cali_done判断校准状态。
  • CARRY_GEN:
    进位链信号生成模块。根据用户配置的CHAIN_LEN和CARRY_TAP,输出对应位宽的进位链数据。

CARRY_LUT模块的输入输出信号接口如下表所示。

Name Bit Width IO Type Description
clk 1 input 系统时钟
rstn 1 input 系统复位
cali_clk 1 input 校准时钟
pulse_rxp/n 1 input 外部输入的带采样的差分信号
cali_en 1 input 校准开始信号
cali_done 1 input 校准结束信号
carry_tap CHAIN_LEN*CARRY_TAP output 采样数据

CARRY_TDC模块

图16
CARRY_TDC模块内部框图如图所示。输入的进位链数据carry_tap经过EDGE_GET获取其上升沿和下降沿的bin相位,该相位信息经过EDGE_CHOOSE选择输出用户需要的相位信息edge_phase。校准状态下以该信息作为RAM的地址,每出现一次该地址,就对该地址内的数据加1,直到校准结束进入计算状态。计算状态下遍历RAM中的所有数据,并将前面所有地址的数据累加至当前地址中。计算状态完成后进入采样状态,此时以采样到的相位为RAM的地址,RAM输出的高32-CALI_TIME位即为该采样信号的时间信息。

在“将前面所有地址的数据累加至当前地址中”步骤之前,每个延迟单元出现的次数为ram地址中对应的数据dout。共校准CLK_CYCLE*(2^CALI_TIME)次,因此每个延迟单元出现的概率为dout/(CLK_CYCLE*(2^CALI_TIME)),系统时钟周期为CLK_CYCLE,因此该延迟单元对应的时间为CLK_CYCLE*dout/(CLK_CYCLE*(2^CALI_TIME)) = dout/(2^CALI_TIME)。即当处于采样状态下时,RAM输出的高32-CALI_TIME位即为该采样信号的时间信息。

  • EDGE_GET:
    边沿获取模块。用于获取进位链数据carry_tap数据离第0位最近的1或0的位置。
  • EDGE_CHOOSE:
    边沿选择功能,用于选择用户需要的边沿信息。
  • STATE_MACHINE
    状态机控制功能,用于系统状态跳转控制。包含空状态(IDLE_ST)、RAM清零状态(ZERO_ST)、校准状态(CALI_ST)、计算状态(CALC_ST)、采样状态(SAMP_ST)。
  • RAM_CTRL
    RAM控制功能,在不同的状态下对RAM的控制也不相同。
  • RAM
    数据缓存和存储模块,使用Xilixn官方xpm_memory_spram原语。既对校准时的临时数据进行缓存,又对校准完成后的数据进行存储。
Name Bit Width IO Type Description
clk 1 input 系统时钟
rstn 1 input 系统复位
carry_tap CHAIN_LEN*CARRY_TAP input 采样数据
pulse_rxp/n 1 input 外部输入的带采样的差分信号
cali_en 1 input 校准开始信号
cali_done 1 output 校准结束信号
samp_edge 1 input 采样边沿控制信号
samp_en 1 input 采样开始信号
samp_done 1 input 采样结束信号
samp_vld 1 output 采样数据有效信号
samp_dat 32-CALI_TIME output 采样数据

结语

项目终于快要结束了,也抽空更新了一下文章。之前的TDC文章结构不太好我就给删了,好不容易有个读者给我回复,删文章也一并删了,痛心。。。。目前该代码也已测试无太大问题,后续会持续更新TDC相关的知识。