天天看點

RGMII接口(KSZ9031)

概述:

RGMII的時序是時鐘雙沿采樣,在預設的RGMII時序中,時鐘(RXC/TXC)邊沿與資料邊沿(TXD/RXD/TX_CTL/RX_CTL)的對齊,是以,FPGA想要正确收發資料,需要對TXC或RXC進行适當的延遲。由于最高時鐘為125MHz,最佳延遲為2ns。

RGMII接口(KSZ9031)

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
           

繼續閱讀