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芯片。