上篇文章介绍了ILOGIC的IDDR,这篇介绍一下ODDR。
简介
ODDR,归属于OLOGIC的范围内。是一种位于名叫IOB(FPGA的输出单元,离IO最近的逻辑块)的逻辑资源,能够以最快的速度到达FPGA的IO口上。顾名思义,DDR输出专用,何为DDR输出,即双倍速率输出,比较常见的应用有以太网的GMII转为RGMII输出,目的是节约时钟资源也节省了IO资源。一下为OLOGIC设计的框图:
话不多说,接下来直接介绍ODDR原语的使用。
ODDR
-
模式介绍:
ODDR和IDDR类似,也有两种模式,分别为OPPOSITE_EDGE Mode和SAME_EDGE Mode,功能描述也如上一篇文章所述,OPPOSITE_EDGE表示在时钟上升沿输出Q1端的数据,下降沿输出Q2端的数据,注意,这边的Q1和Q2表示和FPGA内部相连的端口,输出表示FPGA的IO口。以下是OPPOSITE_EDGE的时序图:
同理,SAME_EDGE模式下,Q1、Q2两个端口的数据在同一个时钟上升沿被读取,但是Q2的数据会在时钟的下降沿被输出,时序图如下所示:
重要说明
从上面的两种模式来看可能看不出什么,但是实际上ODDR有一个重要作用,那就是时钟输出(时钟增殖)。一般来说我们需要使用FPGA给外部芯片提供一个时钟引脚的时候都需要通过专用的时钟输出引脚,否则会导致时钟输出存在延迟,导致和数据段对不上这个问题(注:低频条件下问题不大,例如RGB屏幕显示,但总归是不好),所以我们可以通过一个ODDR原语,将内部时钟进行增殖输出到一个IO口,变成一个独立的时钟,这样一来就能保证时钟的延迟最小,同时驱动能力强(因为是独立的时钟源),具体的调用方法是另Q1为1,Q2为0,输入时钟的上升沿输出Q1(1),下降沿输出Q2(0),达到时钟增殖的目的(具体的代码在后面统一给出)。 -
原语框图
注意:这边虽然说复位和置位是一个端口,但实际上它们不能同时工作(同时给1),所以在例化的时候要分开给,或者其中一个直接不给信号(具体调用方法在后面)。 -
端口介绍
如上所述,复位和置位信号不可同时使用。 -
参数输入
以上参数包含了模式选择、初始值还有置位复位信号的作用类型(参考数电中学过的一些芯片,会有同步和异步的模式,和那个一样,于此就不多介绍了)。 -
代码介绍
话不多说,直接上案例:
// Verilog版本例子
// ODDR: Output Double Data Rate Output Register with Set, Reset
// and Clock Enable.
// 7 Series
// Xilinx HDL Language Template, version 2022.2
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
);
// End of ODDR_inst instantiation
--VHDL版本例子
Library UNISIM;
use UNISIM.vcomponents.all;
-- ODDR: Output Double Data Rate Output Register with Set, Reset
-- and Clock Enable.
-- 7 Series
-- Xilinx HDL Language Template, version 2022.2
ODDR_inst : ODDR
generic map(
DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE"
INIT => '0', -- Initial value for Q port ('1' or '0')
SRTYPE => "SYNC") -- Reset Type ("ASYNC" or "SYNC")
port map (
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 input
S => S -- 1-bit set input
);
-- End of ODDR_inst instantiation
接下来是调用的示例:
//1. 时钟输出调用示例:
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(clk_out), // 1-bit DDR output
.C(clk_in), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(1'b1), // 1-bit data input (positive edge)
.D2(1'b0), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S() // 1-bit set
);
//2. 数据输出模式调用
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(data_out), // 1-bit DDR output
.C(clk_in), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(DATA1), // 1-bit data input (positive edge)
.D2(DATA2), // 1-bit data input (negative edge)
.R(RESET), // 1-bit reset
.S() // 1-bit set
);
结语
ODDR的作用还有很多,本人学识尚浅,只用到过以上所述的内容,如果有朋友发现错误或者有更好的使用方法,欢迎评论区指出。