一、88E1512分析
? ? ? ? 本文不對88E1512進行詳細解析,僅對調試過程中重點使用的幾個寄存器進行說明。
1.1 MDIO時序分析
????????根據手冊,MDIO時序中,mdc時鐘最高為12Mhz。占空比和建立保持時間要求可以觀察上述表格。
? ? ? ? MDIO的讀數據時序圖如下:
? ? ? ? MDIO的寫數據時序圖如下:
????????各字段表示的含義如下:
1.2? 寄存器分析
1.2.1 頁地址寄存器
? ? ? ? 可能是受MDIO寄存器地址字段的位寬限制(5bits),88E1512提供了一個頁地址寄存器的功能,可以通過配置任意頁的寄存器地址22,實現頁的切換,比如從page0,切換到page1,實現遠超2^5的寄存器數目的配置。
? ? ? ? 通過配置此寄存器,可以完成頁的切換,實現不同寄存器的讀寫。
1.2.2? PHY ID寄存器
????????用于驗證MDIO總線是否正常,讀取page0的寄存器2,若回讀數據時0x141,則證明鏈路正確。(通過也需要確認MDIO時序中的TA字段,是否應答正確)
1.2.3 MAC特定控制寄存器
????????本寄存器中,重點關注bit4字段的信息(數據發送時序的控制,有時可能也涉及到bit5字段,數據接收時序的控制),一般FPGA中的MAC會默認增加網絡時鐘和網絡數據的延時,因此88E1512中可以取消其內部的延遲。這個需要根據實際情況進行控制。
? ? ? ? 以下重點看一下數據發送的時序圖。
????????當bit4字段設置為0時,可以從下面的時序圖中看到,此時要求88E1512的引腳上,接收到的時鐘和數據的時序關系應該為:在時鐘的邊沿位置,剛好為數據的穩定位置。即此時FPGA側若進行了時序上的相位處理,那么88E1512則不需要進行內部的延時,就可以達到時序收斂,穩定的接收到發送數據。
????????當bit4字段設置為1時,可以從下面的時序圖中看到,此時要求88E1512的引腳上,接收到的時鐘和數據的時序關系應該為:在時鐘的邊沿位置,剛好為數據的改變位置。即此時FPGA側若不進行了時序上的相位處理,時鐘和數據為相位對齊關系,那么88E1512則需要進行內部的延時,才可以達到時序收斂,穩定的接收到發送數據。
1.2.4 一般控制器1
????????從此寄存器中,可以看到,對于88E1512芯片而言,其默認的工作狀態為mode[2:0] = 3'b111,保留狀態。而我本次需要的工作狀態為RGMII to copper,因此需要進行配置使MODE[2:0]= 3'b000,
????????另外需要注意的是,配置完page6和page18的寄存器時,都需要對芯片進行軟復位,即bit15的SC字段,需要設置為1‘b1,88E1512會自動釋放復位信號。
二、FPGA的代碼實現
2.1 MDIO的時序模塊實現
? ? ? ? 此處也不啰嗦,直接上代碼,有興趣的自行查閱。
`timescale 1ns / 1ps
//
//實現MDC和MDIO時序的SMI接口module phy_smi #(parameter CLK_FREQ = 100_000_000,//系統時鐘頻率 100Mparameter MDC_DIV = 1000 //MDC分頻系數 1000 得到100K時鐘
)
(input clk,input rst_n,output reg mdc,inout mdio,input [4:0] phy_addr, //PHY芯片的地址input [4:0] reg_addr, //寄存器地址//wr_req信號上升沿有效input wr_req,input [15:0] wr_data,//rd_req信號上升沿有效input rd_req,output reg [15:0] rd_data = 16'd0,output reg rd_valid = 1'b0, //接收到的數據有效標志output reg rd_error = 1'b0, //接收數據失敗標志output busy );reg mdc_gen_en = 1'b0; //時鐘生成使能,mdc可以不是持續性時鐘
reg [15:0] mdc_div_cnt = 0;
wire mdc_pos;//MDC上升沿
wire mdc_neg; //MDC下降沿
wire mdc_hig; //MDC高電平中心位置
wire mdc_low;//MDC低電平中心位置reg mdio_buf = 1'b0;
reg mdio_dir = 1'b0;//FPGA是否輸出mdio,1'b1表示FPGA控制輸出,反之為高阻態//-----------------edge detect---------------------------
reg wr_req_rise = 1'b0;
reg wr_req_d0 = 1'b0;
reg wr_req_d1 = 1'b0;reg rd_req_rise = 1'b0;
reg rd_req_d0 = 1'b0;
reg rd_req_d1 = 1'b0;//---------------------FSM-------------------------------
localparam [3:0] S_IDLE = 4'd0;//空閑態
localparam [3:0] S_PRE_RD = 4'd1;//前導態
localparam [3:0] S_RD = 4'd2;//讀狀態
localparam [3:0] S_PRE_WR = 4'd3;//前導態
localparam [3:0] S_WR = 4'd4;//寫狀態
localparam [3:0] S_DONE = 4'd5;//完成態reg [3:0] state = S_IDLE;localparam START_FIELD = 2'b01; //開始2bits信息,指示開始傳輸數據
localparam READ_CODE_FIELD = 2'b10; //讀操作碼
localparam WRITE_CODE_FIELD = 2'b01; //寫操作碼
localparam TA_FIELD = 2'b10; //turn around。寫時,直接發送2'b10即可,讀操作時,MDIO控制權交給PHY,此時FPGA的MDIO為高阻reg [4:0] bit_cnt;//一次讀寫正好 32bit Pre + 32bit WR/RDassign mdc_low = (mdc_div_cnt == 16'd0);
assign mdc_hig = (mdc_div_cnt == (MDC_DIV/2) -1);
assign mdc_pos = (mdc_div_cnt == (MDC_DIV/4) -1);
assign mdc_neg = (mdc_div_cnt == (MDC_DIV/2) + (MDC_DIV/4) -1);always @(posedge clk) beginwr_req_d0 <= wr_req;wr_req_d1 <= wr_req_d0;wr_req_rise <= ~wr_req_d1 & wr_req_d0;rd_req_d0 <= rd_req;rd_req_d1 <= rd_req_d0;rd_req_rise <= ~rd_req_d1 & rd_req_d0;
endalways @(posedge clk)
beginif(mdc_gen_en) beginif( mdc_div_cnt <= MDC_DIV - 1)mdc_div_cnt <= mdc_div_cnt + 1'b1;elsemdc_div_cnt <= 0;endelsemdc_div_cnt <= 0;
endalways @(posedge clk)
beginif(mdc_gen_en) beginif( mdc_pos )mdc <= 1'b1;else if(mdc_neg)mdc <= 1'b0;endelsemdc <= 0;
endalways @(posedge clk or negedge rst_n)
beginif(~rst_n) beginstate <= S_IDLE;mdc_gen_en <= 1'b0;rd_valid <= 1'b0;endelse beginrd_valid <= 1'b0;case(state)S_IDLE : beginmdc_gen_en <= 1'b0;bit_cnt <= 0;rd_error <= 1'b0;if(rd_req_rise)state <= S_PRE_RD;else if(wr_req_rise)state <= S_PRE_WR; endS_PRE_RD : beginmdc_gen_en <= 1'b1;if(mdc_neg) beginif(bit_cnt==5'd31) begin //發送完32個前導1'b1bit_cnt <= 0;state <= S_RD;endelse beginbit_cnt <= bit_cnt + 1'b1;endend//前導碼均為1,FPGA輸出mdio_buf <= 1'b1;mdio_dir <= 1'b1;endS_RD : beginmdc_gen_en <= 1'b1;if(mdc_neg) beginif(bit_cnt==5'd31) begin //完成32bits的通信bit_cnt <= 0;state <= S_DONE;endelse beginbit_cnt <= bit_cnt + 1'b1;endendcase(bit_cnt)5'd0: begin mdio_dir <= 1'b1; mdio_buf <= START_FIELD[1];end5'd1: begin mdio_dir <= 1'b1; mdio_buf <= START_FIELD[0];end5'd2: begin mdio_dir <= 1'b1;mdio_buf <= READ_CODE_FIELD[1];end5'd3: begin mdio_dir <= 1'b1;mdio_buf <= READ_CODE_FIELD[0];end5'd4: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[4];end5'd5: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[3];end5'd6: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[2];end5'd7: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[1];end5'd8: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[0];end5'd9: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[4];end5'd10: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[3];end5'd11: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[2];end5'd12: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[1];end5'd13: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[0];enddefault: begin mdio_dir <= 1'b0;end endcase//MDC下降沿時取數據if(mdc_neg) begincase(bit_cnt)5'd14: rd_error <= mdio; //若PHY應答,此處會被拉低,否則失敗;5'd15: rd_data[15] <= mdio;5'd16: rd_data[14] <= mdio;5'd17: rd_data[13] <= mdio;5'd18: rd_data[12] <= mdio;5'd19: rd_data[11] <= mdio;5'd20: rd_data[10] <= mdio;5'd21: rd_data[9] <= mdio;5'd22: rd_data[8] <= mdio;5'd23: rd_data[7] <= mdio;5'd24: rd_data[6] <= mdio;5'd25: rd_data[5] <= mdio;5'd26: rd_data[4] <= mdio;5'd27: rd_data[3] <= mdio;5'd28: rd_data[2] <= mdio;5'd29: rd_data[1] <= mdio;5'd30:begin rd_data[0] <= mdio; rd_valid <= 1'b1;enddefault:;endcaseendendS_PRE_WR : beginmdc_gen_en <= 1'b1;if(mdc_neg) beginif(bit_cnt==5'd31) begin //發送完32個前導1'b1bit_cnt <= 0;state <= S_WR;endelse beginbit_cnt <= bit_cnt + 1'b1;endend//前導碼均為1,FPGA輸出mdio_buf <= 1'b1;mdio_dir <= 1'b1;endS_WR : beginmdc_gen_en <= 1'b1;if(mdc_neg) beginif(bit_cnt==5'd31) begin //完成32bits的通信bit_cnt <= 0;state <= S_DONE;endelse beginbit_cnt <= bit_cnt + 1'b1;endendcase(bit_cnt)5'd0: begin mdio_dir <= 1'b1; mdio_buf <= START_FIELD[1];end5'd1: begin mdio_dir <= 1'b1; mdio_buf <= START_FIELD[0];end5'd2: begin mdio_dir <= 1'b1;mdio_buf <= WRITE_CODE_FIELD[1];end5'd3: begin mdio_dir <= 1'b1;mdio_buf <= WRITE_CODE_FIELD[0];end5'd4: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[4];end5'd5: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[3];end5'd6: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[2];end5'd7: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[1];end5'd8: begin mdio_dir <= 1'b1;mdio_buf <= phy_addr[0];end5'd9: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[4];end5'd10: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[3];end5'd11: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[2];end5'd12: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[1];end5'd13: begin mdio_dir <= 1'b1;mdio_buf <= reg_addr[0];end5'd14: begin mdio_dir <= 1'b1;mdio_buf <= TA_FIELD[1];end5'd15: begin mdio_dir <= 1'b1;mdio_buf <= TA_FIELD[0];end5'd16: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[15];end5'd17: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[14];end5'd18: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[13];end5'd19: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[12];end5'd20: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[11];end5'd21: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[10];end5'd22: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[9];end5'd23: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[8];end5'd24: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[7];end5'd25: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[6];end5'd26: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[5];end5'd27: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[4];end5'd28: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[3];end5'd29: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[2];end5'd30: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[1];end5'd31: begin mdio_dir <= 1'b1; mdio_buf <= wr_data[0];enddefault:;endcaseendS_DONE : beginmdc_gen_en <= 1'b0;state <= S_IDLE;endendcaseend
endassign busy = (state != S_IDLE);assign mdio = mdio_dir ? mdio_buf : 1'bZ;//wire mdio_in;
//assign mdio_in = mdio;endmodule
2.2 88E1512的控制代碼
`timescale 1ns / 1psmodule PHY_88E1512(input clk,input rst_n,output mdc, //inout mdio, //input config_start, //啟動88E1512的配置,使FPGA可在RGMII 1G模式下通信output reg link_ok = 1'b0, //讀取PHY ID驗證鏈路是否正確output reg link_error = 1'b0, //讀取PHY ID驗證鏈路是否正確output reg config_done //配置完成);localparam PHY_88E1512_ADDR = 5'd0;localparam IDLE_STATE = 4'd0;
localparam ARB_STATE = 4'd1;
localparam LINK_READY_STATE = 4'd2; //讀取PHY ID :bit3:18 數值為0x0141即通信鏈路正常
localparam CFG_READY_STATE = 4'd3;
localparam WR_OP_STATE = 4'd4;
localparam RD_OP_STATE = 4'd5;
localparam LINK_ERR_STATE = 4'd6;
localparam DONE_STATE = 4'd7;reg [3:0] state = IDLE_STATE;reg config_start_d1 = 1'b0;reg [3:0] link_cnt = 4'd0;//鏈路驗證時寄存器計數
reg [3:0] wr_cnt = 4'd0; //寫讀寄存器計數
reg [15:0] rd_data_latch = 0;//SMI接口信號
//reg [4:0] phy_addr = 5'd0; //PHY芯片的地址
reg [4:0] reg_addr = 5'd0; //寄存器地址//wr_req信號上升沿有效
reg wr_req = 1'b0;
reg [15:0] wr_data = 16'd0;//rd_req信號上升沿有效
reg rd_req = 1'b0;
wire [15:0]rd_data;
wire rd_valid; //接收到的數據有效標志
wire rd_error; //接收數據失敗標志wire busy ;
reg busy_d1 = 1'b0;always@(posedge clk)
beginbusy_d1 <= busy;
endalways@(posedge clk)
beginconfig_start_d1 <= config_start;
endalways@(posedge clk or negedge rst_n)
beginif(!rst_n) beginstate <= IDLE_STATE;wr_req <= 1'b0;wr_cnt <= 0;rd_req <= 1'b0;link_cnt <= 0;rd_data_latch <= 16'h0;end else begincase(state)IDLE_STATE: beginwr_req <= 1'b0;wr_cnt <= 0;rd_req <= 1'b0;link_cnt <= 0;if(!config_start_d1 & config_start) beginstate <= ARB_STATE;rd_data_latch <= 16'h0;link_ok <= 1'b0;link_error <= 1'b0;config_done <= 1'b0;endendARB_STATE : beginwr_req <= 1'b0;rd_req <= 1'b0;if(link_ok == 1'b0) //先進行鏈路驗證state <= LINK_READY_STATE;else if(config_done == 1'b0) //再配置寄存器state <= CFG_READY_STATE;elsestate <= IDLE_STATE;endLINK_READY_STATE : begincase(link_cnt)4'd0: begin //首先寫寄存器,選中page0link_cnt <= 4'd1;reg_addr <= 5'd22; //寄存器地址22wr_data <= 16'd0;state <= WR_OP_STATE;end4'd1: begin //其次讀取PHY IDlink_cnt <= 4'd2;reg_addr <= 5'd2; //寄存器地址2state <= RD_OP_STATE;end4'd2: begin //驗證回讀ID是否正確if(rd_data_latch == 16'h0141) beginstate <= ARB_STATE;link_ok <= 1'b1;endelse beginstate <= LINK_ERR_STATE;endenddefault:state <= IDLE_STATE;endcaseendCFG_READY_STATE : beginwr_cnt <= wr_cnt + 4'd1;case(wr_cnt)//-----由于FPGA MAC核已經調整好時序,因此PHY處不需要進行時序延時4'd0: begin //首先寫寄存器,選中page2reg_addr <= 5'd22; //寄存器地址22wr_data <= 16'd2;state <= WR_OP_STATE;end4'd1: begin //讀寄存器reg_addr <= 5'd21; //寄存器地址21state <= RD_OP_STATE;end 4'd2: begin //取消PHY內部的延時reg_addr <= 5'd21; //寄存器地址21wr_data <= rd_data_latch & 16'hFFEF;//bit4置為0state <= WR_OP_STATE;end //-----PHY工作在RGMII to Copper模式4'd3: begin //首先寫寄存器,選中page18reg_addr <= 5'd22; //寄存器地址22wr_data <= 16'd18;state <= WR_OP_STATE;end4'd4: begin //其次配置MODE參數,使PHY工作在RGMII to Copper模式reg_addr <= 5'd20; //寄存器地址20wr_data <= 16'h0;//bit15表示軟復位,bit[2:0]表示RGMII to Copper模式,其余為默認值state <= WR_OP_STATE;end4'd5: begin //其次配置MODE參數,使PHY工作在RGMII to Copper模式reg_addr <= 5'd20; //寄存器地址20wr_data <= 16'h8000;//bit15表示軟復位,bit[2:0]表示RGMII to Copper模式,其余為默認值state <= WR_OP_STATE;config_done <= 1'b1;enddefault:state <= IDLE_STATE;endcaseendWR_OP_STATE : beginwr_req <= 1'b1;if(busy_d1 & !busy) //busy下降沿state <= ARB_STATE;endRD_OP_STATE : begin rd_req <= 1'b1;if(rd_valid) beginrd_data_latch <= rd_data;endif(rd_error)state <= LINK_ERR_STATE;else if(busy_d1 & !busy) //busy下降沿state <= ARB_STATE;endLINK_ERR_STATE : beginlink_error <= 1'b1;state <= IDLE_STATE;endDONE_STATE : beginstate <= IDLE_STATE;enddefault : beginstate <= IDLE_STATE;endendcaseend
endphy_smi #(.CLK_FREQ(25_000_000),//系統時鐘頻率 25M.MDC_DIV(50) //MDC分頻系數 50 得到500K時鐘
)
phy_smi(.clk(clk),//input clk,.rst_n(rst_n),//input rst_n,.mdc(mdc),//output reg mdc,.mdio(mdio),//inout mdio,.phy_addr(PHY_88E1512_ADDR),//input [4:0] phy_addr, //PHY芯片的地址.reg_addr(reg_addr),//input [4:0] reg_addr, //寄存器地址//wr_req信號上升沿有效.wr_req(wr_req),//input wr_req,.wr_data(wr_data),//input [15:0] wr_data,//rd_req信號上升沿有效.rd_req(rd_req),//input rd_req,.rd_data(rd_data),//output reg [15:0] rd_data = 16'd0,.rd_valid(rd_valid),//output reg rd_valid = 1'b0, //接收到的數據有效標志.rd_error(rd_error),//output reg rd_error = 1'b0, //接收數據失敗標志.busy(busy) //output busy );
endmodule
2.3 整體網絡通信的驗證
????????網絡通信模塊,借用了米聯客的UDP通信模塊。基本上根據根據上述代碼部分,應該可以自行完成通信調試,若需要原始工程的,可以自行下載使用。
? ? ?https://download.csdn.net/download/yindq1220/91200585
電腦側IP地址 | 192.168.137.1 |
電腦側UDP端口號 | 6001 |
開發板側IP地址 | 192.168.137.2 |
開發板側UDP端口號 | 6002 |