> 技术文档 > FPGA控制88E1512 PHY芯片完成网络通信_88e1512芯片手册

FPGA控制88E1512 PHY芯片完成网络通信_88e1512芯片手册


一、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 #( parameterCLK_FREQ = 100_000_000,//系统时钟频率 100M parameterMDC_DIV= 1000 //MDC分频系数 1000 得到100K时钟)( inputclk, inputrst_n, outputregmdc, inoutmdio, input[4:0]phy_addr, //PHY芯片的地址 input[4:0]reg_addr, //寄存器地址 //wr_req信号上升沿有效 inputwr_req, input[15:0]wr_data, //rd_req信号上升沿有效 inputrd_req, outputreg[15:0]rd_data = 16\'d0, outputregrd_valid = 1\'b0,//接收到的数据有效标志 outputregrd_error = 1\'b0,//接收数据失败标志 outputbusy );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低电平中心位置regmdio_buf = 1\'b0;regmdio_dir = 1\'b0;//FPGA是否输出mdio,1\'b1表示FPGA控制输出,反之为高阻态//-----------------edge detect---------------------------regwr_req_rise = 1\'b0;regwr_req_d0 = 1\'b0;regwr_req_d1 = 1\'b0;regrd_req_rise = 1\'b0;regrd_req_d0 = 1\'b0;regrd_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;localparamSTART_FIELD= 2\'b01; //开始2bits信息,指示开始传输数据localparamREAD_CODE_FIELD = 2\'b10; //读操作码localparamWRITE_CODE_FIELD = 2\'b01; //写操作码localparamTA_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) begin if(mdc_gen_en) begin if( mdc_div_cnt <= MDC_DIV - 1) mdc_div_cnt <= mdc_div_cnt + 1\'b1; else mdc_div_cnt <= 0; end else mdc_div_cnt <= 0;endalways @(posedge clk) begin if(mdc_gen_en) begin if( mdc_pos ) mdc <= 1\'b1; else if(mdc_neg) mdc <= 1\'b0; end else mdc <= 0;endalways @(posedge clk or negedge rst_n) begin if(~rst_n) begin state<= S_IDLE; mdc_gen_en <= 1\'b0; rd_valid <= 1\'b0;endelse begin rd_valid <= 1\'b0; case(state) S_IDLE : begin mdc_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;  end S_PRE_RD : begin mdc_gen_en <= 1\'b1; if(mdc_neg) begin  if(bit_cnt==5\'d31) begin //发送完32个前导1\'b1 bit_cnt <= 0; state<= S_RD;  end  else begin bit_cnt <= bit_cnt + 1\'b1;  end end //前导码均为1,FPGA输出 mdio_buf <= 1\'b1; mdio_dir <= 1\'b1; end S_RD : begin mdc_gen_en <= 1\'b1; if(mdc_neg) begin  if(bit_cnt==5\'d31) begin //完成32bits的通信 bit_cnt <= 0; state<= S_DONE;  end  else begin bit_cnt <= bit_cnt + 1\'b1;  end end case(bit_cnt)  5\'d0: begin mdio_dir <= 1\'b1; mdio_buf<= START_FIELD[1];end  5\'d1: begin mdio_dir <= 1\'b1; mdio_buf<= START_FIELD[0];end  5\'d2: begin mdio_dir <= 1\'b1;mdio_buf<= READ_CODE_FIELD[1];end  5\'d3: begin mdio_dir <= 1\'b1;mdio_buf<= READ_CODE_FIELD[0];end  5\'d4: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[4];end  5\'d5: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[3];end  5\'d6: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[2];end  5\'d7: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[1];end  5\'d8: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[0];end  5\'d9: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[4];end  5\'d10: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[3];end  5\'d11: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[2];end  5\'d12: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[1];end  5\'d13: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[0];end  default: begin mdio_dir <= 1\'b0;end  endcase //MDC下降沿时取数据 if(mdc_neg) begin  case(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;end default:;  endcase end end S_PRE_WR : begin mdc_gen_en <= 1\'b1; if(mdc_neg) begin  if(bit_cnt==5\'d31) begin //发送完32个前导1\'b1 bit_cnt <= 0; state<= S_WR;  end  else begin bit_cnt <= bit_cnt + 1\'b1;  end end //前导码均为1,FPGA输出 mdio_buf <= 1\'b1; mdio_dir <= 1\'b1; end S_WR : begin mdc_gen_en <= 1\'b1; if(mdc_neg) begin  if(bit_cnt==5\'d31) begin //完成32bits的通信 bit_cnt <= 0; state<= S_DONE;  end  else begin bit_cnt <= bit_cnt + 1\'b1;  end end case(bit_cnt)  5\'d0: begin mdio_dir <= 1\'b1; mdio_buf<= START_FIELD[1];end  5\'d1: begin mdio_dir <= 1\'b1; mdio_buf<= START_FIELD[0];end  5\'d2: begin mdio_dir <= 1\'b1;mdio_buf<= WRITE_CODE_FIELD[1];end  5\'d3: begin mdio_dir <= 1\'b1;mdio_buf<= WRITE_CODE_FIELD[0];end  5\'d4: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[4];end  5\'d5: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[3];end  5\'d6: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[2];end  5\'d7: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[1];end  5\'d8: begin mdio_dir <= 1\'b1;mdio_buf<= phy_addr[0];end  5\'d9: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[4];end  5\'d10: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[3];end  5\'d11: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[2];end  5\'d12: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[1];end  5\'d13: begin mdio_dir <= 1\'b1;mdio_buf<= reg_addr[0];end  5\'d14: begin mdio_dir <= 1\'b1;mdio_buf<= TA_FIELD[1];end  5\'d15: begin mdio_dir <= 1\'b1;mdio_buf<= TA_FIELD[0];end  5\'d16: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[15];end  5\'d17: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[14];end  5\'d18: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[13];end  5\'d19: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[12];end  5\'d20: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[11];end  5\'d21: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[10];end  5\'d22: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[9];end  5\'d23: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[8];end  5\'d24: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[7];end  5\'d25: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[6];end  5\'d26: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[5];end  5\'d27: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[4];end  5\'d28: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[3];end  5\'d29: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[2];end  5\'d30: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[1];end  5\'d31: begin mdio_dir <= 1\'b1; mdio_buf<= wr_data[0];end  default:; endcase end S_DONE : begin mdc_gen_en <= 1\'b0; state<= S_IDLE; end endcase endendassign 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)begin busy_d1 <= busy;endalways@(posedge clk)begin config_start_d1 <= config_start;endalways@(posedge clk or negedge rst_n)begin if(!rst_n) begin state <= IDLE_STATE; wr_req <= 1\'b0; wr_cnt <= 0; rd_req <= 1\'b0; link_cnt <= 0; rd_data_latch <= 16\'h0; end else begin case(state) IDLE_STATE: begin wr_req <= 1\'b0; wr_cnt <= 0; rd_req <= 1\'b0; link_cnt <= 0; if(!config_start_d1 & config_start) begin  state <= ARB_STATE;  rd_data_latch <= 16\'h0;  link_ok <= 1\'b0;  link_error <= 1\'b0;  config_done <= 1\'b0; end end ARB_STATE : begin wr_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; else  state <= IDLE_STATE; end LINK_READY_STATE : begin case(link_cnt)  4\'d0: begin //首先写寄存器,选中page0 link_cnt <= 4\'d1; reg_addr <= 5\'d22; //寄存器地址22 wr_data <= 16\'d0; state <= WR_OP_STATE;  end  4\'d1: begin //其次读取PHY ID link_cnt <= 4\'d2; reg_addr <= 5\'d2; //寄存器地址2 state <= RD_OP_STATE;  end  4\'d2: begin //验证回读ID是否正确 if(rd_data_latch == 16\'h0141) begin state <= ARB_STATE; link_ok <= 1\'b1; end else begin state <= LINK_ERR_STATE; end  end  default:state <= IDLE_STATE; endcase end CFG_READY_STATE : begin wr_cnt <= wr_cnt + 4\'d1; case(wr_cnt) //-----由于FPGA MAC核已经调整好时序,因此PHY处不需要进行时序延时  4\'d0: begin //首先写寄存器,选中page2 reg_addr <= 5\'d22; //寄存器地址22 wr_data <= 16\'d2; state <= WR_OP_STATE;  end  4\'d1: begin //读寄存器 reg_addr <= 5\'d21; //寄存器地址21 state <= RD_OP_STATE;  end4\'d2: begin //取消PHY内部的延时 reg_addr <= 5\'d21; //寄存器地址21 wr_data <= rd_data_latch & 16\'hFFEF;//bit4置为0 state <= WR_OP_STATE;  end //-----PHY工作在RGMII to Copper模式  4\'d3: begin //首先写寄存器,选中page18 reg_addr <= 5\'d22; //寄存器地址22 wr_data <= 16\'d18; state <= WR_OP_STATE;  end  4\'d4: begin //其次配置MODE参数,使PHY工作在RGMII to Copper模式 reg_addr <= 5\'d20; //寄存器地址20 wr_data <= 16\'h0;//bit15表示软复位,bit[2:0]表示RGMII to Copper模式,其余为默认值 state <= WR_OP_STATE;  end  4\'d5: begin //其次配置MODE参数,使PHY工作在RGMII to Copper模式 reg_addr <= 5\'d20; //寄存器地址20 wr_data <= 16\'h8000;//bit15表示软复位,bit[2:0]表示RGMII to Copper模式,其余为默认值 state <= WR_OP_STATE; config_done <= 1\'b1;  end  default:state <= IDLE_STATE; endcase end WR_OP_STATE : begin wr_req <= 1\'b1; if(busy_d1 & !busy) //busy下降沿  state <= ARB_STATE; end RD_OP_STATE : begin  rd_req <= 1\'b1; if(rd_valid) begin  rd_data_latch <= rd_data; end if(rd_error)  state <= LINK_ERR_STATE; else if(busy_d1 & !busy) //busy下降沿  state <= ARB_STATE; end LINK_ERR_STATE : begin link_error <= 1\'b1; state <= IDLE_STATE; end DONE_STATE : begin state <= IDLE_STATE; end default : begin state <= IDLE_STATE; end endcase endend phy_smi #( .CLK_FREQ(25_000_000),//系统时钟频率 25M .MDC_DIV(50) //MDC分频系数 50 得到500K时钟)phy_smi( .clk(clk),//inputclk, .rst_n(rst_n),//inputrst_n, .mdc(mdc),//outputregmdc, .mdio(mdio),//inoutmdio, .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),//inputwr_req, .wr_data(wr_data),//input[15:0]wr_data, //rd_req信号上升沿有效 .rd_req(rd_req),//inputrd_req, .rd_data(rd_data),//outputreg[15:0]rd_data = 16\'d0, .rd_valid(rd_valid),//outputregrd_valid = 1\'b0,//接收到的数据有效标志 .rd_error(rd_error),//outputregrd_error = 1\'b0,//接收数据失败标志 .busy(busy) //outputbusy );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