您的位置:首页 > 理论基础 > 计算机网络

在V4中如何使用局部时钟网络

2017-10-27 15:09 190 查看
在大多数FPGA中,使用全局时钟资源(BUFG)可以得到延迟小,一致性高的时钟资源,但是全局时钟资源一般是非常有限的,而且使用全局时钟资源处理IO同步问题显得有些大材小用,而且由于全局时钟考虑的是较大区域内的时钟延迟,在局部未必能做到最好。Xilinx在V4中提供了另一种时钟网络:局部时钟网络专门用于解决小范围,尤其是IO上的时钟同步问题。

V4使用具有时钟能力的IO(Clock
Capable I/O,简称为CC),进行局部的IO采样。CC按对出现,即可以做为差分局部时钟,也可以做为单端的局部时钟。通过IO时钟驱动器(I/O
Clock Buffer,简称BUFIO),在IO列中驱动局部时钟网络,对同一局部时钟域内的IO信号进行采样,从而达到以最小的延时采样相关信号的目的。CC可以采样的IO均分布在其周围,且属于同一个局部时钟域,在PCB上这些IO(包括CC)具有相似的布线轨迹和区域,受到的干扰也相差不多,使用CC对这些信号采样,可以得到最大的眼图。

局部时钟网络不依赖于全局时钟,但是BUFIO只能用于驱动IO,不能驱动任何逻辑资源(如CLB、BlockRAM等),通过CC采样后的IO信号还需要经过局部时钟驱动(Regional
Clock Buffer,简称BUFR)再次采样才能被内部逻辑使用。局部时钟的数据采样结构如图,描述了通过CC采样其周边IO信号并传递到内部逻辑的过程。BUFIO和BUFR均为V4独有的库原件,需要通过库原语方式添加到设计中使用,这些库原语的使用可参见ISE随工具文档v4ldl.pdf《Virtex-4
Libraries Guide for HDL Designs》。

具体如何使用局部时钟网络呢,下面以RGMII接口(不包括RGMII数据处理部分)。

module rgmii(rst,rx_clk, rx_data, rx_dv, tx_clk, tx_en, tx_data);

input rst;

input rx_clk;

input [3:0] rx_data;

input rx_dv;

input tx_clk;

output tx_en;

output [3:0] tx_data;

wire clk_buf;

wire inside_clk;

wire hrx_dv;

wire lrx_dv;

wire empty;

wire [3:0] hrx_data;

wire [3:0] lrx_data;

wire [7:0] data_out;

BUFIO BUFIO_inst (

.O(clk_buf), // Clock buffer output

.I(rx_clk) // Clock buffer input

);

BUFR #(

.BUFR_DIVIDE("BYPASS"), // "BYPASS", "1", "2", "3", "4",
"5", "6", "7", "8

