7系列IO模块相关的结构如图1所示,前文对IOB、IDELAYE2、ILOGIC、OLOGIC进行了讲解,还剩下ISERDESE2、OSERDESE2、ODELAYE2原语,本文对ODELAYE2进行讲解,该原语只有HP bank才有,即7系列FPGA的A7系列没有ODELAYE2结构,不能使用ODELAYE2原语。
图2中显示了这5个结构在FPGA中的位置,每个管脚都会存在这5个结构,输入信号的流向是IOB,选择经过IDELAYE,然后通过ILOGIC到达内部逻辑。而输出信号是内部逻辑经过OLOGIC,然后选择经过ODELAYE,最后通过IOB的管脚PAD。
ODELAYE2原语的功能和工作模式与IDELAYE2一致,必须配和IDELAYCTRL原语一起使用。主要的作用就是对输出的信号延时,将多bit信号的各位进行对齐。 没有这个原语的FPGA可以通过时序约束来告知综合器在布局布线时考虑各位信号间的延迟。
图3是ODELAYE2原语的框图,与IDELAYE2的信号端口类似。
原语的端口信号如表1所示,多数信号与IDELAYE2的信号含义一致,由于ODELAYE2在输出端,只能对FPGA内部信号(普通信号或者时钟)进行延时,相比IDELAYE2原语,ODELAYE2缺少IDATAIN信号,多了一个CLKIN,当需要延时的信号是时钟信号,从CLKIN端口输入。
信号名 | I/O | 位宽 | 含义 |
---|---|---|---|
C | I | 1 | 当ODELAYE2工作在FIXED模式下,不需要时钟信号,其余模式必须提供时钟信号。 |
REGRST | I | 1 | 仅VAR_LOAD_PIPE模式下使用的复位。 |
LD、CE、INC | I | 1 | 与IDELAYE2中含义一致。 |
CINVCTRL | I | 1 | 动态翻转时钟 © 极性。 |
CNTVALUEIN | I | 5 | 使用这个值来动态更改延迟抽头系数,位宽为5,最大31。 |
CLKIN | I | 1 | 被延时信号是时钟信号时,从该端口输入,必须是时钟缓冲器(BUFIO、BUFG 或 BUFR)的信号。 |
DATAIN | I | 1 | 来自FPGA逻辑的数据输入。 |
LDPIPEEN | I | 1 | 把输入的延时值加载到流水线中。 |
DATAOUT | O | 1 | 输出延迟后的信号,输出到IOB。 |
CNTVALUEOUT | O | 5 | 输出该模块当前工作的延时系数 |
ODELAYE2的参数与IDELAYE2的参数完全一致,如下表2所示,不再赘述,详解参考IDELAYE2参数讲解。
参数名 | 含义 |
---|---|
ODELAY_TYPE | FIXED固定延时模式,VAR_LOAD 动态加载抽头值模式,VAR_LOAD或VAR_LOAD _PIPE模式下,可以动态加载外部输入的值,并进行动态调整,一般使用VAR_LOAD模式。 |
ODELAY_VALUE | FIXED、VAR_LOAD模式的延时初始值,VAR_LOAD、VAR_LOAD_PIPE模式时被忽略。 |
CINVCTRL_SEL | 是否动态翻转时钟信号C的极性。 |
DELAY_SRC | 需要延时的信号是内部的信号(DATAIN),还是管脚输入的信号(IDATAIN) |
HIGH_PERFORMANCE_MODE | 是否使用高性能模式减小抖动,会增加功耗。 |
PIPE_SEL | VAR_LOAD_PIPE 模式的时候使用 |
REFCLK_FREQUENCY | 设置参考时钟频率(单位:MHz),取值范围190210、290310、390~410,通常取200,与延时的分辨率有关。 |
SIGNAL_PATTERN | 延时的是时钟信号还是数据,不同类型信号路径上的抖动不同。 |
ODELAYE2的原语模板如下所示:
(* IODELAY_GROUP = *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
ODELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("ODATAIN"), // Delay input (ODATAIN, CLKIN)
.HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.ODELAY_TYPE("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.ODELAY_VALUE(0), // Output delay tap setting (0-31)
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
)
ODELAYE2_inst (
.CNTVALUEOUT(CNTVALUEOUT), // 5-bit output: Counter value output
.DATAOUT(DATAOUT), // 1-bit output: Delayed data/clock output
.C(C), // 1-bit input: Clock input
.CE(CE), // 1-bit input: Active high enable increment/decrement input
.CINVCTRL(CINVCTRL), // 1-bit input: Dynamic clock inversion input
.CLKIN(CLKIN), // 1-bit input: Clock delay input
.CNTVALUEIN(CNTVALUEIN), // 5-bit input: Counter value input
.INC(INC), // 1-bit input: Increment / Decrement tap delay input
.LD(LD), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
// VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
.LDPIPEEN(LDPIPEEN), // 1-bit input: Enables the pipeline register to load data
.ODATAIN(ODATAIN), // 1-bit input: Output delay data input
.REGRST(REGRST) // 1-bit input: Active-high reset tap-delay input
);
四种模式与IDELAYE2也是一致的,所以直接对四种模式进行仿真,相应的设计代码如下:
module odelaye2_ctrl(
input clk ,//系统时钟信号,默认200MHZ;
input rst ,//系统复位信号,高电平有效;
input din ,//输入数据;
input ce ,
input inc ,
input ld ,
input ldpipeen ,
input reg_rst ,
input [4 : 0] cntvaluein ,
output [4 : 0] cntalueout ,
output rdy ,
output dout //延时后的输出数据;
);
(* IODELAY_GROUP = "ODELAY_CTRL" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
ODELAYE2 #(
.CINVCTRL_SEL ( "FALSE" ),// Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC ( "ODATAIN" ),// Delay input (ODATAIN, CLKIN)
.HIGH_PERFORMANCE_MODE ( "FALSE" ),// Reduced jitter ("TRUE"), Reduced power ("FALSE")
.ODELAY_TYPE ( "FIXED" ),// FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.ODELAY_VALUE ( 0 ),// Output delay tap setting (0-31)
.PIPE_SEL ( "FALSE" ),// Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY ( 200.0 ),// IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN ( "DATA" ) // DATA, CLOCK input signal
)
ODELAYE2_inst (
.CNTVALUEOUT (cntalueout ),// 5-bit output: Counter value output
.DATAOUT (dout ),// 1-bit output: Delayed data/clock output
.C (clk ),// 1-bit input: Clock input
.CE (ce ),// 1-bit input: Active high enable increment/decrement input
.CINVCTRL (1'b0 ),// 1-bit input: Dynamic clock inversion input
.CLKIN (1'b0 ),// 1-bit input: Clock delay input
.CNTVALUEIN (cntvaluein ),// 5-bit input: Counter value input
.INC (inc ),// 1-bit input: Increment / Decrement tap delay input
.LD (ld ),// 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
.LDPIPEEN (ldpipeen ),// 1-bit input: Enables the pipeline register to load data
.ODATAIN (din ),// 1-bit input: Output delay data input
.REGRST (reg_rst ) // 1-bit input: Active-high reset tap-delay input
);
//例化IDELAYCTRL原语
(* IODELAY_GROUP = "ODELAY_CTRL" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYCTRL IDELAYCTRL_inst (
.RDY ( rdy ),// 1-bit output: Ready output
.REFCLK ( clk ),// 1-bit input: Reference clock input
.RST ( rst ) // 1-bit input: Active high reset input
);
endmodule
TestBench参考代码:
`timescale 1 ns/1 ps
module test();
parameter CYCLE = 5 ;//系统时钟周期,单位ns,默认5ns;
reg clk ;//系统时钟,默认100MHz;
reg rst ;//系统复位,默认高电平有效;
reg din ;
reg ce ;
reg inc ;
reg ld ;
reg ldpipeen;
reg reg_rst ;
reg [4 : 0] cntvaluein;
wire [4 : 0] cntalueout ;
wire rdy ;
wire dout ;
odelaye2_ctrl u_odelaye2_ctrl (
.clk ( clk ),
.rst ( rst ),
.din ( din ),
.ce ( ce ),
.inc ( inc ),
.ld ( ld ),
.ldpipeen ( ldpipeen ),
.reg_rst ( reg_rst ),
.cntvaluein ( cntvaluein ),
.cntalueout ( cntalueout ),
.rdy ( rdy ),
.dout ( dout )
);
//生成周期为CYCLE数值的系统时钟;
initial begin
clk = 0;
forever #(CYCLE/2) clk = ~clk;
end
//生成复位信号;
initial begin
rst = 0;
#2;
rst = 1;//开始时复位10个时钟;
#(10*CYCLE);
rst = 0;
#(500*CYCLE);
$stop;//停止仿真;
end
initial begin
#1;din = 0;//输入数据初始化为0;
ce = 0; inc = 0; ld = 0;
ldpipeen = 0;reg_rst = 0;cntvaluein = ({$random} % 32);
#(15*CYCLE);//延迟10个时钟周期;
din = 1'b1;
forever begin
#(2*CYCLE);
ldpipeen = 1;
#(CYCLE);
ldpipeen = 0;
cntvaluein = ({$random} % 32);
#(CYCLE);
ld = 1'b1;
#(CYCLE);
ld = 1'b0;
#(CYCLE);
ce = 1'b1;inc = 1'b1;
#(2*CYCLE);
ce = 1'b1;inc = 1'b0;
#(3*CYCLE);
ce = 1'b0;inc = 1'b1;
#(CYCLE);
ce = 1'b0;inc = 1'b0;
end
end
initial begin
#1;din = 0;//输入数据初始化为0;
#(15*CYCLE);//延迟10个时钟周期;
din = 1'b1;
forever begin
#((({$random} % 6))*CYCLE/2);
din = ({$random} % 2);
end
end
endmodule
将odelaye2_ctrl.v第23行ODELAY_TYPE参数修改为FIXED模式,仿真即可得到图4结果。Cntalueout为0,测得输入与输出信号的延迟为0.6ns,与IDELAYE2的基础延时一致。
当延时系数为0时,此时输入与输出时延时600ps,并不是0。
输入与输出的具体延时=基础延时(600ps)+延时系数*78ps(参考时钟为200MHz), 比如将odelaye2_ctrl.v第24行的ODELAY_VALUE参数设置为11,此时输入与输出延时应该是600ps+11*78ps=1458ps。修改后的仿真结果如图5所示,与预期一致。
将odelaye2_ctrl.v第23行ODELAY_TYPE参数修改为VARIABLE模式,第24行的ODELAY_VALUE参数设置为11,仿真即可得到图6结果。
在VARIABLE模式下,当ld信号在时钟上升沿时为高电平时,ODELAYE2延时参数变为ODELAY_VALUE参数设置的值,当CE和INC同时为高电平时,延时参数在时钟上升沿加1,当CE为高电平,INC为低电平时,延时参数在时钟上升沿减一,当CE为低电平且ld为低电平时,延时参数不变。Cntalueout信号表示ODELAYE2当前的延时参数。具体的延时时间与前面FIXED模式计算方式一致,就不再进行计算和测量了。
将odelaye2_ctrl.v第23行ODELAY_TYPE参数修改为VAR_LOAD模式,第24行的ODELAY_VALUE参数设置为11,仿真即可得到图7结果。
上图仿真表示在VAR_LOAD模式下,ODELAY_VALUE参数无效,当ld信号为高电平时,将cntvaluenin的数值作为ODELAYE2的延时系数,当ce和inc同时为高电平时,ODELAYE2的延时系数在时钟上升沿加1,当ce为高电平且inc为低电平时,延时参数在时钟上升沿减一,当ce为低电平且ld为低电平时,延时参数不变。cntalueout信号表示ODELAYE2当前的延时参数。
VAR_LOAD模式相比VARIABLE模式相比的优势在于,VAR_LOAD模式的ld加载的延时数据是cntvaluenin信号输入的数据,cntvaluenin值是可以改变的。 而VARIABLE模式加载的是ODELAY2的ODELAY_TYPE参数,这个参数是一个固定的值,程序运行时不能变化,与parameter定义的参数一致。所以VAR_LOAD模式是ODELAYE2最常用的模式。
VAR_LOAD_PIPE模式相比VAR_LOAD模式就多了一个寄存器,用来暂存采集输入的延时参数,但是需要将PIPE_SEL参数设置为TRUE才能使用寄存器,参数为FALSE时,VAR_LOAD_PIPE模式相比VAR_LOAD模式没有区别。
将odelaye2_ctrl.v第23行VAR_LOAD_PIPE参数修改为VAR_LOAD模式,第25行的PIPE_SEL参数设置为FALSE,仿真即可得到图8结果,与图7的VAR_LOAD模式没有区别。
将odelaye2_ctrl.v第23行VAR_LOAD_PIPE参数修改为VAR_LOAD模式,第25行的PIPE_SEL参数设置为TRUE,仿真即可得到图9结果。
当ldpipeen为高电平时,ODELAYE2将cntvaluein的值存入内部寄存器中,当ld为高电平时,ODELAYE2不是读取cntvaluein的值作为延时参数,而是把内部寄存器的值作为延时参数。上图中ldpipeen第一次为高电平时,把cntvaluein的值28保存到内部寄存器,当ld为高电平时,不是把此时cntvaluein的值12作为延时参数,而是把内部寄存器保存的28作为延时参数。这就是VAR_LOAD_PIPE模式的VAR_LOAD模式区别,其余功能均相同。
将上述工程分配引脚,然后对工程进行综合、实现,最后打开Default Layout,找到dout信号,并打开走线(方法在讲解IDDR原语时讲过),得到图10所示的走线图。
从上图知dout信号先经过OLOGIC输入到ODELAYE2模块进行延时,最后通过IOB的PAD(管脚)输出FPGA。
放大图10中左半部分,得到图11,由于dout信号并没有使用OLOGIC中的ODDR和触发器功能,所以信号从组合逻辑部分直接输出到ODELAYE2的DATAIN端口,然后输出。
由于前文已经对IDELAYE2和IDELAYCTRL原语做了详细讲解,ODELAYE2与IDELAYE2的模式和参数基本一致,所以本文比较简单,但是ODELAYE2只有K7的HP bank才有,A7系列是不能使用该原语的。因为我手中同时有ZYNQ 7020(A7)和ZYNQ 7030(K7)的开发板,所以还是对ODELAYE2单独做了一个解释发出来。
需要本文所使用工程的在公众号后台回复”ODELAY”(不包括引号)即可,工程中的代码为最初模式,其余模式按照文中描述修改即可。