ODDR位于OLOGIC中,可以把单沿传输的数据转换为双沿传输的数据, 在讲解ODDR功能之前,需要先了解OLOGIC的结构及功能。
1、OLOGIC
OLOGIC块位于IOB的内侧,FPGA内部信号想要输出到管脚,都必须经过OLOGIC。OLOGIC资源的类型为OLOGICE2(HP I/O Bank)和OLOGICE3(HR I/O Bank),两者在功能和结构上是相同的,所以本文称为OLOGIC。
图1是OLOGIC的结构框图,分为上下两部分,下半部分用于配置输出数据路径,上半部分用于配置三态控制路径,分别实现对数据、三态信号进行单沿转双沿的功能,两部分具有共同的时钟 (CLK),但是使能信号不同(OCE和TCE)。
如果输出的信号不使用OLOGIC中的ODDR功能,那么此时信号从图1中红线路径进行传输,从组合逻辑电路输出到IOB模块。如果要使用OLOGIC模块中的D触发器功能,那么信号从D1进入OLOGIC模块,沿绿色信号线进行传输。如果要使用OLOGIC的ODDR功能,把单沿传输的信号转换为双沿传输的信号,此时需要两个输入信号D1、D2沿蓝色路径进行传输。
图2是FPGA中OLOGIC实际的框图,因为该电路还可以被配置为OSERDESE2,所以相比图1会多出一些信号端口。
2、ODDR原语
图3是ODDR原语框图,与IDDR一样不支持同时复位和置位。ODDR端口信号如表1所示,表2描述了ODDR原语的可用参数。
端口名 | 含义 |
---|---|
C | 时钟输入信号。 |
CE | 钟使能信号,高电平有效。 |
D1、D2 | ODDR输入信号。 |
S/R | 置位/复位引脚,高电平有效。 |
Q | ODDR输出信号。 |
参数名 | 含义 | 取值 |
---|---|---|
DDR_CLK_EDGE | ODDR工作模式 | OPPOSITE_EDGE (默认), SAME_EDGE |
INIT | 设置Q端口的初始值 | 0(默认),1 |
SRTYPE | 设置复位/置位相对于时钟的类型 | ASYNC, SYNC(默认) |
上述的信号和参数都比较简单,与前面IDDR原语相似,不做过多解释。ODDR只有两种工作模式,相比IDDR会少一种,下文对两种模式进行讲解。
图4是OPPOSITE_EDGE模式的时序图,在时钟CLK上升沿采集D1信号D1A,并在时钟上升沿把D1A输出到OQ。然后在时钟CLK下降沿采集D2信号D2A,并在下降沿将采集到的信号输出。这种模式使用起来会相对麻烦,FPGA内部需要在时钟上升沿给D1赋值,在时钟下降沿给D2赋值,一般不使用。
图5是SAME_EDGE模式的时序图,在时钟CLK上升沿 同时采集D1、D2的数据,OQ再时钟上升沿输出采集的D1数据,再下降沿输出采集的D2数据。这种方式实现比较简单,属于常用模式。
3、ODDR模式仿真
ODDR原语的模板如下所示:
ODDR #(.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_inst (.Q(Q), // 1-bit DDR output.C(C), // 1-bit clock input.CE(CE), // 1-bit clock enable input.D1(D1), // 1-bit data input (positive edge).D2(D2), // 1-bit data input (negative edge).R(R), // 1-bit reset.S(S) // 1-bit set);
接下来对ODDR的两种工作模式进行仿真,对应的设计文件如下所示,din0在内部D触发器打一拍后得到dout0输出,din0经过OLOGIC内部的D触发器打一拍后得到dout1输出。两个单沿输入的信号din1、din2转化位单沿信号dout2,对应代码如下所示。
module oddr_ctrl(input clk ,//系统时钟信号;input rst ,//系统复位信号,高电平有效;input clk_en ,//时钟使能信号;input din0 ,//输入数据;input din1 ,//输入数据;input din2 ,//输入数据;output dout0 ,//输出数据output dout1 ,//输出数据output dout2
); reg dout0 ;(* IOB = "TRUE" *)reg dout1 ;//将dout1放在ILOGICE中;always@(posedge clk)begindout0 <= din0;dout1 <= din0;end//例化ODDR原语ODDR #(.DDR_CLK_EDGE ( "OPPOSITE_EDGE" ),// "OPPOSITE_EDGE" or "SAME_EDGE" .INIT ( 1'b0 ),// Initial value of Q: 1'b0 or 1'b1.SRTYPE ( "SYNC" ) // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_inst (.Q ( dout2 ),// 1-bit DDR output.C ( clk ),// 1-bit clock input.CE ( clk_en ),// 1-bit clock enable input.D1 ( din1 ),// 1-bit data input (positive edge).D2 ( din2 ),// 1-bit data input (negative edge).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;reg clk ;//系统时钟,默认100MHz;reg rst ;//系统复位,默认高电平有效;reg clk_en ;reg din0 ;reg din1 ;reg din2 ;wire dout0 ;wire dout1 ;wire dout2 ;oddr_ctrl u_oddr_ctrl (.clk ( clk ),.rst ( rst ),.clk_en ( clk_en ),.din0 ( din0 ),.din1 ( din1 ),.din2 ( din2 ),.dout0 ( dout0 ),.dout1 ( dout1 ),.dout2 ( dout2 ));//生成周期为CYCLE数值的系统时钟;initial beginclk = 1;forever #(CYCLE/2) clk = ~clk;end//生成复位信号;initial beginrst = 0;#2;rst = 1;//开始时复位10个时钟;#(10*CYCLE);rst = 0;endinitial begin#1;clk_en = 1'b0;din2 = 1'b0;din0 = 1'b0;din1 = 1'b0;#(CYCLE*20);clk_en = 1'b1;#(CYCLE);repeat(100)begin//产生100个双沿时钟数据。#(CYCLE/2);din0 = ({$random} % 2);din1 = ({$random} % 2);#(CYCLE/2);din2 = ({$random} % 2);end#(CYCLE);clk_en = 1'b0;#(10*CYCLE);$stop;//停止仿真;endendmodule
首先对OPPOSITE_EDGE模式进行仿真,对应的TestBench代码如下所示,仿真结果如图6所示。
图6仿真结果与图4的时序图一致,不做过多解释。
然后对SAME_EDGE模式进行仿真,对应的设计文件:
module oddr_ctrl(input clk ,//系统时钟信号;input rst ,//系统复位信号,高电平有效;input clk_en ,//时钟使能信号;input din0 ,//输入数据;input din1 ,//输入数据;input din2 ,//输入数据;output dout0 ,//输出数据output dout1 ,//输出数据output dout2
); reg dout0 ;reg doutr ;(* IOB = "TRUE" *)reg dout1 ;//将dout1放在ILOGICE中;always@(posedge clk)begindoutr <= din0;dout0 <= doutr;dout1 <= doutr;end//例化ODDR原语ODDR #(.DDR_CLK_EDGE ( "SAME_EDGE" ),// "OPPOSITE_EDGE" or "SAME_EDGE" .INIT ( 1'b0 ),// Initial value of Q: 1'b0 or 1'b1.SRTYPE ( "SYNC" ) // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_inst (.Q ( dout2 ),// 1-bit DDR output.C ( clk ),// 1-bit clock input.CE ( clk_en ),// 1-bit clock enable input.D1 ( din1 ),// 1-bit data input (positive edge).D2 ( din2 ),// 1-bit data input (negative edge).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;reg clk ;//系统时钟,默认100MHz;reg rst ;//系统复位,默认高电平有效;reg clk_en ;reg din0 ;reg din1 ;reg din2 ;wire dout0 ;wire dout1 ;wire dout2 ;oddr_ctrl u_oddr_ctrl (.clk ( clk ),.rst ( rst ),.clk_en ( clk_en ),.din0 ( din0 ),.din1 ( din1 ),.din2 ( din2 ),.dout0 ( dout0 ),.dout1 ( dout1 ),.dout2 ( dout2 ));//生成周期为CYCLE数值的系统时钟;initial beginclk = 1;forever #(CYCLE/2) clk = ~clk;end//生成复位信号;initial beginrst = 0;#2;rst = 1;//开始时复位10个时钟;#(10*CYCLE);rst = 0;endinitial begin#1;clk_en = 1'b0;din2 = 1'b0;din0 = 1'b0;din1 = 1'b0;#(CYCLE*20);clk_en = 1'b1;#(CYCLE);repeat(100)begin//产生100个双沿时钟数据。#(CYCLE);din0 = ({$random} % 2);din1 = ({$random} % 2);din2 = ({$random} % 2);end#(CYCLE);clk_en = 1'b0;#(10*CYCLE);$stop;//停止仿真;endendmodule
仿真结果如图7所示。
图7仿真结果与图5基本一致,不再过多解释。图8是该模式下时钟使能无效时仿真结果,此时输出信号将保持不变。
上述仿真均与前文理论一致,下面将工程信号引脚分配,对工程进行编译,查看走线的图。
4、OLOGIC中触发器(OFD)的使用方式
在vivado中打开走线的方式在讲解IDDR原语时已经进行了讲解,本文不再赘述。
前文的代码中dout0与dout1的代码都相同,都是使用D触发器对din0打一拍,然后输出,通过查看dout0和dout1的寄存器位置,得到OLOGIC中组合电路和触发器功能的使用方式。图9是din0到dout0信号的走线图,红框处是寄存器所在位置,白线是信号的走线。
dout0信号是没有使用OLOGIC中的触发器和ODDR功能的,图10就是dou0信号经过OLOGIC时的路径,与前文讲解一致,直接经过组合逻辑输出。
如图11所示,是dout1信号在FPGA内部的走向,路径上又两个触发器,其中一个在OLOGIC中。
将OLOGIC放大,如图12所示,可知dout1触发器在OLOGIC中。
最后查看dout2信号的走向,如图13所示,din1和din2输入FPGA后,在OLOGIC进行单沿转双沿信号,然后通过dout2管脚输出。
将对应的OLOGIC放大,可见其实现的是ODDR功能,信号流向与前文讲解一致。
综上,OLOGIC与ILOGIC功能类似,本文主要是讲解ODDR的工作模式,并对工作模式进行仿真,同时将OLOGIC的使用方式进行讲解。掌握ODDR使用方式的同时,也知道如何使用OLOGIC中的触发器(使用IOB=TRUE原语,查看设计文件中dout1信号的定义)功能,以及OLOGIC在FPGA中的位置。
OLOGIC中的触发器相对于FPGA内部触发器更靠近管脚,并且触发器输出与IOB之间的路径是固定的,对于多bit数据输出更有利于对齐。
最后需要此工程文件的用户,在公众号后台回复“ODDR”(不包括引号)即可。