Menu Close

网络综合实验 Verilog工程(eth_phy_core.v及其子模块)

1. eth_phy_core.v

eth_phy_core.v是PHY的核心模块,如图1所示。在这个模块里例化了速度检测模块phy_detect.v,PHY发送和接收顶层模块ETH_PHY.v和MDIO读写配置RTL8211E芯片的模块mdio_app.v。eth_phy_core.v代码如下:

 

图1 eth_phy_core.v模块

`timescale 1ns / 1ps

module eth_phy_core #
(
    parameter MAIN_CLK = 125,
    parameter PHY_SPEED = "AUTO"  // "AUTO", "1G", "100M", "10M"
)
(
    input        ref_clk,
    
    input        E_RXC,
    input        E_RXDV,
    input  [3:0] E_RXD,

    output       E_TXC,         //以太网发送时钟
    output       E_TXEN,        //发送使能
    output [3:0] E_TXD,         //发送数据

    output       phy_rx_clk,
    output       phy_rx_dv,
    output [7:0] phy_rx_data,
    output       phy_rx_toggle,
    
    input        phy_tx_clk,
    input [7:0]  phy_tx_data,
    input [1:0]  phy_tx_en,
    
    input        sys_clk,
    output       smi_done,
    output       e_mdc,
    input	     e_mdio_i,
    output       e_mdio_o,
    output       e_mdio_t,
    
    output [2:0] eth_speed,
    
    input        reset
);

ETH_PHY   #
(   
    .MAIN_CLK   (MAIN_CLK),  // 125m sys_clk
    .PHY_SPEED  (PHY_SPEED)  // "AUTO", "1G", "100M", "10M"
)
ETH_PHY_inst
(
    .ref_clk    (ref_clk),

    .E_RXC      (E_RXC),
    .E_RXDV     (E_RXDV),
    .E_RXD      (E_RXD),

    .E_TXC      (E_TXC),         //以太网发送时钟
    .E_TXEN     (E_TXEN),        //发送使能
    .E_TXD      (E_TXD),         //发送数据

    .phy_rx_clk (phy_rx_clk),
    .phy_rx_dv  (phy_rx_dv),
    .phy_rx_data(phy_rx_data),
    .phy_rx_toggle (phy_rx_toggle),

    .phy_tx_clk (phy_tx_clk),
    .phy_tx_data(phy_tx_data),
    .phy_tx_en  (phy_tx_en),

    .eth_speed  (eth_speed),
    .reset      (reset)
);


//=========================================================================
phy_detect #
(
    .MAIN_CLK (125)
)
phy_detect_inst
(
    .sys_clk    (sys_clk),
    .rx_clk     (phy_rx_clk),
    .eth_speed  (eth_speed),
    .sys_rst    (reset)
);
//=========================================================================
mdio_app #
(
    .MAIN_CLK   (MAIN_CLK), // 125m sys_clk
    .PHY_SPEED  (PHY_SPEED) // "AUTO", "1G", "100M", "10M"
)
mdio_app_inst
(
    .sys_clk    (sys_clk),
    
    .e_mdc      (e_mdc),
    .e_mdio_i   (e_mdio_i),
    .e_mdio_o   (e_mdio_o),
    .e_mdio_t   (e_mdio_t),

    .mdio_done  (smi_done),    
    .reset      (reset)
);
//=========================================================================
endmodule

 

 

2. phy_detect.v

phy_detect.v在整个网络综合实验工程中的位置如图2所示。速度检测模块主要原理是通过引入另外一个确定的时钟进行检测RXC。文章网速检测及Verilog实现技巧中有对phy_detetc.v模块进行详细说明,这里不再赘述。

 

图2 phy_detect.v 模块

 

3. ETH_PHY.v

ETH_PHY.v是与FII-PRX100-D FPGA板上以太网芯片RTL8211E PHY层交流(接收和发送)的模块,如图3所示。ETH_PHY下有接收和发送的selectIO IP核(这里没有直接使用selectIO IP核,而是使用IP核生成的Verilog代码),更多相关selectIO的信息参见文章Xilinx FPGA DDR 接口及Verilog实现。ETH_PHY.v代码如下:

 

 

图3 ETH_PHY.v模块

 

`timescale 1ns / 1ps



module ETH_PHY #
(
    parameter   PHY_IODELAY_GROUP = "ETH_PHY_group",
    parameter   MAIN_CLK = 125,
    parameter   PHY_SPEED = "AUTO"  // "AUTO", "1G", "100M", "10M"
)
(
    input        ref_clk,
    
    input        E_RXC,
    input        E_RXDV,
    input  [3:0] E_RXD,

    output       E_TXC,         //以太网发送时钟
    output       E_TXEN,        //发送使能
    output [3:0] E_TXD,         //发送数据
    
    output       phy_rx_clk,
    output reg   phy_rx_dv = 0,
    output reg [7:0] phy_rx_data = 0,
    output reg   phy_rx_toggle = 0,

    input        phy_tx_clk,
    input  [7:0] phy_tx_data,
    input  [1:0] phy_tx_en,
    
    input  [2:0] eth_speed,
    
    input        reset
);
    
    
//=========================================================================    
//接收端口
wire [1:0] eth_rxdv;
wire [7:0] eth_rx_d;

