1.MDIO协议简介

  MAC和PHY芯片有一个配置接口,即MDIO接口。可以配置PHY芯片的工作模式以及获取PHY芯片的状态信息。PHY芯片内部有一系列寄存器。用户通过配置寄存器来配置PHY芯片的工作模式。
  FPGA通过MDIO接口对PHY芯片的内部寄存器进行配置。通常情况下芯片在默认情况下也可以工作,即配置芯片不是必须的。也可通过外接特殊引脚的方式来配置PHY芯片的工作模式。
在这里插入图片描述

2. MDIO协议时序

  MDIO接口也被称为SMI接口(Serial Management Interface,串行管理接口),包括ETH_MDC(数据管理时钟,最大不超过12.5MHZ)和ETH_MDIO(数据管理输入输出,双向数据线)两条信号线。
  MDIO接口的读写通信协议如下图:
在这里插入图片描述

名称1作用
Preamble32位引导码,由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空闲状态均为高阻态

转向就是MAC由发送数据变成接收数据
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.MDIO程序

`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



Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