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模块
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
`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模块
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
`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芯片。