wire [9:0] rgmii_rx;    
assign {eth_rxdv[1],eth_rx_d[7:4], eth_rxdv[0],eth_rx_d[3:0]} = rgmii_rx;

ETH_RX_PHY   #( .PHY_IODELAY_GROUP (PHY_IODELAY_GROUP))
ETH_RX_PHY_inst
(
    .data_in_from_pins  ({E_RXDV,E_RXD}),   // input [4:0] data_in_from_pins
    .data_in_to_device  (rgmii_rx),         // output [9:0] data_in_to_device

    .delay_locked       (),                 // output delay_locked                      
    .ref_clock          (ref_clk),          // input ref_clock                         
    .clk_in             (E_RXC),            // input clk_in                            
    .clk_out            (phy_rx_clk),       // output clk_out
    .io_reset           (reset)              // input io_reset
); 
//======================= 区分不同的MII =========================================    
reg toggle = 0;
reg rx_st = 0;
always @ (posedge phy_rx_clk or posedge reset)
if(reset)
begin
    rx_st <= 0;
    toggle <= 0;
    phy_rx_toggle <= 0;
    phy_rx_dv <= 0;
end
else case (rx_st)
0:
begin
    phy_rx_toggle <= 0;
    phy_rx_dv <= 0;
    if(eth_speed == 1)
    toggle <= 1;
    else
    toggle <= 0;
    
    if(eth_rxdv == 2'b11)
    begin
        phy_rx_dv <= 1;

        if(eth_speed == 0 || eth_speed == 2 || eth_speed == 4) //1.25m, 12.5m, 125m
        begin
            phy_rx_toggle <= 1;
            phy_rx_data <= eth_rx_d;
        end
        
        if(eth_speed == 1 || eth_speed == 3) // 2.5m, 25m
        begin
            toggle <= ~toggle;

            phy_rx_data <= {eth_rx_d[3:0], phy_rx_data[7:4]};
            phy_rx_toggle <= toggle;
        end
        rx_st <= 1;
    end
end
1:
begin
    if(eth_rxdv == 2'b11)
    begin
        phy_rx_dv <= 1;
        if(eth_speed == 0 || eth_speed == 2 || eth_speed == 4) //1.25m, 12.5m, 125m
        begin
            phy_rx_toggle <= 1;
            phy_rx_data <= eth_rx_d;
        end
        
        if(eth_speed == 1 || eth_speed == 3) // 2.5m, 25m
        begin
            toggle <= ~toggle;

            phy_rx_data <= {eth_rx_d[3:0], phy_rx_data[7:4]};
            phy_rx_toggle <= toggle;
        end
    end
    else 
    begin
        phy_rx_dv <= 0;
        phy_rx_toggle <= 0;
        phy_rx_data <= eth_rx_d;
        rx_st <= 0;
    end
end    
default: rx_st <= 0;
endcase

//=========================================================================
//发送端口
reg [9:0] rgmii_tx = 0;
always @ (posedge phy_tx_clk)
begin
    if(eth_speed == 0 || eth_speed == 2 || eth_speed == 4) //1.25m, 12.5m, 125m ====> RGMII/RMII
    begin
        rgmii_tx <= {phy_tx_en[1], phy_tx_data[7:4], phy_tx_en[1], phy_tx_data[3:0]};
    end
    else   //MII
    begin
        if(phy_tx_en[0])
            rgmii_tx <= {phy_tx_en[1], phy_tx_data[3:0], phy_tx_en[1], phy_tx_data[3:0]};
        else
            rgmii_tx <= {phy_tx_en[1], phy_tx_data[7:4], phy_tx_en[1], phy_tx_data[7:4]};
    end
end


ETH_TX_PHY  # (.PHY_IODELAY_GROUP(PHY_IODELAY_GROUP))
ETH_TX_PHY_inst
(
    .data_out_from_device   (rgmii_tx),
    .data_out_to_pins       ({E_TXEN,E_TXD[3:0]}),
    .clk_in                 (phy_tx_clk),
    .clk_to_pins            (E_TXC),
    .ref_clk                (ref_clk),
    .io_reset               (reset)
);

//=========================================================================

endmodule

 

4. mdio_app.v,mdio_core.v,mdio_phy.v

mdio_app.v以及其子模块mdio_core.v和mdio_phy.v是通过MDC和MDIO管脚读写FPGA板上以太网芯片RTL8211E寄存器的模块,如图4所示。关于MDIO和MDC读写寄存器的更多信息,参见文章网络物理层芯片基本知识1–MDIO,以太网PHY 之 MDIO接口及应用。

 

图4 mdio_app.v模块

 

RTL8211E芯片的寄存器的地址分配如图5所示。

图5 RTL8211E寄存器

更多关于RTL8211E芯片寄存器定义的信息,参见文章RTL8211E 千兆网phy芯片。

 

Posted in FPGA 教材教案, Verilog, Verilog, Vivado, Vivado, 开发工具, 开发工具, 开发语言, 教材与教案, 文章, 编程语言

发表回复

相关链接