上文中配置输出差分时钟,BUFG驱动后直接转单端从时钟管脚输出了,而参考 https://blog.csdn.net/zkf0100007/article/details/82559250 中使用了原语ODDR对BUFG输出的时钟又做了处理,本文探讨一下ODDR用于时钟输出时的作用。
参考:
https://forums.xilinx.com/t5/Other-FPGA-Architecture/Clock-capable-pin-pair-as-input-and-output/m-p/900002#M29713
一、ODDR概述及使用方法
ODDR(Output Double Data Rate,输出双倍数据速率):
在DDR接口中,ODDR用于发送时钟和数据;
在SDR接口中,ODDR转发时钟(仍在时钟树内),输出端要直连到输出port,不可加逻辑,连接方式:输出时钟连接ODDR的C引脚,D1固定值1'b1, D2固定值1'b0,CE固定值1’b1,ODDR的输出Q连接到OBUF;
时钟输入有限制,需要从SRCC或者MRCC专用时钟输入引脚输入,时钟输出可以在任何引脚上输出。当输出时钟时,即使使用的是时钟专用输入管脚去输出时钟,也等同于使用普通的GPIO管脚输出时钟。
输出时钟的最佳方法是使用ODDR来转发时钟(假设输出的时钟是一个专用时钟网络上的时钟)。每个IOB(IO Bank)都具有ODDR功能。
这样做时,内部时钟一直停留在专用的时钟网络上,直到ODDR,永远不需要进入一般的路由结构。不建议直接将时钟带到OBUF,因为这需要内部时钟离开专用时钟网络,通过一般的fabric路由线路路由到OBUF。
对Spartan6里面,必须用ODDR寄存器输出。
二、时钟配置
参考https://blog.csdn.net/zkf0100007/article/details/82559250 ,此博客中使用了ODDR输出,通过查阅相关资料,都建议在输出时钟时加入ODDR原语,现在加入ODDR对比一下加入前后的不同。
上文 ZC706评估板IBERT误码率测试和眼图扫描【GT高速串行收发器】【IBERT】【FPGA】【眼图】【FPGA探索者】
加入ODDR代码,D1接高电平,D2接低电平,C接时钟,Q输出。
代码语言:javascript复制wire user_clk;
IBUFDS IBUFDS_inst_user_clk(
.O(user_clk), // Buffer output
.I(USRCLK_P_I), // Diff_p buffer input
.IB(USRCLK_N_I) // Diff_n buffer input
);
wire user_clk_bufg;
BUFG BUFG_inst_user_clk (
.O(user_clk_bufg), // 1-bit output: Clock output
.I(user_clk)
);
wire user_clk_bufg_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_out_clock_inst_user_clock (
.Q(user_clk_bufg_oddr), // 1-bit DDR output
.C(user_clk_bufg), // 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-bit reset
.S() // 1-bit set
);
OBUFDS OBUFDS_inst_user_clock (
.O (USER_SMA_CLOCK_P_O
.OB(USER_SMA_CLOCK_N_O), // Diff_n output
.I (user_clk_bufg_oddr) // Buffer input
)
不加ODDR,布局布线后RTL图如下:
加ODDR,布局布线后RTL图如下:
布局布线的资源中,下图左侧显示没有加ODDR的,右侧是加入了ODDR,可见右侧输出时钟经BUFG后先到输出引脚附近的ODDR(蓝线和紫线交汇处),经ODDR转发后到PAD管脚输出。
三、性能对比
按照Xilinx和网上的一些说法,加入ODDR后输出时钟的质量会更好。
输出DDR可以转发一个时钟副本到输出。这对于传播具有相同延迟的时钟和DDR数据、以及生成多个时钟(其中每个时钟负载都有惟一的时钟驱动)非常有用。这是通过将ODDR的D1输入高电平并且D2输入低电平来实现的。Xilinx建议使用这种方案将时钟从FPGA逻辑转发到输出引脚。
如下图所示是加入ODDR前后的时序裕量和资源消耗对比,可以看到,对WNS(Worst Negative Slack,最差负时序裕量)和WHS(Worst Hold Slack,最差保持时序裕量)、资源使用在加入前后没有明显的区别。实测加入ODDR前后误码率基本一致,眼图扫描结果也一致。
按照Xilinx的推荐,在输出时钟时最好还是把ODDR加上。这个测试用例没有体现出ODDR的优势,也许在资源使用较多、时钟频率更高时才能体现。另外,这里只是输出了时钟,没有输出使用该时钟的数据。
很多人说时钟直接从BUFG输出到管脚会报错,必须加约束或者ODDR,目前我在ZYNQ7045上没有发现此问题。
ODDR的使用场景还在于OSERDES、FPGA的源同步的系统设计,用ODDR使得随路时钟和数据在输出时是严格同步的,保证严格的相位对齐关系。