时间数字转换器TDC的FPGA方案及核心代码
目录
简介
方案
方案项目:
简介
时间到数字转换器(TDC)是将时间间隔(TI)转换为数字代码的高精度时间传感器。它们被广泛应用于时间分辨应用,如粒子物理等。大多数F使用级联携带链来形成抽头延迟线(TDL),因为携带链是现代fpga中的标准链,并有专用的路由资源。
方案
本方案采用FPGA的延时链进行设计,重点采用FPGA的carry4(ultrascale 系列是carry8)
方案项目:
时间分辨率和时间线性度是任何基于FPGA的TDC设计中的关键问题。
分辨率(最小可检测的时间值,通常称为最不显著位,LSB)是TDC的主要度量。对于TDL-TDC,它由携带链的传播延迟决定,可以定义为:
Q=T/n;
其中,T是采样时钟的周期,n是bin的数量。与XILINX 7系列相比,UltraScale系列的CY8的N数量增加了一倍,。然而,当分辨率提高时,线性度就会降低,气泡问题尤为突出。
延时链的不均匀和时钟的抖动(时钟边界)是造成非线性的两个主要因素。
原先方案采用40nm
FPGA 中的延迟链,并采用C0和C3作为触发器编码,时间精度可以达到23ps。
优化后方案如下,采用20nmFPGA,时间精度可以达到5ps。
这里采用双采样模式,理论上精度为原先的4倍数。虽然输出是由DFFs采样的,但它们不能直接编码,因为有意外的逻辑转换(例如,“1”之间的意外的“0”或“0”之间的“1”),通常称为气泡。气泡是由于不均匀传输和时钟倾斜产生的。为了解决气泡问题,采用子sub的方式,子sub采用分解延长方式来减小,最小化时钟倾斜的影响,来减小气泡的输出。
核心代码
粗延时代码
module CoarseCounter(
input CLK_IN,
input RESET_IN,
output [23:0] Q_OUT
);
//wire declaration*****
wire [5:0] q1_i; wire [5:0] q2_i; wire [5:0] q3_i; wire [5:0] q4_i; wire ce1_i; wire ce2_i; wire ce3_i; wire ce4_i;
//*begin of code
counterX counterX_i1 ( .CLK (CLK_IN), // input clk .CE (ce1_i), // input ce .SCLR (RESET_IN), // input sclr .Q (q1_i) ); counterX counterX_i2 ( .CLK (CLK_IN), // input clk .CE (ce2_i), // input ce .SCLR (RESET_IN), // input sclr .Q (q2_i) ); counterX counterX_i3 ( .CLK (CLK_IN), // input clk .CE (ce3_i), // input ce .SCLR (RESET_IN), // input sclr .Q (q3_i) ); counterX counterX_i4 ( .CLK (CLK_IN), // input clk .CE (ce4_i), // input ce .SCLR (RESET_IN), // input sclr .Q (q4_i) ); assign ce1_i = 1'b1; assign ce2_i = (q1_i == 6'b111111) ? 1'b1 : 1'b0; assign ce3_i = (q1_i == 6'b111111 && q2_i == 6'b111111) ? 1'b1 : 1'b0; assign ce4_i = (q1_i == 6'b111111 && q2_i == 6'b111111 && q3_i == 6'b111111) ? 1'b1 : 1'b0; assign Q_OUT = {q4_i,q3_i,q2_i,q1_i};
endmodule
细延时代码
module carrychain_whole #(
parameter StepLength = 192)
(
input RESET_IN,
input CLK_IN,
input STRETCHHIT_IN,
output CO_OUT, output CHNLFIFOWR_OUT, output [7:0] CHNLFIFODATA_OUT, output LATCH_OUT, output HITCLR_OUT
);
//wire declaration******
localparam FineDataWidth = 8;
(* MARK_DEBUG="true" ) wire co_i;( MARK_DEBUG="true" *) wire en_latch_i;
(* MARK_DEBUG="true" *) reg [FineDataWidth-1:0] FineData_r; (* MARK_DEBUG="true" *) reg latch_r;
(* MARK_DEBUG="true" ) reg dataready_r;( MARK_DEBUG="true" ) reg dataready_r1;( MARK_DEBUG="true" *) reg dataready_r2;
(* MARK_DEBUG="true" ) wire [StepLength-1:0] stepdata_i;( MARK_DEBUG="true" *) wire [FineDataWidth-1:0] FineData_i;
//begin of code******
//--------fine data encode---------------- carrychain_plain #( .StepLength (StepLength)) carrychain_plain_i ( .CYINIT_IN (STRETCHHIT_IN), .CLK_IN (CLK_IN), .CO_OUT (co_i), .STEPDATA_OUT (stepdata_i) ); generate if (StepLength == 128) begin encoder_128 #( .StepLength (StepLength) ) encoder_i ( .reset (RESET_IN), .CLK (CLK_IN), .stepdata (stepdata_i), .Finedata (FineData_i) ); end else if (StepLength == 144) begin encoder_144 #( .StepLength (StepLength) ) encoder_i ( .reset (RESET_IN), .CLK (CLK_IN), .stepdata (stepdata_i), .Finedata (FineData_i) ); end else if (StepLength == 160) begin encoder_160 #( .StepLength (StepLength) ) encoder_i ( .reset (RESET_IN), .CLK (CLK_IN), .stepdata (stepdata_i), .Finedata (FineData_i) ); end else begin encoder_192 #( .StepLength (StepLength) ) encoder_i ( .reset (RESET_IN), .CLK (CLK_IN), .stepdata (stepdata_i), .Finedata (FineData_i) ); end endgenerate //---------control signals generate----------------- always @(posedge CLK_IN) begin latch_r <= stepdata_i[0]; dataready_r <= (~latch_r)&(stepdata_i[0]); dataready_r1 <= dataready_r; dataready_r2 <= dataready_r1; end //-----------output data generate------------- always @(posedge CLK_IN) if(RESET_IN) FineData_r <= 8'b0; else if(dataready_r1) FineData_r <= FineData_i; else FineData_r <= FineData_r; //-----------output assignment------------ assign CHNLFIFODATA_OUT = FineData_r; assign LATCH_OUT = latch_r; assign CHNLFIFOWR_OUT = dataready_r2; assign HITCLR_OUT = latch_r & stepdata_i[0]; assign CO_OUT = co_i;
endmodule