1.1 工作原理待测的时间间隔s对应一个正脉冲,图 1给出了测量正脉冲下降沿到紧邻的时钟上升沿之间的延时tf的原理,注意图 1(a)中tf的测量起点时刻为正脉冲的下降沿到达延时链输入端的时刻。图 1(b)为利用延时链测量tf的原理图。图 1(b)中的每个三角表示一个延时单元,它可以是任何一个能带来延时的电路。当代表时间间隔的正脉冲在延时连中到来之前,延时链的输出均为0;在正脉冲进入延时链后,在紧邻正脉冲的时钟上升沿锁存延时链的输出,延时链的1-0跳变f处对应正脉冲的下降沿,延时链输出含有mf个1表示正脉冲下降沿经过的mf个延时单元,故:
用一个延时链测量时间间隔正脉冲需要考虑两种情况,如图 2图 3所示。则待测的时间间隔t可得:
图 4为实际的测量原理框图,s为待测量的时间间隔正脉冲,clk为系统时钟,ENC为统计mf和mr的编码器,counter为统计n的计数器,计数器在s为高时开始计数。当s的正脉冲进入延时链时,counter开始计数,在紧邻正脉冲的第一个时钟上升沿D触发器组对延时链的输出进行锁存,然后由ENC计算mf和mr,最后把n、mf和mr存入FIFO。
1.2 超前进位延时链的实现图 5中的红色箭头部分即是FPGA用于实现多位快速加法运算的超前进位链,图中的1~5表示资源的位置编号,例如左上角的组合逻辑编号为X16_Y79_N0,则右下角使用的触发器逻辑编号为X16_Y79_N4,根据这些编号可以锁定设计中使用的逻辑资源。根据TimeQuest的分析结果,进位链的延时最小为45ps,最大为728ps,如图 6所示。
利用一下脚本查看延时链的延时,最终结果如图 6所示。
create_timing_netlist -model slow -speed 6
read_sdc
report_path -from [get_pins {inst|Add0~1|datac}] -npaths 300 -panel_name {Report Path}
图 5 一个LABCELL原理图
图 6 超前进位链延时
图 6中1为延时链输入点,第一个超前进位延时0.728ns,此为延时链中最大的延时,最小的为2~4的45ps延时,其余的还有5的57ps延时,6的153ps延时为cin输入到和sumout输出延时和7的217ps延时为寄存器d输入到寄存器输出的延时。由此可见最坏情况,最终的测量误差由728ps决定。为了提高测量精度,可以考虑延时链从第二个延时单元开始,另外通过多通道测量取平均的方法以及Wave UnionB等方法亦可以进一步提升测量精度。
系统时钟clk取250MHz后,为了保证能正确锁存待测信号s的边沿,要求延时链的总延时必须大于时钟周期4ns。延时链中延时单元个数应该大于4/0.045 = 88个。图 6中的3~4完成了两位二进制加法,但是延时值只有一个45ps,可知在图3中的一个LABCELL内部的两个全加器的cin与cout的延时只有一个45ps。这样每个LAB中10个LABCELL最大可以提供450ps的延时,所以延时链使用10个LAB,共200位加法器。
具体实现的思路:
图 7 获得200位长进位延时链的顶层原理图
各个模块的程序如下:
module l_add (
input clock,
input st1,
output reg [199:0] result);
always @(posedge clock )
begin
if(st1) result <= result + 2;
end
endmodule
module r_add (
input clock,
input st1,
output reg [199:0] result);
always @(posedge clock )
begin
if(st1) result <= result + 6;
end
endmodule
module add200 (
clock,
dataa,
data_a,
result);
input clock;
input [199:0]dataa,data_a;
output reg [199:0] result;
always @(posedge clock)
begin
result <= dataa + data_a;
end
endmodule
module o_add (
input [199:0] result,
output reg s_o);
always @(result)
begin
if(result > {{100{1'b1}},{100{1'b0}}}) s_o <= 1'b1; else s_o <= 1'b0;
end
endmodule
其中的add200模块需要进行分区设定,逻辑区域锁定在一列上共10个LAB。最终的Chip Planner视图如图 8所示。
图 8 200位延时链的Chip Planner视图
图 9 输出分区信息1
图 10 输出分区信息2
经过图 9和图 10的操作,可以得到add200的netlist和routing信息add200-inst.qxp。重新建一个工程,添加add200作为设计的一个分区,使用图7的Import Design Partion命令导入add200-int.qxp即可得到延时链。最终的超前进位延时链的原理图如图 11所示。
图 11 超前进位延时链
图 11与图 1 (b)的区别是,延时链输出中的0-1对应的输入s的下降沿。
1.3 边沿检测电路时钟clk上升沿要锁存延时链输出中的0-1或1-0跳变,这种在时钟边沿处的跳变必定会违反触发器的时序约束关系,导致触发器的输出处于亚稳态状态,例如:“000011111”锁存后会出现“000101111”、“000010011”等情况,通过公式的上升沿检测电路可以抑制三位突变的情况并正确识别上升沿输出”000010000”。
其中r为边沿检测电路输出;d为锁存器输出;m为r与d的总位数;k表示第几位。
源程序:
module ris_g (
input [199:0] d,
output reg [199:0] r);
integer k;
always @(d)
begin
r[0] <= ~d[0] & d[1];
r[1] <= ~d[0] & ~d[1] & d[2];
for(k=2;k<199;k=k+1)
r[k] <= ~d[k-2] & ~d[k-1] & ~d[k] & d[k+1];
r[199] <= 1'b0;
end
endmodule
同理下降沿检测电路如公式所示。
源程序:
module fal_g (
input [199:0] d,
output reg [199:0] f);
integer k;
always @(d)
begin
f[0] <= d[0] & ~d[1];
f[1] <= d[0] & d[1] & ~d[2];
for(k=2;k<199;k=k+1)
f[k] <= d[k-2] & d[k-1] & d[k] & ~d[k+1];
f[199] <= 1'b0;
end
endmodule
1.4 编码器边沿检测电路的输出最终为”…0001000…”的形式,1表示0-1或1-0跳变的位置。求出1的位置编号的方法主要MUX结构、Fat Tree结构、Wallace Tree结构和ROM结构,此处不对这些方法进行介绍和比较,从程序编写的难易程度、路径延时、资源使用上最终选择ROM结构的编码器。
其中rp为编码器输出;i为位置编号;m为编码器输入的位数;OR表示或运算。
源程序:
module encoder(
input [199:0] srin,
output [8:0] tenout
);
integer i,k,j;
reg cen_valid;
reg [8:0] cen_tenout;
reg [255:0] cen_srin;
always @(srin)
begin
cen_srin = {{58{1'b0}},srin[199:2]};
cen_tenout = {9{1'b0}};
for(i=0;i<8;i=i+1)
for(k=0;k<2**(7-i);k=k+1)
for(j=(2**i)*(2*k+1)-1;j<=(2**(i+1))*(k+1)-2;j=j+1)
cen_tenout = cen_srin[j] | cen_tenout;
end
assign tenout = cen_tenout;
endmodule
1.5总结最终的顶层原理图:
图 12 系统顶层原理图
源程序:
module do_in (
din,
dataa,
data_a);
input din;
output [199:0] dataa,data_a;
assign dataa = {{199{1'b0}},din};
assign data_a = {200{1'b1}};
endmodule
module n_counter (
input clock,
input st1,
output reg [199:0] n);
always @(posedge clock )
begin
if(st1) n <= n + 1;
end
endmodule
module for_store (
input clk,
input [199:0] m_in,
input [8:0] f_in,
input [8:0] r_in,
output reg wr,
output reg [255:0] d_store);
always @(posedge clk)
begin
if(f_in && r_in ) begin d_store <= f_in - r_in; wr <= 1'b1; end
else begin
if(r_in) begin d_store <= (m_in << 9) + f_in - r_in; wr <= 1'b1; end
else wr <= 1'b0;
end
end
endmodule
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module d_fifo (
data,
rdclk,
rdreq,
wrclk,
wrreq,
q,
rdempty,
wrfull);
input [255:0] data;
input rdclk;
input rdreq;
input wrclk;
input wrreq;
output [255:0] q;
output rdempty;
output wrfull;
wire [255:0] sub_wire0;
wire sub_wire1;
wire sub_wire2;
wire [255:0] q = sub_wire0[255:0];
wire rdempty = sub_wire1;
wire wrfull = sub_wire2;
dcfifo dcfifo_component (
.data (data),
.rdclk (rdclk),
.rdreq (rdreq),
.wrclk (wrclk),
.wrreq (wrreq),
.q (sub_wire0),
.rdempty (sub_wire1),
.wrfull (sub_wire2),
.aclr (),
.rdfull (),
.rdusedw (),
.wrempty (),
.wrusedw ());
defparam
dcfifo_component.intended_device_family = "Cyclone V",
dcfifo_component.lpm_numwords = 256,
dcfifo_component.lpm_showahead = "OFF",
dcfifo_component.lpm_type = "dcfifo",
dcfifo_component.lpm_width = 256,
dcfifo_component.lpm_widthu = 8,
dcfifo_component.overflow_checking = "ON",
dcfifo_component.rdsync_delaypipe = 4,
dcfifo_component.underflow_checking = "ON",
dcfifo_component.use_eab = "ON",
dcfifo_component.wrsync_delaypipe = 4;
endmodule
系统的250MHz的时钟可以使用一个PLL生成,最终的输出结果,可以由Nios II或者ARM核进行读取按照公式进行计算得到结果,所以在时间数字转换器和CPU之间必须增加一个FIFO来实现异步时钟的读写。另外:
1. FIFO的长度和连续测量的次数有关,此处可以缩小至所期望的,不需要256个存储空间。
2. 最终的准确计算结果应该对tf和tr进行除以2修正,因为在芯片一个LABCELL中两个全加器只有一个延时值。
3. 从测量的角度来看,采用多通道测量最后求平均可以进一步提升测量精度。