概述:
RGMII的时序是时钟双沿采样,在默认的RGMII时序中,时钟(RXC/TXC)边沿与数据边沿(TXD/RXD/TX_CTL/RX_CTL)的对齐,因此,FPGA想要正确收发数据,需要对TXC或RXC进行适当的延迟。由于最高时钟为125MHz,最佳延迟为2ns。
RGMII接口时序延迟设计:
在三速网络应用中,通常的方案需要125MHz,25MHz和2.5MHz的时钟和相移90°的时钟信号。同时锁相环可能无法产生2.5MHz时钟,该方案时钟数量多达6个,需要2级选择器,不适合时序约束。另一种方案是,将输入时钟进行一定的延迟,然后用延迟后的时钟信号进行内部数据处理,最后将延迟后的时钟信号在通过一定的延迟输出,这样即可保证三速网络的应用,同时时钟数量少,适合时序分析和约束。
在如Xilinx的7z020、7z030等系列中,有IDELAY2和ODELAY2对时钟和数据进行精确延迟,可以实现三速率应用。使用IDELAY2和ODELAY2还需要一个200MHz的参考时钟和IDELAYCTRL,并将其约束成一个group。其使用方法如下:
// IDELAYCTRL is needed for calibration
(* IODELAY_GROUP = "rgmii_interface" *)
IDELAYCTRL idelayctrl_inst
(
.RDY ( ),
.REFCLK (ref_clock_bufg ),
.RST (idelay_reset )
);
//delay the output clock
defparam odelay2_inst.CINVCTRL_SEL = "FALSE" ;
defparam odelay2_inst.DELAY_SRC = "CLKIN" ;
defparam odelay2_inst.HIGH_PERFORMANCE_MODE = "FALSE" ;
defparam odelay2_inst.ODELAY_TYPE = "FIXED" ;
defparam odelay2_inst.ODELAY_VALUE = 25 ;
defparam odelay2_inst.PIPE_SEL = "FALSE" ;
defparam odelay2_inst.REFCLK_FREQUENCY = 200.0 ;
defparam odelay2_inst.SIGNAL_PATTERN = "CLOCK" ;
(* IODELAY_GROUP = "rgmii_interface" *)
ODELAYE2 odelay2_inst
(
.DATAOUT (clk_out_delay ),
.C (1'b0 ),
.CE (1'b0 ),
.INC (1'b0 ),
.ODATAIN (1'b0 ),
.LD (idelay_reset ),
.LDPIPEEN (1'b0 ),
.REGRST (1'b0 ),
.CLKIN (rgmii_clk ),
.CNTVALUEIN (5'b00000 ),
.CNTVALUEOUT ( ),
.CINVCTRL (1'b0 )
);
// delay the input clock
defparam idelay2_inst.CINVCTRL_SEL = "FALSE" ;
defparam idelay2_inst.DELAY_SRC = "IDATAIN" ;
defparam idelay2_inst.HIGH_PERFORMANCE_MODE = "FALSE" ;
defparam idelay2_inst.IDELAY_TYPE = "FIXED" ;
defparam idelay2_inst.IDELAY_VALUE = 25 ;
defparam idelay2_inst.REFCLK_FREQUENCY = 200.0 ;
defparam idelay2_inst.PIPE_SEL = "FALSE" ;
defparam idelay2_inst.SIGNAL_PATTERN = "CLOCK" ;
(* IODELAY_GROUP = "rgmii_interface" *)
IDELAYE2 idelay2_inst
(
.DATAOUT (clk_in_delay ), // Delayed clock
.DATAIN (1'b0 ), // Data from FPGA logic
.C (1'b0 ),
.CE (1'b0 ),
.INC (1'b0 ),
.IDATAIN (clk_in ),
.LD (idelay_reset ),
.LDPIPEEN (1'b0 ),
.REGRST (1'b0 ),
.CNTVALUEIN (5'b00000 ),
.CNTVALUEOUT ( ),
.CINVCTRL (1'b0 )
);
在Altera的Cyclone系列中,可以通过IOE delay实现。在Assignment Editor中通过Decrease Input Delay 或则 Increase Delay to Output Pin.或其他Delay。
由于部分FPGA并不完全具备延迟调节的功能,因此,可以通过SMI接口调节PHY的内部延迟达到预计效果。其中,数据的延迟范围是-0.42~0.48ns,时钟的延迟范围是-0.9~0.96ns。
KSZ9031的SMI接口程序:
// ==============================================================
// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2016.4
// Copyright (C) 1986-2016 Xilinx, Inc. All Rights Reserved.
//
// ===========================================================
`timescale 1 ns / 1 ps
module smi (
reset ,
sysclk ,
start ,//start
ack ,//ack
op_mode ,//1-read,0-write
phy_addr ,
reg_addr ,
write_data ,
read_data ,
read_data_val ,
done ,
mdc ,
mdi ,
mdo ,
mdoe
);
parameter N = 50_000_000 ;//分频因子,MDC时钟频率=时钟/N;
localparam Nmax = N-1 ;
localparam wd = Nmax>0 ? clogb2(Nmax) : 1;
input reset ;
input sysclk ;
input start ;
output ack ;
input op_mode ;
input [2:0] phy_addr ;
input [4:0] reg_addr ;
input [15:0] write_data ;
output [15:0] read_data ;
output read_data_val ;
output done ;
output mdc ;
input mdi ;
output mdo ;
output mdoe ;
reg [15:0] read_data ;
reg mdoe = 1'b0 ;
reg mdo ;
reg done ;
reg read_data_val ;
reg mdc ;
reg ack='b0 ;
wire mdi ;
reg [wd-1:0] sys_cnt ={wd{1'b0}} ;
reg clk_en ;
reg [31:0] data_reg ;
reg read_flag ;
reg [ 7:0] clk_cnt=8'd0 ;
//---0-----1-----2-----
//-start---x-----------
//--------ack----x-----
//clk_cnt[7]:1-work,0-idle
//clk_cnt[0]:mdc
//------------------------SMI timing-------------------------------
// preamble | sof | op | phy | reg | TA | data | IDLE
// 32x1 01 10 00aaa rrrrr z0 16*x z (read)
// 32x1 01 01 00aaa rrrrr 10 16*x z (write)
//-----------------------------------------------------------------
//preamble: 128<=clk_cnt<192,
//sof : 192<=clk_cnt<196,
//op_code : 196<=clk_cnt<200,
//phy addr: 200<=clk_cnt<210,
//reg addr: 210<=clk_cnt<220,
//TA : 220<=clk_cnt<224,
//data : 224<=clk_cnt<256,
//IDLE : 0 <=clk_cnt<127,
//分频产生目标MDC时钟频率
always @ ( posedge sysclk )
if(sys_cnt == Nmax)
sys_cnt <= {wd{1'b0}};
else
sys_cnt <= sys_cnt + 1;
always @ ( posedge sysclk )
clk_en <= sys_cnt == Nmax;
//握手信号,开始和应答
always @ ( posedge sysclk )
if(clk_en)
begin
if(clk_cnt==0)
begin
if(start)
clk_cnt <= 7'd1;
end
else
clk_cnt <= clk_cnt + 7'd1;
end
always @ ( posedge sysclk )
ack <= clk_en & (clk_cnt == 0) & start;
//MDC时钟信号
always @ ( posedge sysclk )
mdc <= clk_cnt[0] & clk_cnt[7];
//MDIO输出
always @ ( posedge sysclk )
if(clk_cnt[7]&&clk_cnt<=8'd191)
mdo <= 1'b1;
else
mdo <= data_reg[31];
//MDIO输出使能
always @ ( posedge sysclk )
if(read_flag)
mdoe <= (clk_cnt[7] && clk_cnt<=8'd219);
else
mdoe <= clk_cnt[7];
//MDIO输入
always @ ( posedge sysclk )
if(!mdoe & clk_cnt[0] & clk_en)
read_data <= {read_data,mdi};
//MDIO输入数据完成
always @ ( posedge sysclk )
read_data_val <= (clk_en&read_flag)&(clk_cnt==8'd254);
//传输完成
always @ ( posedge sysclk )
done <= clk_en & (clk_cnt==8'd255);
//输出移位寄存器
always @ ( posedge sysclk )
if(clk_en)
begin
if(clk_cnt==0)
begin
if(start)
begin
data_reg <= {2'b01,op_mode,!op_mode,
2'b00,phy_addr,reg_addr,
2'b10,write_data};
read_flag <= op_mode;
end
end
else if((clk_cnt[7:6]==2'b11)&clk_cnt[0])
data_reg <= {data_reg,1'b0};
end
//求log2(xin)
function integer clogb2;
input integer depth;
for (clogb2=0; depth>0; clogb2=clogb2+1)
depth = depth >> 1;
endfunction
endmodule //smi