本人写的一个时钟对齐的模块,仅供参考
主要原因:
由于 FPGA 内部布局布线所带来的延时,到达 ISERDESE2 相应管脚的帧时钟、位时
钟与数据信号可能不再保持初始的相位关系,从而导致无法得到正确的串并转换数据,所以需
要利用 SelectIO 资源使时钟与数据恢复对应的相位关系
框架模型: LVDS接口模块电路框图
module clk_align(
input i_clk_dclk ,
input i_rst ,
input i_idelay_refclk ,
input i_clk_align_rst ,
output o_clk_hs ,
output o_clk_div ,
output o_clk_align_ok
);
parameter P_WAIT_INIT = 20 ,
P_STABLE_TIME = 100,
P_ALIGN_CNT = 100;
parameter IDLE = 0,
IDELYCTRL_INIT = 1,
IDLEY_INIT = 2,
ISERDES_INIT = 3,
CLOCK_ALIGN_PROCESS = 4,
WAIT_DATA_STABLE = 5,
ALIGN_OK = 6;
wire w_clk_out ;
wire idelay_rdy ;
wire w_dclk_delay ;
wire [7:0] sample_value ;
reg [2:0] r_custates ;
reg [2:0] r_nxstates ;
reg [7:0] r_states_cnt ;
reg [7:0] r_dclk_data ;
reg [15:0] r_align_cnt ;
reg r_clk_align_ok ;
reg r_f ;
reg r_idelay_ctrl_rst ;
reg r_idelay_rst ;
reg r_iserds_rst ;
reg r_idelay_load ;
reg r_idelay_ce ;
reg r_idelay_inc ;
assign o_clk_align_ok = r_clk_align_ok ;
always @ (posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_custates <= IDLE ;
else
r_custates <= r_nxstates;
end
always @ (*) begin
case (r_custates)
IDLE : begin
if (r_states_cnt == P_WAIT_INIT)
r_nxstates = IDELYCTRL_INIT;
else
r_nxstates = r_custates;
end
IDELYCTRL_INIT : begin
if (idelay_rdy && r_states_cnt == P_WAIT_INIT)
r_nxstates = IDLEY_INIT;
else
r_nxstates = r_custates;
end
IDLEY_INIT : begin
if (r_states_cnt == P_WAIT_INIT)
r_nxstates = ISERDES_INIT;
else
r_nxstates = r_custates;
end
ISERDES_INIT : begin
if (r_states_cnt == P_WAIT_INIT)
r_nxstates = WAIT_DATA_STABLE;
else
r_nxstates = r_custates;
end
CLOCK_ALIGN_PROCESS : begin
if (r_align_cnt == P_ALIGN_CNT -1)
r_nxstates = ALIGN_OK;
else
r_nxstates = WAIT_DATA_STABLE;
end
WAIT_DATA_STABLE : begin
if (r_states_cnt == P_STABLE_TIME -1)
r_nxstates = CLOCK_ALIGN_PROCESS;
else
r_nxstates = r_custates;
end
ALIGN_OK :begin
if (i_clk_align_rst)
r_nxstates = IDLE;
else
r_nxstates = r_custates;
end
default : begin
r_nxstates = IDLE;
end
endcase
end
// states cnt
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_states_cnt <= 0;
else if (r_custates != r_nxstates)
r_states_cnt <= 0;
else
r_states_cnt <= r_states_cnt + 1;
end
// idely ctrl rst
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_idelay_ctrl_rst <= 0;
else if (r_custates == IDELYCTRL_INIT && r_states_cnt <= 1)
r_idelay_ctrl_rst <= 1;
else
r_idelay_ctrl_rst <= 0;
end
//idely rst
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_idelay_rst <= 0;
else if (r_custates == IDLEY_INIT && r_states_cnt <= 1)
r_idelay_rst <= 1;
else
r_idelay_rst <= 0;
end
//iserdes rst
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_iserds_rst <= 0;
else if (r_custates == ISERDES_INIT && r_states_cnt <= 1)
r_iserds_rst <= 1;
else
r_iserds_rst <= 0;
end
//r_idelay_load
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_idelay_load <= 0;
else if (r_custates == IDLEY_INIT && r_states_cnt == 10)
r_idelay_load <= 1;
else
r_idelay_load <= 0;
end
// idelay ce and inc
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst) begin
r_idelay_ce <= 0;
r_idelay_inc <= 0;
end else if (r_custates == CLOCK_ALIGN_PROCESS && sample_value == 8'h00) begin
r_idelay_ce <= 1;
r_idelay_inc <= 1;
end else if (r_custates == CLOCK_ALIGN_PROCESS && sample_value == 8'hff) begin
r_idelay_ce <= 1;
r_idelay_inc <= 0;
end else begin
r_idelay_ce <= 0;
r_idelay_inc <= 0;
end
end
// compare
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_dclk_data <= 0;
else
r_dclk_data <= sample_value;
end
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_align_cnt <= 0;
else if (r_custates == CLOCK_ALIGN_PROCESS && r_align_cnt == P_ALIGN_CNT - 1)
r_align_cnt <= 0;
else if ( r_custates == CLOCK_ALIGN_PROCESS && r_f)
r_align_cnt <= r_align_cnt + 1;
else
r_align_cnt <= r_align_cnt;
end
// 数据不相等的标记
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_f <= 0;
else if (r_custates == CLOCK_ALIGN_PROCESS)
r_f <= 0;
else if (r_dclk_data != sample_value)
r_f <= 1;
else
r_f <= r_f;
end
// output clk_align flag
always @(posedge o_clk_div or posedge i_rst) begin
if (i_rst)
r_clk_align_ok <= 0;
else if (r_custates != ALIGN_OK)
r_clk_align_ok <= 0;
else if (r_custates == ALIGN_OK)
r_clk_align_ok <= 1;
else
r_clk_align_ok <= r_clk_align_ok;
end
/*-----------------------------------------------------XILINX PRIMITIVE-----------------------------------------------------------*/
// (*IODELAY_GROUP = clk*) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYCTRL IDELAYCTRL_inst (
.RDY(idelay_rdy), // 1-bit output: Ready output
.REFCLK(i_idelay_refclk), // 1-bit input: Reference clock input
.RST(r_idelay_ctrl_rst) // 1-bit input: Active high reset input
);
//(* IODELAY_GROUP = clk*) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
IDELAYE2 #(
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE("VARIABLE"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE(9), // Input delay tap setting (0-31)
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
.SIGNAL_PATTERN("CLOCK") // DATA, CLOCK input signal
)
IDELAYE2_inst (
.REGRST(r_idelay_rst), // 1-bit input: Active-high reset tap-delay input
.C(o_clk_div), // 1-bit input: Clock input
.CE(r_idelay_ce), // 1-bit input: Active high enable increment/decrement input
.INC(r_idelay_inc), // 1-bit input: Increment / Decrement tap delay input
.LD(r_idelay_load), // 1-bit input: Load IDELAY_VALUE input
.DATAIN(1'b0), // 1-bit input: Internal delay data input
.IDATAIN(i_clk_dclk), // 1-bit input: Data input from the I/O
.DATAOUT(w_dclk_delay), // 1-bit output: Delayed data output
.LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input
.CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN(5'b00000), // 5-bit input: Counter value input
.CNTVALUEOUT() // 5-bit output: Counter value output
);
BUFIO BUFIO_inst (
.O(o_clk_hs), // 1-bit output: Clock output (connect to I/O clock loads).
.I(w_clk_out) // 1-bit input: Clock input (connect to an IBUF or BUFMR).
);
BUFR #(
// .BUFR_DIVIDE("3"), // Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8" 3div,clk_out_div=100M
.BUFR_DIVIDE("4"), // Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8" 4div,clk_out_div=100M
.SIM_DEVICE("7SERIES") // Must be set to "7SERIES"
)
BUFR_inst (
.O(o_clk_div), // 1-bit output: Clock output port
.CE(1'b1), // 1-bit input: Active high, clock enable (Divided modes only)
//.CLR(1'b0), // 1-bit input: Active high, asynchronous clear (Divided modes only)
.CLR(i_rst), // 1-bit input: Active high, asynchronous clear (Divided modes only)
.I(w_clk_out) // 1-bit input: Clock buffer input driven by an IBUF, MMCM or local interconnect
);
ISERDESE2 #(
.SERDES_MODE("MASTER"), // MASTER, SLAVE
.INTERFACE_TYPE("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
.IOBDELAY("IBUF"), // NONE, BOTH, IBUF, IFD
.DATA_RATE("SDR"), // DDR, SDR
.DATA_WIDTH(8), // Parallel data width (2-8,10,14)
.DYN_CLKDIV_INV_EN("FALSE"),
.DYN_CLK_INV_EN("FALSE"),
.INIT_Q1(1'b0),
.INIT_Q2(1'b0),
.INIT_Q3(1'b0),
.INIT_Q4(1'b0),
.NUM_CE(1),
.OFB_USED("FALSE"),
.SRVAL_Q1(1'b0),
.SRVAL_Q2(1'b0),
.SRVAL_Q3(1'b0),
.SRVAL_Q4(1'b0)
)
ISERDESE2_inst (
.RST(r_iserds_rst),
.D(i_clk_dclk),
.DDLY(w_dclk_delay),
.O(w_clk_out),
.CLK(o_clk_hs),
.CLKB(1'b0),
.CLKDIV(o_clk_div),
.CLKDIVP(1'b0),
.DYNCLKDIVSEL(1'b0),
.DYNCLKSEL(1'b0),
.OCLK(1'b0),
.OCLKB(1'b0),
.OFB(1'b0),
.CE1(1'b1),
.CE2(1'b0),
.BITSLIP(1'b0),
.Q1(sample_value[0]),
.Q2(sample_value[1]),
.Q3(sample_value[2]),
.Q4(sample_value[3]),
.Q5(sample_value[4]),
.Q6(sample_value[5]),
.Q7(sample_value[6]),
.Q8(sample_value[7]),
.SHIFTOUT1(),
.SHIFTOUT2(),
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0)
);
endmodule