??在 MAC 與 PHY 之間,有一個配置接口,即 MDIO(也稱 SMI,Serial Management Interface),可以配置 PHY 的工作模式、獲取 PHY 芯片的工作狀態(tài)等。本文以 PHY 芯片 B50610 為例,實現(xiàn) MDIO 接口,以實現(xiàn)對傳輸速度、接口類型的自協(xié)商。
??MDIO 包含 2 根信號線:
- MDC,由 MAC 側提供給 PHY 的時鐘信號,最大 12.5MHz;
- MDIO,inout,數(shù)據(jù)線
??MDIO 的通信協(xié)議如下
MDIO 的幀構成如下:
- Preamble,32 位前導碼,MAC 端發(fā)送 32 位邏輯 1,以同步 PHY 芯片
- Start of Frame,幀開始信號,2’b01
- Operation Code,操作碼,2‘b01 表示進行寫操作,2’b10 表示讀操作
- PHY Address,5 位 PHY 地址,用于決定和哪個 PHY 芯片通信
- Register Address,5 位寄存器地址,最大可表示 32 個寄存器
- Turn Around,2 位轉向,在讀操作中,MDIO 在此時由 MAC 驅動改為 PHY 驅動,在第一個 TA 位,MDIO 引腳為高阻狀態(tài),第二個 TA 位,PHY 將 MDIO 引腳拉低,準備發(fā)送數(shù)據(jù),MAC 端此兩位設為高阻,若 MAC 檢測到第二位非低電平,表明對方無應答,可通過這個判斷是否讀取失?。辉趯懖僮髦?,不需要 MDIO 方向發(fā)生變化,MAC 固定輸出 2’b10
- Data,16bit 數(shù)據(jù)
- IDLE,空閑狀態(tài)下,一般將 MDIO 上拉
??MDIO 的時序如下
可以看到,PHY 芯片在 MDC 上升沿讀取數(shù)據(jù),并在上升沿給出數(shù)據(jù)。因此 MAC 端須在 MDC 下降沿給出數(shù)據(jù),而讀取數(shù)據(jù)在上升沿、下降沿均可(不過需要注意,由于 MDIO_DELAY 最大可達 50ns,若使用高于 10M 的 MDC 頻率,則下降沿讀取可能出現(xiàn)問題,因此推薦 MAC 側在 MDC 上升沿讀取數(shù)據(jù))。
??由于一次 MDIO 讀寫,均由 32bit 前導碼,以及 32bit 讀寫比特流組成,因此可以方便地使用一個 5bit 計數(shù)器進行計數(shù),然后根據(jù)當前的計數(shù)值操作 MDIO。Verilog 代碼如下
/*
* file : smi_top.v
* author : 今朝無言
* date : 2023-05-31
* version : v2.0
* description : SMI(MDIO)Read/Write
*/
module smi_top(
input clk,
input rst_n,
output mdc,
inout mdio,
input [4:0] phy_addr,
input [4:0] reg_addr,
input [15:0] wrdata,
input write_req,
//req 信號上升沿有效,應維持至少一個 mdc 周期,可在檢測到 busy 后再置低,下同
output reg [15:0] rddata,
input read_req,
output reg busy,
output reg rd_failed //對方無應答
);
parameter CLK_FREQ = 100_000_000;
parameter MDC_FREQ = 100_000;
clkdiv #(.N(CLK_FREQ/MDC_FREQ))
clkdiv_inst(
.clk_in (clk),
.clk_out (mdc)
);
reg mdio_buf;
reg link; //MAC是否占有MDIO控制權
assign mdio = link? mdio_buf : 1'bz;
//-----------------edge detect---------------------------
wire write_req_pe;
reg write_req_d0;
reg write_req_d1;
wire read_req_pe;
reg read_req_d0;
reg read_req_d1;
always @(posedge mdc) begin
write_req_d0 <= write_req;
write_req_d1 <= write_req_d0;
read_req_d0 <= read_req;
read_req_d1 <= read_req_d0;
end
assign write_req_pe = write_req_d0 & (~write_req_d1);
assign read_req_pe = read_req_d0 & (~read_req_d1);
//---------------------FSM-------------------------------
localparam S_IDLE = 8'h01;
localparam S_ARB = 8'h02;
localparam S_PRE_R = 8'h04;
localparam S_RD = 8'h08;
localparam S_PRE_W = 8'h10;
localparam S_WR = 8'h20;
localparam S_STOP = 8'h40;
localparam ST = 2'b01;
localparam R_OP = 2'b10;
localparam W_OP = 2'b01;
localparam W_TA = 2'b10;
reg [4:0] cnt; //一次讀寫正好 32bit Pre + 32bit WR/RD
reg [7:0] state = S_IDLE;
reg [7:0] next_state;
always @(posedge mdc or negedge rst_n) begin
if(~rst_n) begin
state <= S_IDLE;
end
else begin
state <= next_state;
end
end
always @(*) begin
case(state)
S_IDLE: begin
next_state <= S_ARB;
end
S_ARB: begin
if(write_req_pe) begin //寫優(yōu)先
next_state <= S_PRE_W;
end
else if(read_req_pe) begin
next_state <= S_PRE_R;
end
else begin
next_state <= S_ARB;
end
end
S_PRE_R: begin
if(cnt==5'd31) begin
next_state <= S_RD;
end
else begin
next_state <= S_PRE_R;
end
end
S_RD: begin
if(cnt==5'd31) begin
next_state <= S_STOP;
end
else begin
next_state <= S_RD;
end
end
S_PRE_W: begin
if(cnt==5'd31) begin
next_state <= S_WR;
end
else begin
next_state <= S_PRE_W;
end
end
S_WR: begin
if(cnt==5'd31) begin
next_state <= S_STOP;
end
else begin
next_state <= S_WR;
end
end
S_STOP: begin
next_state <= S_IDLE;
end
default: begin
next_state <= S_IDLE;
end
endcase
end
//cnt
always @(posedge mdc) begin
case(state)
S_IDLE: begin
cnt <= 5'd0;
end
S_PRE_R, S_RD, S_PRE_W, S_WR: begin
cnt <= cnt + 1'b1;
end
default: begin
cnt <= 5'd0;
end
endcase
end
//mdio_buf
always @(negedge mdc) begin //必須下降沿發(fā)送數(shù)據(jù)
case(state)
S_IDLE: begin
mdio_buf <= 1'b1;
end
S_PRE_R, S_PRE_W: begin
mdio_buf <= 1'b1;
end
S_WR: begin
case(cnt)
5'd0: mdio_buf <= ST[1];
5'd1: mdio_buf <= ST[0];
5'd2: mdio_buf <= W_OP[1];
5'd3: mdio_buf <= W_OP[0];
5'd4: mdio_buf <= phy_addr[4];
5'd5: mdio_buf <= phy_addr[3];
5'd6: mdio_buf <= phy_addr[2];
5'd7: mdio_buf <= phy_addr[1];
5'd8: mdio_buf <= phy_addr[0];
5'd9: mdio_buf <= reg_addr[4];
5'd10: mdio_buf <= reg_addr[3];
5'd11: mdio_buf <= reg_addr[2];
5'd12: mdio_buf <= reg_addr[1];
5'd13: mdio_buf <= reg_addr[0];
5'd14: mdio_buf <= W_TA[1];
5'd15: mdio_buf <= W_TA[0];
5'd16: mdio_buf <= wrdata[15];
5'd17: mdio_buf <= wrdata[14];
5'd18: mdio_buf <= wrdata[13];
5'd19: mdio_buf <= wrdata[12];
5'd20: mdio_buf <= wrdata[11];
5'd21: mdio_buf <= wrdata[10];
5'd22: mdio_buf <= wrdata[9];
5'd23: mdio_buf <= wrdata[8];
5'd24: mdio_buf <= wrdata[7];
5'd25: mdio_buf <= wrdata[6];
5'd26: mdio_buf <= wrdata[5];
5'd27: mdio_buf <= wrdata[4];
5'd28: mdio_buf <= wrdata[3];
5'd29: mdio_buf <= wrdata[2];
5'd30: mdio_buf <= wrdata[1];
5'd31: mdio_buf <= wrdata[0];
default: begin
mdio_buf <= 1'b1;
end
endcase
end
S_RD: begin
case(cnt)
5'd0: mdio_buf <= ST[1];
5'd1: mdio_buf <= ST[0];
5'd2: mdio_buf <= R_OP[1];
5'd3: mdio_buf <= R_OP[0];
5'd4: mdio_buf <= phy_addr[4];
5'd5: mdio_buf <= phy_addr[3];
5'd6: mdio_buf <= phy_addr[2];
5'd7: mdio_buf <= phy_addr[1];
5'd8: mdio_buf <= phy_addr[0];
5'd9: mdio_buf <= reg_addr[4];
5'd10: mdio_buf <= reg_addr[3];
5'd11: mdio_buf <= reg_addr[2];
5'd12: mdio_buf <= reg_addr[1];
5'd13: mdio_buf <= reg_addr[0];
default: begin
mdio_buf <= 1'b1;
end
endcase
end
default: begin
mdio_buf <= 1'b1;
end
endcase
end
//data_tmp
reg [15:0] data_tmp;
always @(posedge mdc) begin //在上升沿讀數(shù)據(jù)
case(state)
S_RD: begin
case(cnt)
5'd15: rd_failed <= mdio; //若對方應答,此處會被拉低,否則失敗
5'd16: data_tmp[15] <= mdio;
5'd17: data_tmp[14] <= mdio;
5'd18: data_tmp[13] <= mdio;
5'd19: data_tmp[12] <= mdio;
5'd20: data_tmp[11] <= mdio;
5'd21: data_tmp[10] <= mdio;
5'd22: data_tmp[9] <= mdio;
5'd23: data_tmp[8] <= mdio;
5'd24: data_tmp[7] <= mdio;
5'd25: data_tmp[6] <= mdio;
5'd26: data_tmp[5] <= mdio;
5'd27: data_tmp[4] <= mdio;
5'd28: data_tmp[3] <= mdio;
5'd29: data_tmp[2] <= mdio;
5'd30: data_tmp[1] <= mdio;
5'd31: data_tmp[0] <= mdio;
default: begin
data_tmp <= data_tmp;
end
endcase
end
default: begin
data_tmp <= data_tmp;
end
endcase
end
//rddata
always @(posedge mdc) begin
case(state)
S_STOP: begin
rddata <= data_tmp;
end
default: begin
rddata <= rddata;
end
endcase
end
//link
always @(posedge mdc) begin
case(state)
S_PRE_W, S_PRE_R, S_WR: begin
link <= 1'b1;
end
S_RD: begin
if(cnt<=13) begin
link <= 1'b1;
end
else begin
link <= 1'b0;
end
end
default: begin
link <= 1'b0;
end
endcase
end
//busy
always @(posedge mdc) begin
case(state)
S_IDLE, S_ARB: begin
busy <= 1'b0;
end
S_PRE_R, S_RD, S_PRE_W, S_WR, S_STOP: begin
busy <= 1'b1;
end
default: begin
busy <= busy;
end
endcase
end
endmodule
測試
??B50610 的 PHY 地址通過 PHYA0、TEST3、TEST2 三個引腳進行配置
比如我們想查看下網絡是否連接、當前操作速度等信息,可以查看 Auxiliary Status Summary Register 寄存器,地址 0x19。各比特含義如下
??測試代碼略.
??連接路由器(100BASE)后:
可以看到網絡已連接(bit2=1),自協(xié)商已完成(bit15=1),當前網速 100M 全雙工(bit10:8=0b101)。
拔掉網線:
文章來源:http://www.zghlxwxcb.cn/news/detail-561600.html
可以檢測到網絡斷開(bit2=0),正在自協(xié)商過程中(bit15=0)。文章來源地址http://www.zghlxwxcb.cn/news/detail-561600.html
到了這里,關于以太網——MDIO(SMI)接口的FPGA實現(xiàn)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!