IDDR的主要功能就是将输入的双沿信号转换为单沿信号输出给FPGA内部逻辑进行使用,IDDR位于通1中的ILOGICE部分,在讲解IDDR使用前,需要了解ILOGICE的结构及功能。
ILOGICE位于IOB旁边,ILOGICE块包含同步元件,用于在数据通过IOB进入FPGA时捕获数据。7系列芯片中ILOGICE可能是ILOGICE2(HP I/O bank)或ILOGICE3(HR I/O bank)。
对比图2、图3知,ILOGICE2和ILOGICE3唯一区别是ILOGICE3有零保持延迟元件(ZHOLD自动与内部时钟分配延迟相匹配,并且在使用时确保焊盘到焊盘的保持时间为零),ILOGICE3模块在输入上支持可选的静态未补偿零保持 (ZHOLD) 延迟线,以补偿时钟插入延迟。
ZHOLD默认启用,除非时钟源是MMCM或PLL,或者在Xilinx设计约束 (XDC)中设置了IOBDELAY属性。
其他功能相同,所以本文将ILOGICE2和ILOGICE3统一称为ILOGICE。
注意ILOGICE不是基本元件,用户不能通过代码去对ILOGICE进行例化。但ILOGICE包含用户实例化的元件,例如布局和布线后的输入触发器 (IFD) 或输入DDR元件(IDDR)。
如图4所示,输入信号D,可以通过红色信号线进行传输,然后直接输出该模块, 就是当FPGA没有调用IDDR、IFD这些原语时,FPGA外部信号通过管脚进入后,需要通过ILOGICE单元,但是又不使用IDDR这些功能器件,那就会通过红色路径直接穿过ILOGICE单元,到达FPGA内部逻辑。
当需要使用IDDR这些转换功能时,通过绿色的信号传输到下面器件的输入端D,这个器件可以配置为锁存器(latch)、触发器(FF)、双沿转单沿器件(DDR),而CE1是时钟使能信号,高电平有效,不连接时默认为高电平。
当不使用IDDR功能时,在使用IDDR原语后,只需要添加(IOB == “TRUE”)原语,就可以使用ILOGICE中的触发器(FF)的功能。 这个触发器相比FPGA内部触发器更靠近FPGA管脚,使得建立时间余量更大,更有利于时序。
Q1和Q2是输出端口,S/R是复位、置位端口,在调用原语是可以对同步、异步触发进行设置。此框图中S/R的是一根线,但IDDR的原语将S和R分开为两个端口,使用时不能让S、R同时有效。
IDDR的功能就是将双沿采样的数据转换为单沿数据传输给FPGA内部进行使用。 FPGA内部的D触发器一般都是在时钟上升沿去采集输出数据,这种方式被称为SDR,与SDRAM传输数据类似。在SDRAM之后,为了提高数据传输速率,推出了DDR,在时钟的上升沿和下降沿都能传输数据,同样时钟频率下,速率可以提升一倍,这种传输数据的方式就是双沿传输,这种方式一般都只存在接口部分,内部电路采用双沿会比较麻烦,所以会转换为单沿进行处理,FPGA调用IDDR原语即可实现。
如图5是IDDR的原语框图,将图3中的控制信号引出。
IDDR原语的输入输出信号的含义如表1所示。
端口名 | 含义 |
---|---|
Q1、Q2 | IDDR输出寄存器 |
C | 时钟输入引脚 |
CE | 必须为高电平才能将新数据加载到DDR触发器中,低电平时,时钟转换被忽略,新数据不会加载到DDR触发器中。 |
D | IDDR寄存器从IOB输入 |
S/R | 同步/异步设置/复位引脚,高电平有效。 |
IDDR 原语支持三种操作模式:OPPOSITE_EDGE模式、SAME_EDGE模式、SAME_EDGE_PIPELINED模式。
如图6是该模式对应的时序图,C是时钟信号,CE是时钟使能信号,D是输入数据,有图可知D在C的上升沿和下降沿都有数据变化的,即双沿传输。
Q1和Q2是输出数据,Q1在时钟上升沿输出在时钟上升沿采集到的数据,而Q2在时钟下降沿数据发生变化,用于输出在时钟下降沿时采集到的数据,后续逻辑只需要在时钟上升沿同时采集Q1和Q2的数据进行拼接就可以得到D在上升沿和下降沿传输的数据了。
如图7所示,输入信号与上个模式时序图一致,SAME_EDGE模式与OPPOSITE_EDGE模式的区别在于,SAME_EDGE模式的Q1和Q2只会在时钟C的上升沿输出数据。
图6中在第一个时钟上升沿之后,Q1输出采集到的D0A,由于此时还没有经过下降沿,Q1输出无效数据。在第一个下降沿,Q2采集D的数据,在第二个时钟上升沿之后,Q1输出采集第二个时钟上升沿采集的数据D2A,Q2输出第一个时钟下降沿采集的数据D1A。
上述两种方式都不是最常用的模式,最常用的数据采集模式是SAME_EDGE_PIPELINED模式,与SAME_EDGE模式类似,只是将Q1输出数据延迟一个时钟周期,让Q1和Q2的第一个数据至最后一个数据进行对齐,方便后续操作,代价是Q1输出数据延迟了一个是时钟周期。
图8显示了使用SAME_EDGE_PIPELINED模式的IDDR的时序图,Q1和Q2同时提供给FPGA逻辑,更方便用户使用。
IDDR原语的几个参数如表2所示,
参数名 | 描述 | 参数值 |
---|---|---|
DDR_CLK_EDGE | 设置相对于时钟沿的IDDR操作模式 | OPPOSITE_EDGE (默认), |
SAME_EDGE, | ||
SAME_EDGE_PIPELINED | ||
INIT_Q1 | 设置Q1默认初始值 | 0(默认),1 |
INIT_Q2 | 设置Q2默认初始值 | 0(默认),1 |
SRTYPE | 相对于时钟的置位/复位类型 | 异步(默认),同步 |
一般把DDR_CLK_EDGE参数设置为SAME_EDGE_PIPELINED模式即可,其余参数可以不进行设置,保持默认即可。
在vivado中获取原语模板的方法如图9所示,在Tools选项卡下点击Language Templates。
然后如图10所示操作,再搜索框搜索想要的原语模块名,此处为IDDR,然后选择对应器件下的对应原语,此处使用zynq7020,即A7。5处就是IDDR原语对应的例化模板,赋值到代码中使用即可。
通过下面一段代码SAME_EDGE_PIPELINED模式进行仿真,TestBench文件也放在下面,再Vivado中创建工程进行仿真。
原语模板如下:
IDDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_inst (
.Q1(Q1), // 1-bit output for positive edge of clock
.Q2(Q2), // 1-bit output for negative edge of clock
.C(C), // 1-bit clock input
.CE(CE), // 1-bit clock enable input
.D(D), // 1-bit DDR data input
.R(R), // 1-bit reset
.S(S) // 1-bit set
);
设计文件代码:
module iddr_ctrl(
input clk ,//系统时钟信号;
input rst ,//系统复位信号,高电平有效;
input clk_en ,//时钟使能信号;
input din ,//输入数据;
output dout1 ,//输出数据
output dout2
);
reg clk_en_r ;
//(* IOB = "TRUE" *)reg clk_en_r ;//将clk_en_r放在ILOGICE中;
//将clk_en打拍,用于验证IOB原语是否有效;
always@(posedge clk)begin
clk_en_r <= clk_en;
end
//例化IDDR原语
IDDR #(
.DDR_CLK_EDGE ("SAME_EDGE_PIPELINED" ),// "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0 ),// Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0 ),// Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
)
IDDR_inst (
.Q1 (dout1 ),// 1-bit output for positive edge of clock
.Q2 (dout2 ),// 1-bit output for negative edge of clock
.C (clk ),// 1-bit clock input
.CE (clk_en_r),// 1-bit clock enable input
.D (din ),// 1-bit DDR data input
.R (rst ),// 1-bit reset
.S (1'b0 ) // 1-bit set
);
endmodule
TestBench代码如下所示:
`timescale 1 ns/1 ns
module test();
parameter CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns;
parameter RST_TIME = 10 ;//系统复位持续时间,默认10个系统时钟周期;
reg clk ;//系统时钟,默认100MHz;
reg rst ;//系统复位,默认高电平有效;
reg clk_en ;
reg din ;
wire dout1 ;
wire dout2 ;
iddr_ctrl u_iddr_ctrl (
.clk ( clk ),
.rst ( rst ),
.clk_en ( clk_en ),
.din ( din ),
.dout1 ( dout1 ),
.dout2 ( dout2 )
);
//生成周期为CYCLE数值的系统时钟;
initial begin
clk = 1;
forever #(CYCLE/2) clk = ~clk;
end
//生成复位信号;
initial begin
rst = 0;
#2;
rst = 1;//开始时复位10个时钟;
#(RST_TIME*CYCLE);
rst = 0;
repeat(120) @(posedge clk);
$stop;//停止仿真;
end
initial begin
#1;
din = 1'b0;clk_en = 1'b0;
#(CYCLE*(10+RST_TIME));
clk_en = 1'b1;
#(CYCLE);
repeat(100)begin//产生100个双沿时钟数据。
#(CYCLE/2);
din = ({$random} % 2);
#(CYCLE/2);
din = ({$random} % 2);
end
#(CYCLE);
clk_en = 1'b0;
end
endmodule
仿真结果如图11所示,在时钟使能clk_en拉高后,输入信号din拉高后,在时钟的第二上升沿时,dout1和dout2同时输出高电平,后续波形也能与图8对应上,仿真通过。
将IDDR的模式进行修改,代码如下,对IDDR的OPPOSITE_EDGE模式进行仿真。
module iddr_ctrl(
input clk ,//系统时钟信号;
input rst ,//系统复位信号,高电平有效;
input clk_en ,//时钟使能信号;
input din ,//输入数据;
output dout1 ,//输出数据
output dout2
);
reg clk_en_r ;
//(* IOB = "TRUE" *)reg clk_en_r ;//将clk_en_r放在ILOGICE中;
//将clk_en打拍,用于验证IOB原语是否有效;
always@(posedge clk)begin
clk_en_r <= clk_en;
end
//例化IDDR原语
IDDR #(
.DDR_CLK_EDGE ("OPPOSITE_EDGE" ),// "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0 ),// Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0 ),// Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
)
IDDR_inst (
.Q1 (dout1 ),// 1-bit output for positive edge of clock
.Q2 (dout2 ),// 1-bit output for negative edge of clock
.C (clk ),// 1-bit clock input
.CE (clk_en_r),// 1-bit clock enable input
.D (din ),// 1-bit DDR data input
.R (rst ),// 1-bit reset
.S (1'b0 ) // 1-bit set
);
endmodule
仿真结果如图12所示,在din信号变化后,dout1在时钟上升沿输出采集到的数据,dout2在时钟下降沿输出在时钟下降沿采集到的数据,与时序图6对应,仿真通过。
同样TestBetch文件不变,只需要将IDDR的模式变为SAME_EDGE模式,就可以对该模式进行仿真,对应代码如下。
module iddr_ctrl(
input clk ,//系统时钟信号;
input rst ,//系统复位信号,高电平有效;
input clk_en ,//时钟使能信号;
input din ,//输入数据;
output dout1 ,//输出数据
output dout2
);
reg clk_en_r ;
//(* IOB = "TRUE" *)reg clk_en_r ;//将clk_en_r放在ILOGICE中;
//将clk_en打拍,用于验证IOB原语是否有效;
always@(posedge clk)begin
clk_en_r <= clk_en;
end
//例化IDDR原语
IDDR #(
.DDR_CLK_EDGE ("SAME_EDGE" ),// "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0 ),// Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0 ),// Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC" ) // Set/Reset type: "SYNC" or "ASYNC"
)
IDDR_inst (
.Q1 (dout1 ),// 1-bit output for positive edge of clock
.Q2 (dout2 ),// 1-bit output for negative edge of clock
.C (clk ),// 1-bit clock input
.CE (clk_en_r),// 1-bit clock enable input
.D (din ),// 1-bit DDR data input
.R (rst ),// 1-bit reset
.S (1'b0 ) // 1-bit set
);
endmodule
仿真结果如图13所示,在din信号变化后,dout1在时钟上升沿输出采集到的数据,dout2在时钟上升沿输出其在时钟下降沿采集到的数据,与时序图7对应,仿真通过。
上述仿真结束后,对工程进行综合、分配管脚、然后实现,在实现后,如图14所示,点击Open Implementation Design,然后选择Defaout Layout,放大查看走线。
Vivado默认是将走线关闭的,点击图15中方框部分,就可以打开走线视图,查看更加详细的走线,以及小的结构单元。
图16就是布局布线后的一个走线,图中Y143是din输入所在管脚,灰色部分就是IOB,经过白线先到IDDR,然后经过dout1输出FPGA。
图17是将din所在管脚放大,din的管脚在IOB33,信号从金属管脚PAD进入芯片,然后经过了一个IBUF,通过I端口输出。由图也可以知道IOB可以实现管脚的上拉(PAD左边有类似波浪线的上拉电阻)和下拉,有IBUF、OBUF(IBUF左边那个)。
上图中输出的信号经过一段距离传输,传输到图18所示的ILOGICE2的IDDR输入端口D了,由图18可知同一ILOGICE2(因为IDDR和IDELAYE2位置编号均为X1Y144)的IDDR和IDELAYE2分布在上下位置,此处没有使用IDELAYE2,信号就直接到达了IDDR。
图18是din的信号走向,经过了IDDR,那如果不使用IDDR的信号,是否如之前所说,会直接经过IDDR上面的组合路径输出呢?
我们可以通过查看clk_en信号的流向得到答案,这个信号没有经过IDDR,对应走线如下图19所示,结果与前文分析一致,所又输入信号都需要经过ILOGICE,没有使用ILOGICE内部IDDR、寄存器功能的信号,通过顶部组合逻辑电路输出。
最后图20是一个输出信号的管脚信号走向,目的是确定IOB单元内IBUF对面的就是OBUF结构,IBUF和OBUF是vivado自动对输入输出信号添加的。
前文讲了如果不使用ILOGICE的IDDR功能,可以将通过使用原语,将ILOGICE配置成触发器,这个触发器相比FPGA内部逻辑的触发器,更靠近FPGA管脚,使接口信号更有利于满足建立时间要求。
clk_en信号没有使用IDDR功能,那么此处在clk_en进入FPGA后,使用触发器打一拍后,先不做任何限制(去掉第10行代码注释,11行代码注释掉),然后对工程进行综合,去查看布线后的走线图,寄存器分布位置。
在vivado综合编译之后,如图21是clk_en走线图,信号从端口输入,然后经过ILOGICE的组合逻辑部分,然后进入到FPGA内部CLB的一个D触发器中。
经图21中三个部分放大,如图22所示,比较平常的结果。
如果在clk_en_r定义处添加IOB原语(将上述代码第10行注释,第11行注释取消), 在vivado综合编译之后,走线如图23所示,clk_en信号在经过ILOGICE时,并没有像图21一样,从组合逻辑部分直接输出,而是经过了下面部分。
将图23中ILOGICE部分进行放大,如图24所示,clk_en_r触发器就位于ILOGICE中IDDR位置,只是目前的功能是D触发器。
对上述代码进行仿真,查看clk_en是否被延迟一个时钟周期,仿真结果如图25所示,clk_en_r相对clk_en延迟一个时钟周期,仿真正确。
很明显图23中clk_en从PAD到clk_en_r触发器的路径,相比图21中的路径会短很多,并且走线都是固定的,clk_en_r的位置也是固定的,不能进行随意更换,在ADC采集数据时,可以将触发器放入ILOGICE减小延时。
总结本文就几点:
第一:了解ILOGICE的结构及功能,能够实现IDDR和触发器功能。
第二:熟悉IDDR的三种模式,常用的是SAME_EDGE_PIPELINED模式,并对三种模式依次仿真,通过查看走线图,更加详细了解IOB和ILOGICE的结构。
第三:通过使用原语,将触发器放入ILOGICE中,减少从FPGA管脚到触发器的路径。
最后需要此工程文件的用户,在公众号后台回复“IDDR”(不包括引号)即可,工程里只有一个设计文件,因为三个文件只需要修改IDDR的工作模式即可,IDDR会在千兆网接口中使用。