.SIM_DEVICE("VIRTEX4") // Specify target device, "VIRTEX4"
or "VIRTEX5

) BUFR_inst (

.O(inside_clk), // Clock buffer output

.CE(1), // Clock enable input

.CLR(rst), // Clock buffer reset input

.I(clk_buf) // Clock buffer input

);

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_inst0 (

.Q1(hrx_data[0]), // 1-bit output for positive edge of clock

.Q2(lrx_data[0]), // 1-bit output for negative edge of clock

.C(clk_buf), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D(rx_data[0]), // 1-bit DDR data input

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst1 (

.Q1(hrx_data[1]), // 1-bit output for positive edge of clock

.Q2(lrx_data[1]), // 1-bit output for negative edge of clock

.C(clk_buf), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D(rx_data[1]), // 1-bit DDR data input

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst2 (

.Q1(hrx_data[2]), // 1-bit output for positive edge of clock

.Q2(lrx_data[2]), // 1-bit output for negative edge of clock

.C(clk_buf), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D(rx_data[2]), // 1-bit DDR data input

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst3 (

.Q1(hrx_data[3]), // 1-bit output for positive edge of clock

.Q2(lrx_data[3]), // 1-bit output for negative edge of clock

.C(clk_buf), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D(rx_data[3]), // 1-bit DDR data input

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst4 (

.Q1(hrx_dv), // 1-bit output for positive edge of clock

.Q2(lrx_dv), // 1-bit output for negative edge of clock

.C(clk_buf), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D(rx_dv), // 1-bit DDR data input

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

rj_fifo rj_fifo(

.wr_clk(inside_clk),

.rd_clk(tx_clk),

.din({hrx_data,lrx_data}),

.rd_en(!empty),

.rst(rst),

.wr_en(hrx_dv & lrx_dv),

.dout(data_out),

.empty(empty),

.full()

);

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_inst0 (

.Q(tx_data[0]), // 1-bit DDR output

.C(tx_clk), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D1(data_out[0]), // 1-bit data input (positive edge)

.D2(data_out[1]), // 1-bit data input (negative edge)

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst1 (

.Q(tx_data[1]), // 1-bit DDR output

.C(tx_clk), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D1(data_out[2]), // 1-bit data input (positive edge)

.D2(data_out[3]), // 1-bit data input (negative edge)

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst2 (

.Q(tx_data[2]), // 1-bit DDR output

.C(tx_clk), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D1(data_out[4]), // 1-bit data input (positive edge)

.D2(data_out[5]), // 1-bit data input (negative edge)

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst3 (

.Q(tx_data[3]), // 1-bit DDR output

.C(tx_clk), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D1(data_out[6]), // 1-bit data input (positive edge)

.D2(data_out[7]), // 1-bit data input (negative edge)

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

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_inst4 (

.Q(tx_en), // 1-bit DDR output

.C(tx_clk), // 1-bit clock input

.CE(1), // 1-bit clock enable input

.D1(!empty), // 1-bit data input (positive edge)

.D2(!empty), // 1-bit data input (negative edge)

.R(rst), // 1-bit reset

.S(!rst) // 1-bit set

);

endmodule

程序结构比较简单,通过BUFIO将接收时钟转变为局部时钟rx_clk,通过IOB中的双沿寄存器IDDR捕获PIN上的RGMII输入数据;通过BUFR将接收时钟转变为内部逻辑可用时钟inside_clk将接收的数据存入fifo,这样就完成了RGMII的信号接收。

RGMII的信号处理不需要用到局部时钟,只是使用发送时钟tx_clk将数据从fifo中读出,并通过IOB双沿寄存器发送出去即可,同时也需要将tx_clk发送到FPGA片外。

使用局部时钟可以节省全局时钟资源,在做复杂时钟系统时非常有用,比如需要做10个RGMII接口,就不得不用10个DCM(因为每个RGMII口的接收数据时钟rx_clk是不同源的);但使用局部时钟,就几乎不需要使用DCM。

当完成RGMII代码后,需要指定管脚位置,这样带来一个新问题:局部时钟只能作用同一个区域(region)中的IO,那么每个局部时钟在FPGA中的分布,以及影响哪些IO呢,我们以V4
LX 100-1148芯片来说明。

建立一个空的ucf文件,通过ISE的Assign
Package Pins功能打开指定管脚窗口,如下图:

打开后的界面如下图:

该界面隐含设置为使用不同的颜色显示FPGA的Bank,我们需要通过单击Show
Clock Region(上图红圈处)图标将其转换到使用不同颜色显示FPGA的不同局部时钟域。这样可以很方便的通过颜色了解哪些局部时钟(CC)影响哪些IO,相同颜色区域的任何一个CC都可以作用于同颜色区域里的任何IO,但不能作用于其它颜色区域里的IO。可以看到每个颜色区域(局部时钟域)内都有1-2对六边形标志的管脚(上图绿圈处),这些就是局部时钟(CC)管脚,,与其它圆形标志的普通IO是有区别的。

局部放大后得到下图,可以看到我们需要将局部时钟管脚分布在CC管脚上(六边形标志),需要注意,六边形标志的管脚是成对出现的,当使用单端信号做为局部时钟时(如LVTTL,LVCMOS等),只能将做为局部时钟的信号分布在标为P的CC管脚上,例如上图,我们将rxc_a(RGMII接收时钟,即前文例程中的rx_clk)分布在B28管脚上(IO_L25P_CC_LC_5);当使用差分信号做为局部时钟时(如LVDS等),将局部时钟的信号分布在成对P/N的CC管脚上,例如B28/C28(IO_L25P_CC_LC_5/IO_L25N_CC_LC_5)。

需要被局部时钟采样的管脚,例如RGMII中的接收数据信号rx_data和rx_dv都需要分布在深绿色的区域内。

RGMII的发送时钟没有使用局部时钟,在可任意分布,不受影响。

分布完后,通过布局布线如果出现错误,对照前文的注意事项看有哪些没有注意,再修改分布。如果布局布线不出错,就说明分布已经成功了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: