以太网——MDIO(SMI)接口的FPGA实现

这篇具有很好参考价值的文章主要介绍了以太网——MDIO(SMI)接口的FPGA实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  在 MAC 与 PHY 之间,有一个配置接口,即 MDIO(也称 SMI,Serial Management Interface),可以配置 PHY 的工作模式、获取 PHY 芯片的工作状态等。本文以 PHY 芯片 B50610 为例,实现 MDIO 接口,以实现对传输速度、接口类型的自协商。

  MDIO 包含 2 根信号线:

  • MDC,由 MAC 侧提供给 PHY 的时钟信号,最大 12.5MHz;
  • MDIO,inout,数据线

  MDIO 的通信协议如下

mdio接口,数字逻辑,Ethernet,fpga开发

MDIO 的帧构成如下:

  • Preamble,32 位前导码,MAC 端发送 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 引脚为高阻状态,第二个 TA 位,PHY 将 MDIO 引脚拉低,准备发送数据,MAC 端此两位设为高阻,若 MAC 检测到第二位非低电平,表明对方无应答,可通过这个判断是否读取失败;在写操作中,不需要 MDIO 方向发生变化,MAC 固定输出 2’b10
  • Data,16bit 数据
  • IDLE,空闲状态下,一般将 MDIO 上拉

  MDIO 的时序如下

mdio接口,数字逻辑,Ethernet,fpga开发

可以看到,PHY 芯片在 MDC 上升沿读取数据,并在上升沿给出数据。因此 MAC 端须在 MDC 下降沿给出数据,而读取数据在上升沿、下降沿均可(不过需要注意,由于 MDIO_DELAY 最大可达 50ns,若使用高于 10M 的 MDC 频率,则下降沿读取可能出现问题,因此推荐 MAC 侧在 MDC 上升沿读取数据)。

  由于一次 MDIO 读写,均由 32bit 前导码,以及 32bit 读写比特流组成,因此可以方便地使用一个 5bit 计数器进行计数,然后根据当前的计数值操作 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		//写优先
			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		//必须下降沿发送数据
	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	//在上升沿读数据
	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 三个引脚进行配置

mdio接口,数字逻辑,Ethernet,fpga开发

比如我们想查看下网络是否连接、当前操作速度等信息,可以查看 Auxiliary Status Summary Register 寄存器,地址 0x19。各比特含义如下

mdio接口,数字逻辑,Ethernet,fpga开发

  测试代码略.

  连接路由器(100BASE)后:

mdio接口,数字逻辑,Ethernet,fpga开发

可以看到网络已连接(bit2=1),自协商已完成(bit15=1),当前网速 100M 全双工(bit10:8=0b101)。

拔掉网线:

mdio接口,数字逻辑,Ethernet,fpga开发

可以检测到网络断开(bit2=0),正在自协商过程中(bit15=0)。文章来源地址https://www.toymoban.com/news/detail-561600.html

到了这里,关于以太网——MDIO(SMI)接口的FPGA实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA 20个例程篇:12.千兆网口实现MDIO接口读写

           千兆网口是我们日常生活中经常见到的外设接口,在后面三个例程中,我们将会一起去动手实现千兆网口实现MDIO接口读写、ARP通信协议、ICMP和UDP通信协议等,这三个例程有一定的难度,通过实际分析、动手编码、模块划分、上板调试大家可以学到很多内容,涵盖了

    2024年02月01日
    浏览(44)
  • FPGA实现以太网(一)——以太网简介

    以太网(Ethernet)是当今现有局域网采用的最通用的通信协议标准, 该标准定义了在局域网中采用的电缆类型和信号处理方法。 以太网凭借其成本低、通信速率高、抗干扰性强等优点被广泛应用在网络远程监控、 交换机、工业自动化等对通信速率要求较高的场合。 以太网是一

    2024年02月03日
    浏览(17)
  • FPGA-以太网基础知识-MII接口-RMII接口-GMII接口-RGMII接口-MAC协议-UDP协议

    记录学习FPGA以太网基础知识、包括MII接口-RMII接口-GMII接口-RGMII接口-MAC协议、UDP协议 由上图可得,以太网传输流程 : 1、一块fpga的pcb板子以太网部分,包括FPGA芯片、PHY以太网芯片、网口接口。 2、FPGA芯片包括UDP层、IP层、MAC层,即FPGA封装好了这些硬件模块也叫IP核(这里的

    2024年02月08日
    浏览(15)
  • FPGA千兆网口数据传输MDIO接口——FPGA学习笔记3

            是当今现有局域网采用的最通用的通信协议标准,它规定了包括物理层的连线、电子信号和介质访问层协议的内容。成本低,通信速率高,抗干扰能力强。 标准以太网:10Mbit/s 快速以太网:100Mbit/s 千兆以太网:1000Mbit/s ......... 以太网和千兆网口其实不完全相同。

    2024年03月24日
    浏览(14)
  • FPGA实现千兆/百兆自适应以太网UDP传输

    笔者最近在项目中需要使用到ZYNQ中PL端做以太网UDP传输并且需要支持100M/1000M自适应切换。使用的PHY型号为RTL8211。以下分享的主要为利用已有的1000M协议栈修改为100M并且实现二者自适应切换,IP核主要实现以下功能 1、实现100M/1000M自适应 2、回环测试 PS:完整的IP核文件下载地

    2024年01月21日
    浏览(19)
  • 千兆以太网传输层 UDP 协议原理与 FPGA 实现(UDP接收)

    相关文章: (1)千兆以太网网络层 ARP 协议的原理与 FPGA 实现 (2)千兆以太网硬件设计及链路层 MAC 协议格式 (3)CRC校验原理及实现 (4)RGMII 与 GMII 转换电路设计 (5)千兆以太网网络层 IP 协议介绍与 IP 校 验和算法实现 (6)千兆以太网传输层 UDP 协议原理与 FPGA 实现(

    2024年02月04日
    浏览(53)
  • 千兆以太网芯片88E1111 RGMII模式的FPGA驱动实现

    千兆以太网芯片88E1111 RGMII模式的FPGA驱动实现 在网络应用领域,千兆以太网已经成为主流,而88E1111作为一款先进的千兆以太网芯片,其驱动实现对于网络设备的性能和稳定性有着至关重要的影响。本文将介绍在RGMII模式下,如何实现88E1111芯片在FPGA上的驱动。 一、准备工作

    2024年01月22日
    浏览(15)
  • FPGA高端项目:图像采集+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

    FPGA高端项目:图像采集+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持 没玩过图像处理、GT高速接口、UDP网络通信,都不好意思说自己玩儿过FPGA,这是CSDN某大佬说过的一句话,鄙人深信不疑。。。GT资源是Xilinx系列FPGA的重要卖点,也是做

    2024年02月05日
    浏览(22)
  • FPGA高端项目:图像采集+GTX+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

    FPGA高端项目:图像采集+GTX+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持 没玩过图像处理、GT高速接口、UDP网络通信,都不好意思说自己玩儿过FPGA,这是CSDN某大佬说过的一句话,鄙人深信不疑。。。GT资源是Xilinx系列FPGA的重要卖点,也是做

    2024年02月05日
    浏览(21)
  • FPGA高端项目:图像缩放+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

    FPGA高端项目:图像缩放+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持 没玩过图像处理、GT高速接口、UDP网络通信,都不好意思说自己玩儿过FPGA,这是CSDN某大佬说过的一句话,鄙人深信不疑。。。GT资源是Xilinx系列FPGA的重要卖点,也是做

    2024年02月05日
    浏览(19)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包