MAC和PHY芯片有一个配置接口,即MDIO接口。可以配置PHY芯片的工作模式以及获取PHY芯片的状态信息。PHY芯片内部有一系列寄存器。用户通过配置寄存器来配置PHY芯片的工作模式。
FPGA通过MDIO接口对PHY芯片的内部寄存器进行配置。通常情况下芯片在默认情况下也可以工作,即配置芯片不是必须的。也可通过外接特殊引脚的方式来配置PHY芯片的工作模式。
MDIO接口也被称为SMI接口(Serial Management Interface,串行管理接口),包括ETH_MDC(数据管理时钟,最大不超过12.5MHZ)和ETH_MDIO(数据管理输入输出,双向数据线)两条信号线。
MDIO接口的读写通信协议如下图:
名称1 | 作用 |
---|---|
Preamble | 32位引导码,由MAC端发送32位逻辑1,用于同步PHY芯片 |
ST(Start of Frame) | 两位帧开始信号,用01表示 |
OP(Operation Code) | 两位操作码,读:10 , 写:01 |
PHYAD | 五位PHY地址,用于表示和那个PHY芯片通讯 |
REGAD(Register Address) | 五位寄存器地址,可以表示32位寄存器 |
TA(Turnaround) | 两位转向。在读命令中MDIO由MAC驱动改为PHY驱动。写命令中MAC固定输入01 |
data | 读取PHYAD寄存器中对应的数据或者写入数据。高位在前低位在后 |
IDLE | 空闲状态均为高阻态 |
`timescale 1ns / 1ps
module mdio_dri(
input wire clk ,
input wire rst_n ,
input wire op_exec , //触发开始信号
input wire op_rh_wl , //低电平写,高电平读
input wire [4:0] op_phy_addr , //芯片地址
input wire [4:0] op_reg_addr , //寄存器地址
input wire [15:0] op_wr_data , //写数据
output reg op_done , //操作完成
output reg [15:0] op_rd_data , //读出的数据
output reg op_rd_ack , //读应答
output reg eth_mdc ,
inout wire eth_mdio
);
localparam SYS_CLK = 'd50_000_000 ;
localparam DRI_CLK = 'd12_500_000 ;
localparam DIV_CNT_MAX = (SYS_CLK/DRI_CLK >> 1) - 1 ;
localparam IDLE = 6'b000_001; //初始状态
localparam PRE = 6'b000_010; //前导码 32位1
localparam START = 6'b000_100; //发送帧开始加操作码
localparam ADDR = 6'b001_000; //发送PHY地址加寄存器地址
localparam WR = 6'b010_000; //发送TA加写入数据
localparam RD = 6'b100_000; //发送TA加接收数据
localparam Pre = 32'b1111_1111_1111_1111 ; //前导码
localparam ST = 2'b01 ; //帧开始
wire mdio_in ; //mdio数据输入
reg st_done ; //操作完成
reg [5:0] state ; //状态机
reg op_rh_wl_r ;
reg [5:0] op_phy_addr_r;
reg [5:0] op_reg_addr_r;
reg [15:0] op_wr_data_r ;
reg mdio_out ; //mdio数据输出
reg [9:0] clk_cnt ; //时钟计数器
reg mdio_dir ; //mdio数据方向指示 0输入 1输出
reg [1:0] op_code ;
/********************ila模块*****************************/
wire [255:0] probe0;
assign probe0 = { eth_mdc,
mdio_out,
mdio_in,
mdio_dir,
state,
clk_cnt,
op_code,
op_rd_data,
op_rh_wl,
op_phy_addr,
op_wr_data,
st_done,
op_reg_addr,
op_rd_ack
};
ila_0 ila_0_inst (
.clk(clk), // input wire clk
.probe0(probe0) // input wire [255:0] probe0
);
/********************************************************/
//双向IO
assign eth_mdio = mdio_dir ? mdio_out : 1'bz;
assign mdio_in = eth_mdio;
//eth_mdc 12.5MHZ 1 3时钟变化
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
eth_mdc <= 'd1;
end
else if (state != IDLE && clk_cnt[0] == 1'b0) begin
eth_mdc <= ~eth_mdc;
end
end
//寄存器 当传输开始时将数据锁存起来
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
op_code <= 'd0;
op_phy_addr_r <= 'd0;
op_reg_addr_r <= 'd0;
op_wr_data_r <= 'd0;
end
else if (op_exec == 1'b1) begin
op_code <= {op_rh_wl,~op_rh_wl};//OP_CODE: 2'b01(写) 2'b10(读)
op_phy_addr_r <= op_phy_addr ;
op_reg_addr_r <= op_reg_addr;
op_wr_data_r <= op_wr_data ;
end
end
//状态转移
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
state <= IDLE ;
end
else begin
case(state)
IDLE : begin
if (op_exec == 1'b1) begin
state <= PRE ;
end
end
PRE : begin
if (st_done == 1'b1) begin
state <= START ;
end
end
START : begin
if (st_done == 1'b1) begin
state <= ADDR ;
end
end
ADDR : begin
if (st_done == 1'b1 && op_code[1] == 1'b0) begin
state <= WR ;
end
else if (st_done == 1'b1 && op_code[1] == 1'b1) begin
state <= RD ;
end
end
WR : begin
if (st_done == 1'b1) begin
state <= IDLE ;
end
end
RD : begin
if (st_done == 1'b1) begin
state <= IDLE ;
end
end
endcase
end
end
//状态输出
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
clk_cnt <= 'd0;
mdio_out <= 1'b0;
mdio_dir <= 1'b0;
op_done <= 1'b0;
st_done <= 1'b0;
op_rd_data <= 16'b0;
end
else begin
clk_cnt <= clk_cnt + 1'b1;
op_done <= 1'b0;
case(state)
IDLE : begin
clk_cnt <= 'd0;
mdio_dir <= 'd0;
mdio_out <= 'd1;
op_rd_ack <= 1'b1;
op_done <= 1'b0;
end
PRE : begin //前导码 发送32位1
mdio_dir <= 1'b1;
mdio_out <= 1'b1;
if (clk_cnt == 4 * 32 - 2) begin
st_done <= 1'b1;
end
else if (clk_cnt == 4 * 32 - 1) begin
st_done <= 1'b0;
clk_cnt <= 'd0;
end
end
START : begin //帧开始加操作码
mdio_dir <= 1'b1;
case(clk_cnt)
0 : mdio_out <= 1'b0 ;
4 : mdio_out <= 1'b1 ; //两位帧开始
8 : mdio_out <= op_code[1] ;
12 : mdio_out <= op_code[0] ;
14 : st_done <= 1'b1 ;
15 : begin
st_done <= 1'b0 ;
clk_cnt <= 'd0 ;
end
endcase
end
ADDR : begin
mdio_dir <= 1'b1;
case(clk_cnt)
0 : mdio_out <= op_phy_addr_r[4] ;
4 : mdio_out <= op_phy_addr_r[3] ;
8 : mdio_out <= op_phy_addr_r[2] ;
12 : mdio_out <= op_phy_addr_r[1] ;
16 : mdio_out <= op_phy_addr_r[0] ; //PHY地址
20 : mdio_out <= op_reg_addr_r[4] ;
24 : mdio_out <= op_reg_addr_r[3] ;
28 : mdio_out <= op_reg_addr_r[2] ;
32 : mdio_out <= op_reg_addr_r[1] ;
36 : mdio_out <= op_reg_addr_r[0] ; //寄存器地址
38 : st_done <= 1'b1 ;
39 : begin
st_done <= 1'b0;
clk_cnt <= 'b0;
end
endcase
end
WR : begin
mdio_dir <= 1'b1;
case(clk_cnt)
0 : mdio_out <= 1'b1 ;
4 : mdio_out <= 1'b0 ; //写操作 不转向 10
8 : mdio_out <= op_wr_data_r[15] ;
12 : mdio_out <= op_wr_data_r[14] ;
16 : mdio_out <= op_wr_data_r[13] ;
20 : mdio_out <= op_wr_data_r[12] ;
24 : mdio_out <= op_wr_data_r[11] ;
28 : mdio_out <= op_wr_data_r[10] ;
32 : mdio_out <= op_wr_data_r[9] ;
36 : mdio_out <= op_wr_data_r[8] ;
40 : mdio_out <= op_wr_data_r[7] ;
44 : mdio_out <= op_wr_data_r[6] ;
48 : mdio_out <= op_wr_data_r[5] ;
52 : mdio_out <= op_wr_data_r[4] ;
56 : mdio_out <= op_wr_data_r[3] ;
60 : mdio_out <= op_wr_data_r[2] ;
64 : mdio_out <= op_wr_data_r[1] ;
68 : mdio_out <= op_wr_data_r[0] ;
70 : st_done <= 1'b1 ;
71 : begin
st_done <= 1'b0 ;
clk_cnt <= 'd0 ;
mdio_dir <= 1'b0 ;
op_done <= 1'b1 ;
mdio_out <= 1'b1 ;
end
endcase
end
RD : begin
mdio_dir <= 1'b0;
case(clk_cnt)
2 : ; //等待转向
6 : op_rd_ack <= mdio_in ; //转向完成 应答信号拉低代表应答成功
10 : op_rd_data[15] <= mdio_in ;
14 : op_rd_data[14] <= mdio_in ;
18 : op_rd_data[13] <= mdio_in ;
22 : op_rd_data[12] <= mdio_in ;
26 : op_rd_data[11] <= mdio_in ;
30 : op_rd_data[10] <= mdio_in ;
34 : op_rd_data[9] <= mdio_in ;
38 : op_rd_data[8] <= mdio_in ;
42 : op_rd_data[7] <= mdio_in ;
46 : op_rd_data[6] <= mdio_in ;
50 : op_rd_data[5] <= mdio_in ;
54 : op_rd_data[4] <= mdio_in ;
58 : op_rd_data[3] <= mdio_in ;
62 : op_rd_data[2] <= mdio_in ;
66 : op_rd_data[1] <= mdio_in ;
70 : op_rd_data[0] <= mdio_in ;
72 : st_done <= 1'b1 ;
73 : begin
st_done <= 1'b0 ;
clk_cnt <= 'd0 ;
mdio_dir <= 'd0 ; //高阻
mdio_out <= 'd1 ;
op_done <= 1'b1 ;
end
endcase
end
endcase
end
end
endmodule