TEMAC配置及三速自协商环回测试

  上一篇文章中我们对 Xilinx 的三速以太网 MAC IP 核(Tri Mode Ethernet MAC,TEMAC)进行了介绍,本文在此基础上进一步实现 TEMAC 的配置功能控制以及三速自适应环回测试。

TEMAC Management 接口

  TEMAC 以 AXI4-Lite 接口实现配置接口,从而配置 TEMAC 寄存器以及实现对 PHY 的 MDIO 读写,AXI4-Lite 的读写时序如下:

在这里插入图片描述

在这里插入图片描述

注意到在 AXI4-Lite 的写时序中,地址和数据可以同时给出,因此在设计 FSM (有限状态机)时,相较于 AXI4 接口的,可以少设计一个状态。

AXI4-Lite 接口读写功能实现

  根据以上时序图,可设计如下的 AXI4-Lite 读写控制模块:

/* 
 * file			: AXI_Lite_ctrl.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2024-11-21
 * version		: v1.0
 * description	: AXI4-Lite Interface 控制模块
 * Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.
 */
`default_nettype none
module AXI_Lite_ctrl(
input	wire							s_axi_aclk,
input	wire							s_axi_resetn,

//User-Interface
input	wire							wr_req,				//写请求
input	wire	[AXI_ADDR_WIDTH-1:0]	wr_addr,
input	wire	[AXI_DATA_WIDTH-1:0]	wr_data,

input	wire							rd_req,				//读请求
input	wire	[AXI_ADDR_WIDTH-1:0]	rd_addr,
output	reg		[AXI_DATA_WIDTH-1:0]	rd_data,
output	reg								rd_valid,

output	reg								wr_busy,
output	reg								rd_busy,

//AXI4-Lite Interface
output	reg		[AXI_ADDR_WIDTH-1:0]	s_axi_awaddr,
output	reg								s_axi_awvalid,
input	wire							s_axi_awready,

output	reg		[AXI_DATA_WIDTH-1:0]	s_axi_wdata,
output	reg								s_axi_wvalid,
input	wire							s_axi_wready,

input	wire	[1:0]					s_axi_bresp,
input	wire							s_axi_bvalid,
output	reg								s_axi_bready,

output	reg		[AXI_ADDR_WIDTH-1:0]	s_axi_araddr,
output	reg								s_axi_arvalid,
input	wire							s_axi_arready,

input	wire	[AXI_DATA_WIDTH-1:0]	s_axi_rdata,
input	wire	[1:0]					s_axi_rresp,
input	wire							s_axi_rvalid,
output	reg								s_axi_rready
);

parameter	AXI_ADDR_WIDTH	= 12;
parameter	AXI_DATA_WIDTH	= 32;

//----------------------------------state define-----------------------------------------
localparam	S_INIT			= 8'h01;	//初始化
localparam	S_ARB			= 8'h02;	//仲裁(Arbitrate)接下来进行WR还是RD
localparam	S_WR_ADDR_DATA	= 8'h04;	//写地址/数据
localparam	S_WR_RESP		= 8'h10;	//写回复
localparam	S_RD_ADDR		= 8'h20;	//读地址
localparam	S_RD_DATA		= 8'h40;	//读数据/回复

//----------------------------------vars define------------------------------------------
reg		[7:0]	state		= S_INIT;
reg		[7:0]	next_state;

reg		[1:0]	arb_w_r		= 2'd0;		//0默认,1写,2读

//--------------------------------------FSM----------------------------------------------
always @(posedge s_axi_aclk or negedge s_axi_resetn) begin
	if(~s_axi_resetn) begin
		state	<= S_INIT;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin
	case(state)
	S_INIT: begin
		next_state	<= S_ARB;
	end
	S_ARB: begin
		case(arb_w_r)
		2'd1: begin
			next_state	<= S_WR_ADDR_DATA;
		end
		2'd2: begin
			next_state	<= S_RD_ADDR;
		end
		default: begin
			next_state	<= S_ARB;
		end
		endcase
	end
	S_WR_ADDR_DATA: begin
		if(s_axi_awvalid & s_axi_awready & s_axi_wvalid & s_axi_wready) begin
			next_state	<= S_WR_RESP;
		end
		else begin
			next_state	<= S_WR_ADDR_DATA;
		end
	end
	S_WR_RESP: begin
		if(s_axi_bvalid & s_axi_bready) begin
			next_state	<= S_ARB;
		end
		else begin
			next_state	<= S_WR_RESP;
		end
	end
	S_RD_ADDR: begin
		if(s_axi_arvalid & s_axi_arready) begin
			next_state	<= S_RD_DATA;
		end
		else begin
			next_state	<= S_RD_ADDR;
		end
	end
	S_RD_DATA: begin
		if(s_axi_rvalid & s_axi_rready) begin
			next_state	<= S_ARB;
		end
		else begin
			next_state	<= S_RD_DATA;
		end
	end
	default: begin
		next_state	<= S_INIT;
	end
	endcase
end

//arb_w_r
always @(posedge s_axi_aclk) begin
	case(state)
	S_INIT, S_WR_RESP: begin
		arb_w_r	<= 2'd0;
	end
	S_ARB: begin
		case(arb_w_r)
		2'd0: begin
			if(wr_req) begin			//写优先
				arb_w_r	<= 2'd1;
			end
			else if(rd_req) begin
				arb_w_r	<= 2'd2;
			end
			else begin
				arb_w_r	<= arb_w_r;
			end
		end
		2'd1, 2'd2: begin
			arb_w_r	<= arb_w_r;
		end
		default: begin
			arb_w_r	<= 2'd0;
		end
		endcase
	end
	default: begin
		arb_w_r	<= arb_w_r;
	end
	endcase
end

//s_axi_awaddr & s_axi_awvalid
always @(*) begin
	case(state)
	S_WR_ADDR_DATA: begin
		s_axi_awaddr	<= wr_addr;
		s_axi_awvalid	<= 1'b1;
	end
	default: begin
		s_axi_awaddr	<= 'd0;
		s_axi_awvalid	<= 1'b0;
	end
	endcase
end

//s_axi_wdata & s_axi_wvalid
always @(*) begin
	case(state)
	S_WR_ADDR_DATA: begin
		s_axi_wdata		<= wr_data;
		s_axi_wvalid	<= 1'b1;
	end
	default: begin
		s_axi_wdata		<= 'd0;
		s_axi_wvalid	<= 1'b0;
	end
	endcase
end

//s_axi_bready
always @(*) begin
	case(state)
	S_WR_RESP: begin
		s_axi_bready	<= 1'b1;
	end
	default: begin
		s_axi_bready	<= 1'b0;
	end
	endcase
end

//s_axi_araddr & s_axi_arvalid
always @(*) begin
	case(state)
	S_RD_ADDR: begin
		s_axi_araddr	<= rd_addr;
		s_axi_arvalid	<= 1'b1;
	end
	default: begin
		s_axi_araddr	<= 'd0;
		s_axi_arvalid	<= 1'b0;
	end
	endcase
end

//s_axi_rready
always @(*) begin
	case(state)
	S_RD_DATA: begin
		s_axi_rready	<= 1'b1;
	end
	default: begin
		s_axi_rready	<= 1'b0;
	end
	endcase
end

//rd_data
always @(*) begin
	rd_data		<= s_axi_rdata;
end

//rd_valid
always @(*) begin
	case(state)
	S_ARB: begin
		rd_valid	<= 1'b1;
	end
	default: begin
		rd_valid	<= 1'b0;
	end
	endcase
end

//wr_busy
always @(*) begin
	case(state)
	S_ARB: begin
		wr_busy		<= 1'b0;
	end
	S_WR_ADDR_DATA, S_WR_RESP, S_INIT: begin
		wr_busy		<= 1'b1;
	end
	default: begin
		wr_busy		<= 1'b1;
	end
	endcase
end

//rd_busy
always @(*) begin
	case(state)
	S_ARB: begin
		rd_busy		<= 1'b0;
	end
	S_RD_ADDR, S_RD_DATA, S_INIT: begin
		rd_busy		<= 1'b1;
	end
	default: begin
		rd_busy		<= 1'b1;
	end
	endcase
end

endmodule

TEMAC 配置控制

  在 AXI4-Lite 接口读写功能的基础上,设计 TEMAC 的配置功能,以实现三速自动仲裁。除此之外,还可以另设计基于 TEMAC 的 MDIO 读写功能,以及帧过滤器配置等功能。

  由于涉及到多个功能,因此需要合理设计状态机的转移顺序,我们可以设计如下的状态转换图:

//********************************** 状态转移示意 *********************************************
//S_INIT_START -> INIT -> S_INIT_FINISH -> S_set_Timer -> S_IDLE -> S_Auto -> S_END -----
//		^										^			|					^		|
//		|										|			v					|		|
//	  rst_n										|		  	|---> S_REG_WR --->-|		|
//												|		  	|---> S_REG_RD --->-|		|
//												|		  	|---> S_MDIO_WR -->-|		|
//												|		  	|---> S_MDIO_RD -->-|		|
//												|		  	----> S_Filter --->--		v
//												-------<-------------<-----------<-------
//********************************************************************************************

在上电后或执行 RST 后,进入初始化状态,对 TEMAC 进行初始化配置;初始化完成后设置合理时间的定时器,并进入等待状态(S_IDLE),在定时结束时,将执行一次自动协商;在 S_IDLE 期间,也可以接收用户命令,并根据用户发起的任务类型,分别进入寄存器读写事务、MDIO 读写事务、帧过滤器配置事务等任务。

  这里作为便于读者理解的简易 demo,仅实现状态转移图中所示的主要部分(包括初始化和三速自动协商),即

//S_INIT_START -> INIT -> S_INIT_FINISH -> S_set_Timer -> S_IDLE -> S_Auto -> S_END -----
//		^										^										|
//		|										|										|
//	  rst_n										|		  								v
//												-------<-------------<-----------<-------

因此设计如下的自动协商模块:

/* 
 * file			: auto_negotiation_TEMAC.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2024-11-21
 * version		: v1.0
 * description	: TEMAC Auto Negotiation   简化版demo
 */
`default_nettype none
module auto_negotiation_TEMAC(
input	wire			s_axi_aclk,
input	wire			s_axi_resetn,				//rst会重新进入INIT阶段

//Speed Auto Config
input	wire			inband_link_status,			//根据连接状态进行三速自适应,5s动作周期自动配置
input	wire	[1:0]	inband_clock_speed,
input	wire			inband_duplex_status,

//Some Configures
input	wire			enable_Promiscuous_Mode,	//是否开启混杂模式      以下这部分的配置都在INIT阶段执行
input	wire	[15:0]	enable_IFG_Adjust,			//动态IFG使能,使能后TEMAC将根据tx_ifg_delay动态调整帧间隙IFG
input	wire			enable_Jumbo_Frame,			//巨型帧使能
input	wire			disable_Check,				//接收器错误校验失能
input	wire			half_Duplex,				//全半双工(0:全双工,1:半双工)

//AXI4-Lite 接口控制
output	reg				AXI_Lite_ctrl_wr_req,
output	reg		[11:0]	AXI_Lite_ctrl_wr_addr,
output	reg		[31:0]	AXI_Lite_ctrl_wr_data,

output	reg				AXI_Lite_ctrl_rd_req,
output	reg		[11:0]	AXI_Lite_ctrl_rd_addr,
input	wire	[31:0]	AXI_Lite_ctrl_rd_data,
input	wire			AXI_Lite_ctrl_rd_valid,

input	wire			AXI_Lite_ctrl_wr_busy,
input	wire			AXI_Lite_ctrl_rd_busy
);

parameter	CLK_FREQ	= 10;						//s_axi_aclk频率,MHz
parameter	MAC_ADDR	= 48'h00_11_22_33_44_55;	//本地MAC地址,用于初始化配置 Receiver Configuration Word 和 Unicast Address Word 寄存器
parameter	PHY_ADDR	= 5'h01;					//PHY地址,用于MDIO读写

//----------------------------------- FSM Status ----------------------------------------------
localparam	S_INIT_START								= 8'h01;
localparam	S_INIT_WR_Receiver_Configuration_Word0		= 8'h02;		//配置接收器(接收流量控制帧的MAC、接收器使能、全半双工、校验使能)
localparam	S_INIT_WR_Receiver_Configuration_Word1		= 8'h03;
localparam	S_INIT_WR_Transmitter_Configuration_Word	= 8'h04;		//配置发送器(发送器使能、全半双工、动态IFG等)
localparam	S_INIT_WR_Unicast_Address_Word0				= 8'h05;		//配置接收器单播地址(关闭混杂模式时,TEMAC仅接收该地址的帧)
localparam	S_INIT_WR_Unicast_Address_Word1				= 8'h06;
localparam	S_INIT_WR_Frame_Filter_Control_Word			= 8'h07;		//配置混杂模式
localparam	S_INIT_WR_MDIO_Setup_Word					= 8'h08;		//配置MDIO使能以及MDC频率
localparam	S_INIT_FINISH								= 8'h0F;

localparam	S_set_Timer									= 8'h11;		//设置5s定时器
localparam	S_IDLE										= 8'h12;		//空闲&等待定时器   若定时器触发,则执行自动协商   等待期间也可执行用户发起的MDIO读写和TEMAC配置操作
localparam	S_END										= 8'h1F;

localparam	S_Auto_START								= 8'h21;
localparam	S_Auto_WR_Speed_Configuration_Word			= 8'h22;		//配置TEMAC速度,以实现三速自协商
localparam	S_Auto_END									= 8'h2F;

//********************************** 状态转移示意 *********************************************
//S_INIT_START -> INIT -> S_INIT_FINISH -> S_set_Timer -> S_IDLE -> S_Auto -> S_END -----
//		^										^			|					^		|
//		|										|			v					|		|
//	  rst_n										|		  	|---> S_REG_WR --->-|		|
//												|		  	|---> S_REG_RD --->-|		|
//												|		  	|---> S_MDIO_WR -->-|		|
//												|		  	|---> S_MDIO_RD -->-|		|
//												|		  	----> S_Filter --->--		v
//												-------<-------------<-----------<-------
//********************************************************************************************

//----------------------------- TEMAC Register Address ----------------------------------------
localparam	REG_ADDR_Receiver_Configuration_Word0		= 12'h400;	//接收器配置寄存器
localparam	REG_ADDR_Receiver_Configuration_Word1		= 12'h404;

localparam	REG_ADDR_Transmitter_Configuration_Word		= 12'h408;	//发送器配置寄存器

localparam	REG_ADDR_Speed_Configuration_Word			= 12'h410;	//速度配置寄存器

localparam	REG_ADDR_MDIO_Setup_Word					= 12'h500;	//MDIO配置寄存器
localparam	REG_ADDR_MDIO_Control_Word					= 12'h504;	//MDIO控制寄存器
localparam	REG_ADDR_MDIO_Write_Data					= 12'h508;	//MDIO写数据寄存器
localparam	REG_ADDR_MDIO_Read_Data						= 12'h50C;	//MDIO读数据寄存器

localparam	REG_ADDR_Unicast_Address_Word0				= 12'h700;	//单播地址寄存器
localparam	REG_ADDR_Unicast_Address_Word1				= 12'h704;

localparam	REG_ADDR_Frame_Filter_Control_Word			= 12'h708;	//帧过滤器控制寄存器
localparam	REG_ADDR_Frame_Filter_Enable				= 12'h70C;	//帧过滤器使能寄存器
localparam	REG_ADDR_Frame_Filter_Value_0				= 12'h710;	//帧过滤器Value寄存器,0x710, 0x714, ..., 0x74C 为 Filter Value 的 Byte3:0, Byte7:4, ..., Byte63:60
localparam	REG_ADDR_Frame_Filter_Mask_0				= 12'h750;	//帧过滤器Mask寄存器,0x750, 0x754, ..., 0x75C 为 Filter Mask 的 Byte3:0, Byte7:4, ..., Byte63:60

//----------------------------------- Some params ---------------------------------------------
localparam	MDIO_OP_WR		= 2'b01;	//MDIO操作符OP
localparam	MDIO_OP_RD		= 2'b10;

localparam	MDIO_clkdiv		= 6'd19;	//MDC_Freq = s_aclk_Freq / (2 * (1 + MDIO_clkdiv[5:0]))

//------------------------------------- Timer -------------------------------------------------
reg			timer_start;
wire		timer_busy;

timer #(
	.CLK_FREQ		(CLK_FREQ)
)
timer_inst(
	.clk			(s_axi_aclk),
	.rst_n			(s_axi_resetn),

	//定时
	.hour			(8'd0),
	.min			(6'd0),
	.sec			(6'd5),				//5s定时
	.ms				(10'd0),
	.us				(10'd0),

	.start			(timer_start),		//上升沿动作
	.busy			(timer_busy),

	//实时输出
	.hour_o			(),
	.min_o			(),
	.sec_o			(),
	.ms_o			(),
	.us_o			()
);

//-------------------------------------- FSM --------------------------------------------------
reg		[7:0]	state	= S_INIT_START;
reg		[7:0]	next_state;

reg		[1:0]	axi_status;		//获取AXI-Lite执行进程

always @(posedge s_axi_aclk or negedge s_axi_resetn) begin
	if(~s_axi_resetn) begin
		state	<= S_INIT_START;
	end
	else begin
		state	<= next_state;
	end
end

always @(*) begin
	case(state)
	//init
	S_INIT_START: begin
		next_state	<= S_INIT_WR_Receiver_Configuration_Word0;
	end
	S_INIT_WR_Receiver_Configuration_Word0: begin
		if(axi_status==2'd2)	next_state	<= S_INIT_WR_Receiver_Configuration_Word1;
		else					next_state	<= S_INIT_WR_Receiver_Configuration_Word0;
	end
	S_INIT_WR_Receiver_Configuration_Word1: begin
		if(axi_status==2'd2) begin
			next_state	<= S_INIT_WR_Unicast_Address_Word0;
			//next_state	<= S_INIT_WR_Transmitter_Configuration_Word;
			//Transmitter_Configuration_Word的配置可能导致发送器失效,原因未知,因此暂时跳过对这一寄存器的配置
		end
		else					next_state	<= S_INIT_WR_Receiver_Configuration_Word1;
	end
	S_INIT_WR_Transmitter_Configuration_Word: begin
		if(axi_status==2'd2)	next_state	<= S_INIT_WR_Unicast_Address_Word0;
		else					next_state	<= S_INIT_WR_Transmitter_Configuration_Word;
	end
	S_INIT_WR_Unicast_Address_Word0: begin
		if(axi_status==2'd2)	next_state	<= S_INIT_WR_Unicast_Address_Word1;
		else					next_state	<= S_INIT_WR_Unicast_Address_Word0;
	end
	S_INIT_WR_Unicast_Address_Word1: begin
		if(axi_status==2'd2)	next_state	<= S_INIT_WR_Frame_Filter_Control_Word;
		else					next_state	<= S_INIT_WR_Unicast_Address_Word1;
	end
	S_INIT_WR_Frame_Filter_Control_Word: begin
		if(axi_status==2'd2)	next_state	<= S_INIT_WR_MDIO_Setup_Word;
		else					next_state	<= S_INIT_WR_Frame_Filter_Control_Word;
	end
	S_INIT_WR_MDIO_Setup_Word: begin
		if(axi_status==2'd2)	next_state	<= S_INIT_FINISH;
		else					next_state	<= S_INIT_WR_MDIO_Setup_Word;
	end
	S_INIT_FINISH: begin
		next_state	<= S_set_Timer;
	end

	//main loop
	S_set_Timer: begin
		if(timer_busy) begin
			next_state	<= S_IDLE;
		end
		else begin
			next_state	<= S_set_Timer;
		end
	end
	S_IDLE: begin
		if(~timer_busy) begin			//定时结束,开始自动协商
			next_state	<= S_Auto_START;
		end
		else begin
			next_state	<= S_IDLE;
		end
	end
	S_END: begin
		next_state	<= S_set_Timer;
	end

	//auto negotiation
	S_Auto_START: begin
		next_state	<= S_Auto_WR_Speed_Configuration_Word;
	end
	S_Auto_WR_Speed_Configuration_Word: begin
		if(axi_status==2'd2)	next_state	<= S_Auto_END;
		else					next_state	<= S_Auto_WR_Speed_Configuration_Word;
	end
	S_Auto_END: begin
		next_state	<= S_END;
	end

	default: begin
		next_state	<= S_IDLE;
	end
	endcase
end

//timer_start
always @(posedge s_axi_aclk) begin
	case(state)
	S_set_Timer: begin
		if(timer_busy) begin			//若回到S_set_Timer时,定时器仍未结束,表明还在本计时周期内,不重新激发定时
			timer_start		<= 1'b0;
		end
		else begin						//否则表明为自动协商后回到的该状态,此时需重新激发定时器
			timer_start		<= 1'b1;
		end
	end
	default: begin
		timer_start		<= 1'b0;
	end
	endcase
end

//axi_status
always @(posedge s_axi_aclk) begin
	case(state)
	S_INIT_START, S_INIT_FINISH, S_set_Timer, S_IDLE, S_END, 
	S_Auto_START, S_Auto_END
	: begin
		axi_status	<= 2'd0;
	end
	S_INIT_WR_Receiver_Configuration_Word0, S_INIT_WR_Receiver_Configuration_Word1, 
	S_INIT_WR_Transmitter_Configuration_Word, 
	S_INIT_WR_Unicast_Address_Word0, S_INIT_WR_Unicast_Address_Word1, 
	S_INIT_WR_Frame_Filter_Control_Word, S_INIT_WR_MDIO_Setup_Word,
	S_Auto_WR_Speed_Configuration_Word
	: begin															//AXI-Lite 写事务
		if(axi_status==2'd0 && AXI_Lite_ctrl_wr_busy) begin
			axi_status	<= 2'd1;
		end
		else if(axi_status==2'd1 && ~AXI_Lite_ctrl_wr_busy) begin
			axi_status	<= 2'd2;
		end
		else if(axi_status==2'd2) begin
			axi_status	<= 2'd0;
		end
		else begin
			axi_status	<= axi_status;
		end
	end
	default: begin
		axi_status	<= 2'd0;
	end
	endcase
end

//AXI_Lite_ctrl WR
always @(posedge s_axi_aclk) begin
	case(state)
	//-----------------------INIT--------------------------------
	//Receiver Configuration Word, initial cfg
	S_INIT_WR_Receiver_Configuration_Word0: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Receiver_Configuration_Word0;
			AXI_Lite_ctrl_wr_data	<= {MAC_ADDR[23:16], MAC_ADDR[31:24], MAC_ADDR[39:32], MAC_ADDR[47:40]};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end
	S_INIT_WR_Receiver_Configuration_Word1: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Receiver_Configuration_Word1;
			AXI_Lite_ctrl_wr_data	<= {1'b0, enable_Jumbo_Frame, 1'b0, 1'b1, 1'b0, 
										half_Duplex, disable_Check, disable_Check, 
										8'd0, MAC_ADDR[7:0], MAC_ADDR[15:8]};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end

	//Transmitter Configuration Word, initial cfg
	S_INIT_WR_Transmitter_Configuration_Word: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Transmitter_Configuration_Word;
			AXI_Lite_ctrl_wr_data	<= {1'b0, enable_Jumbo_Frame, 1'b0, 1'b1, 1'b0, 
										half_Duplex, enable_IFG_Adjust, 25'd0};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end

	//Unicast Address Word, initial cfg
	S_INIT_WR_Unicast_Address_Word0: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Unicast_Address_Word0;
			AXI_Lite_ctrl_wr_data	<= {MAC_ADDR[23:16], MAC_ADDR[31:24], MAC_ADDR[39:32], MAC_ADDR[47:40]};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end
	S_INIT_WR_Unicast_Address_Word1: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Unicast_Address_Word1;
			AXI_Lite_ctrl_wr_data	<= {16'd0, MAC_ADDR[7:0], MAC_ADDR[15:8]};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end

	//Frame Filter Control, initial cfg
	S_INIT_WR_Frame_Filter_Control_Word: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Frame_Filter_Control_Word;
			AXI_Lite_ctrl_wr_data	<= {enable_Promiscuous_Mode, 22'd0, 1'b0, 4'd0, 4'd0};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end

	//MDIO Setup Word, initial cfg
	S_INIT_WR_MDIO_Setup_Word: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_MDIO_Setup_Word;
			AXI_Lite_ctrl_wr_data	<= {25'd0, 1'b1, MDIO_clkdiv};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end

	//-----------------------AUTO--------------------------------
	//Speed Configuration Word
	S_Auto_WR_Speed_Configuration_Word: begin
		if(axi_status==2'd0) begin
			AXI_Lite_ctrl_wr_req	<= 1'b1;
			AXI_Lite_ctrl_wr_addr	<= REG_ADDR_Speed_Configuration_Word;
			AXI_Lite_ctrl_wr_data	<= {inband_clock_speed, 30'd0};
		end
		else begin
			AXI_Lite_ctrl_wr_req	<= 1'b0;
			AXI_Lite_ctrl_wr_addr	<= AXI_Lite_ctrl_wr_addr;
			AXI_Lite_ctrl_wr_data	<= AXI_Lite_ctrl_wr_data;
		end
	end

	default: begin
		AXI_Lite_ctrl_wr_req	<= 1'b0;
		AXI_Lite_ctrl_wr_addr	<= 12'h000;
		AXI_Lite_ctrl_wr_data	<= 32'h0000_0000;
	end
	endcase
end

endmodule

其中用到的定时器实现如下

/* 
 * file			: timer.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2023-05-28
 * version		: v1.0
 * description	: 定时器
 */
`default_nettype none
module timer(
input	wire			clk,
input	wire			rst_n,

//定时
input	wire	[7:0]	hour,
input	wire	[5:0]	min,
input	wire	[5:0]	sec,
input	wire	[9:0]	ms,
input	wire	[9:0]	us,

input	wire			start,
output	reg				busy,

//实时输出
output	reg		[7:0]	hour_o,
output	reg		[5:0]	min_o,
output	reg		[5:0]	sec_o,
output	reg		[9:0]	ms_o,
output	reg		[9:0]	us_o
);

parameter	CLK_FREQ	= 100;	//MHz
//至少2M以上时钟

wire		clk_1M;

(* keep_hierarchy = "yes" *)
clkdiv #(.N(CLK_FREQ))
clkdiv_inst(
	.clk_in		(clk),
	.clk_out	(clk_1M)
);

//-----------------edge detect---------------------------
wire	start_pe;
reg		start_d0;
reg		start_d1;

wire	clk_1M_pe;
reg		clk_1M_d0;
reg		clk_1M_d1;

reg		start_pe_1M;

always @(posedge clk) begin
	start_d0	<= start;
	start_d1	<= start_d0;

	clk_1M_d0	<= clk_1M;
	clk_1M_d1	<= clk_1M_d0;
end

assign	start_pe	= start_d0 & (~start_d1);
assign	clk_1M_pe	= clk_1M_d0 & (~clk_1M_d1);

always @(posedge clk) begin
	if(~rst_n) begin
		start_pe_1M		<= 1'b0;
	end
	else begin
		if(start_pe) begin
			start_pe_1M		<= 1'b1;
		end
		else if(clk_1M_pe) begin
			start_pe_1M		<= 1'b0;
		end
		else begin
			start_pe_1M		<= start_pe_1M;
		end
	end
end

//-------------------timer---------------------------------
reg		[7:0]	hour_buf;
reg		[5:0]	min_buf;
reg		[5:0]	sec_buf;
reg		[9:0]	ms_buf;
reg		[9:0]	us_buf;

wire	[3:0]	carry;	//是否即将进位

assign	carry[0]	= (us_o >= 10'd999)? 1'b1 : 1'b0;
assign	carry[1]	= (ms_o >= 10'd999)? 1'b1 : 1'b0;
assign	carry[2]	= (sec_o >= 6'd59)? 1'b1 : 1'b0;
assign	carry[3]	= (min_o >= 6'd59)? 1'b1 : 1'b0;

always @(posedge clk_1M_pe) begin
	if(~rst_n) begin
		hour_o		<= 8'd0;
		min_o		<= 6'd0;
		sec_o		<= 6'd0;
		ms_o		<= 10'd0;
		us_o		<= 10'd0;

		busy		<= 1'b0;

		hour_buf	<= 8'd0;
		min_buf		<= 6'd0;
		sec_buf		<= 6'd0;
		ms_buf		<= 10'd0;
		us_buf		<= 10'd0;
	end
	else begin
		if(start_pe_1M) begin
			busy		<= 1'b1;
		end
		else if(hour_o >= hour_buf && min_o >= min_buf && sec_o >= sec_buf
				&& ms_o >= ms_buf && us_o >= us_buf) begin
			busy		<= 1'b0;
		end
		else begin
			busy		<= busy;
		end

		if(start_pe_1M) begin
			hour_buf	<= hour;
			min_buf		<= min;
			sec_buf		<= sec;
			ms_buf		<= ms;
			us_buf		<= us;
		end
		else begin
			hour_buf	<= hour_buf;
			min_buf		<= min_buf;
			sec_buf		<= sec_buf;
			ms_buf		<= ms_buf;
			us_buf		<= us_buf;
		end

		if(busy) begin
			if(carry[0])			us_o	<= 10'd0;
			else					us_o	<= us_o + 1'b1;

			if(&carry[1:0])			ms_o	<= 10'd0;
			else if(carry[0])		ms_o	<= ms_o + 1'b1;
			else					ms_o	<= ms_o;

			if(&carry[2:0])			sec_o	<= 10'd0;
			else if(&carry[1:0])	sec_o	<= sec_o + 1'b1;
			else					sec_o	<= sec_o;

			if(&carry[3:0])			min_o	<= 10'd0;
			else if(&carry[2:0])	min_o	<= min_o + 1'b1;
			else					min_o	<= min_o;

			if(&carry[3:0])			hour_o	<= hour_o + 1'b1;
			else					hour_o	<= hour_o;
		end
		else begin
			hour_o		<= 8'd0;
			min_o		<= 6'd0;
			sec_o		<= 6'd0;
			ms_o		<= 10'd0;
			us_o		<= 10'd0;
		end
	end
end

endmodule

TEMAC 三速自协商环回测试

  根据 TEMAC 的收发时序

在这里插入图片描述

在这里插入图片描述

可以设计 TEMAC 的收发控制模块如下,模块中使用了 AXI-Stream 接口形式的 Packet FIFO,用作输入输出缓冲

/* 
 * file			: MAC_use_TEMAC.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2024-11-23
 * version		: v1.0
 * description	: TEMAC收发控制模块
 */
`default_nettype none
module MAC_use_TEMAC(
input	wire			rst_n,

//TEMAC Interface
input	wire			rx_mac_aclk,
input	wire			rx_reset,
input	wire			rx_enable,

input	wire	[4:0]	rx_axis_filter_tuser,
input	wire	[7:0]	rx_axis_mac_tdata,
input	wire			rx_axis_mac_tvalid,
input	wire			rx_axis_mac_tlast,
input	wire			rx_axis_mac_tuser,

input	wire			tx_mac_aclk,
input	wire			tx_reset,
input	wire			tx_enable,

output	wire	[7:0]	tx_axis_mac_tdata,
output	reg				tx_axis_mac_tvalid,
output	wire			tx_axis_mac_tlast,
output	wire			tx_axis_mac_tuser,
input	wire			tx_axis_mac_tready,

//User Interface
input	wire			FIFO_w_aclk,			//Packet FIFO, AXI-Stream
input	wire			FIFO_w_axis_tvalid,
output	wire			FIFO_w_axis_tready,
input	wire	[7:0]	FIFO_w_axis_tdata,
input	wire			FIFO_w_axis_tuser,
input	wire			FIFO_w_axis_tlast,

input	wire			FIFO_r_aclk,
output	wire			FIFO_r_axis_tvalid,
input	wire			FIFO_r_axis_tready,
output	wire	[7:0]	FIFO_r_axis_tdata,
output	wire			FIFO_r_axis_tuser,
output	wire			FIFO_r_axis_tlast
);

wire			FIFO_s_aclk;
wire			FIFO_s_axis_tvalid;
wire			FIFO_s_axis_tready;
wire	[7:0]	FIFO_s_axis_tdata;
wire			FIFO_s_axis_tuser;
wire			FIFO_s_axis_tlast;

FIFO_AXI_Stream FIFO_AXI_Stream_r(
	.s_aresetn		(rst_n),
	.wr_rst_busy	(),
	.rd_rst_busy	(),

	.s_aclk			(FIFO_s_aclk),
	.s_axis_tvalid	(FIFO_s_axis_tvalid),
	.s_axis_tready	(FIFO_s_axis_tready),
	.s_axis_tdata	(FIFO_s_axis_tdata),
	.s_axis_tuser	(FIFO_s_axis_tuser),
	.s_axis_tlast	(FIFO_s_axis_tlast),

	.m_aclk			(FIFO_r_aclk),
	.m_axis_tvalid	(FIFO_r_axis_tvalid),
	.m_axis_tready	(FIFO_r_axis_tready),
	.m_axis_tdata	(FIFO_r_axis_tdata),
	.m_axis_tuser	(FIFO_r_axis_tuser),
	.m_axis_tlast	(FIFO_r_axis_tlast)
);

wire			FIFO_m_aclk;
wire			FIFO_m_axis_tvalid;
reg				FIFO_m_axis_tready;
wire	[7:0]	FIFO_m_axis_tdata;
wire			FIFO_m_axis_tuser;
wire			FIFO_m_axis_tlast;

FIFO_AXI_Stream FIFO_AXI_Stream_w(
	.s_aresetn		(rst_n),
	.wr_rst_busy	(),
	.rd_rst_busy	(),

	.s_aclk			(FIFO_w_aclk),
	.s_axis_tvalid	(FIFO_w_axis_tvalid),
	.s_axis_tready	(FIFO_w_axis_tready),
	.s_axis_tdata	(FIFO_w_axis_tdata),
	.s_axis_tuser	(FIFO_w_axis_tuser),
	.s_axis_tlast	(FIFO_w_axis_tlast),

	.m_aclk			(FIFO_m_aclk),
	.m_axis_tvalid	(FIFO_m_axis_tvalid),
	.m_axis_tready	(FIFO_m_axis_tready),
	.m_axis_tdata	(FIFO_m_axis_tdata),
	.m_axis_tuser	(FIFO_m_axis_tuser),
	.m_axis_tlast	(FIFO_m_axis_tlast)
);

//--------------------------Ctrl-------------------------------
assign	FIFO_s_aclk			= rx_mac_aclk;
assign	FIFO_s_axis_tvalid	= rx_axis_mac_tvalid;	
assign	FIFO_s_axis_tdata	= rx_axis_mac_tdata;
assign	FIFO_s_axis_tuser	= rx_axis_mac_tuser;
assign	FIFO_s_axis_tlast	= rx_axis_mac_tlast;

assign	FIFO_m_aclk			= tx_mac_aclk;
assign	tx_axis_mac_tdata	= FIFO_m_axis_tdata;
assign	tx_axis_mac_tuser	= FIFO_m_axis_tuser;
assign	tx_axis_mac_tlast	= FIFO_m_axis_tlast;

//tx_axis_mac_tvalid
always @(posedge FIFO_m_aclk or negedge rst_n) begin
	if(~rst_n) begin
		tx_axis_mac_tvalid	<= 1'b0;
	end
	else if(FIFO_m_axis_tvalid) begin
		tx_axis_mac_tvalid	<= 1'b1;
	end
	else begin
		tx_axis_mac_tvalid	<= 1'b0;
	end
end

//FIFO_m_axis_tready
always @(*) begin
	if(tx_axis_mac_tready & tx_axis_mac_tvalid) begin
		FIFO_m_axis_tready	<= 1'b1;
	end
	else begin
		FIFO_m_axis_tready	<= 1'b0;
	end
end

endmodule

  结合以上的 MAC 控制器和自动协商模块,从而实现三速自动协商:

//测试 ETH - 使用三速 MAC IP,看能否正常环回
`default_nettype none
module top(
input	wire			clk_sys,			//OXCO_10M

//ETH
input	wire			eth0_rxc,			//MAC-PHY接收数据时钟
input	wire			eth0_rx_dv,			//MAC-PHY输入数据有效信号
input	wire	[3:0]	eth0_rxd,			//MAC-PHY输入数据
output	wire			eth0_txc,			//MAC-PHY发送数据时钟
output	wire			eth0_tx_en,			//MAC-PHY输出数据有效信号
output	wire	[3:0]	eth0_txd,			//MAC-PHY输出数据
output	wire			eth0_rst_n,			//以太网芯片复位信号,低电平有效
output	wire			eth0_mdc,
inout	wire			eth0_mdio,
input	wire			eth0_clk_125,

input	wire			eth1_rxc,			//MAC-PHY接收数据时钟
input	wire			eth1_rx_dv,			//MAC-PHY输入数据有效信号
input	wire	[3:0]	eth1_rxd,			//MAC-PHY输入数据
output	wire			eth1_txc,			//MAC-PHY发送数据时钟
output	wire			eth1_tx_en,			//MAC-PHY输出数据有效信号
output	wire	[3:0]	eth1_txd,			//MAC-PHY输出数据
output	wire			eth1_rst_n,			//以太网芯片复位信号,低电平有效
output	wire			eth1_mdc,
inout	wire			eth1_mdio,
input	wire			eth1_clk_125,

//key
input	wire	[3:0]	Key,				//=L表示按下

//led
output	wire	[3:0]	LED,				//高电平有效
output	wire	[3:0]	LED_work			//高电平有效
);

reg				rst_n	= 1'b1;

wire			clk_100M;
wire			clk_200M;
wire			clk_250M;
wire			clk_500M;

//PLL 10M -> 100M/200M
clk_wiz_0 clk_wiz(
	.clk_in1	(clk_sys),
	.clk_out1	(clk_100M),
	.clk_out2	(clk_200M),
	.reset		(1'b0),
	.locked		()
);

//PLL 100M -> 250M/500M
clk_wiz_1 clk_wiz1(
	.clk_in1	(clk_100M),
	.clk_out1	(clk_250M),
	.clk_out2	(clk_500M),
	.reset		(1'b0),
	.locked		()
);

clk_wiz_GTX_clkgen clk_wiz_GTX_clkgen_inst(
	.clk_in1	(eth0_clk_125),
	.clk_out1	(gtx_clk),
	.clk_out2	(gtx_clk90),
	.reset		(1'b0),
	.locked		()
);

//--------------------------rst---------------------------------
reg		[7:0]	rst_cnt	= 8'd0;

always @(posedge clk_sys) begin
	if(rst_cnt < 8'd200) begin
		rst_cnt		<= rst_cnt + 1'b1;
	end
	else begin
		rst_cnt		<= rst_cnt;
	end
end

always @(*) begin
	if(rst_cnt < 8'd200) begin
		rst_n	<= 1'b0;
	end
	else begin
		rst_n	<= 1'b1;
	end
end

//-------------------- TEMAC IP Core ---------------------------
wire			s_axi_aclk;
wire			s_axi_resetn;

wire			gtx_clk;
wire			gtx_clk90;
wire			glbl_rstn;

wire			rx_axi_rstn;
wire			tx_axi_rstn;

wire	[27:0]	rx_statistics_vector;
wire			rx_statistics_valid;

wire			rx_mac_aclk;
wire			rx_reset;
wire			rx_enable;

wire	[4:0]	rx_axis_filter_tuser;
wire	[7:0]	rx_axis_mac_tdata;
wire			rx_axis_mac_tvalid;
wire			rx_axis_mac_tlast;
wire			rx_axis_mac_tuser;

wire	[7:0]	tx_ifg_delay;
wire	[31:0]	tx_statistics_vector;
wire			tx_statistics_valid;

wire			tx_mac_aclk;
wire			tx_reset;
wire			tx_enable;

wire	[7:0]	tx_axis_mac_tdata;
wire			tx_axis_mac_tvalid;
wire			tx_axis_mac_tlast;
wire			tx_axis_mac_tuser;
wire			tx_axis_mac_tready;		//这个信号在数据被正确接收时被断言,而非指示MAC空闲!!!

wire			pause_req;
wire	[15:0]	pause_val;

wire			speedis100;
wire			speedis10100;

wire	[3:0]	rgmii_txd;
wire			rgmii_tx_ctl;
wire			rgmii_txc;

wire	[3:0]	rgmii_rxd;
wire			rgmii_rx_ctl;
wire			rgmii_rxc;

wire			inband_link_status;
wire	[1:0]	inband_clock_speed;
wire			inband_duplex_status;

wire			mdio;
wire			mdc;

wire	[11:0]	s_axi_awaddr;
wire			s_axi_awvalid;
wire			s_axi_awready;

wire	[31:0]	s_axi_wdata;
wire			s_axi_wvalid;
wire			s_axi_wready;

wire	[1:0]	s_axi_bresp;
wire			s_axi_bvalid;
wire			s_axi_bready;

wire	[11:0]	s_axi_araddr;
wire			s_axi_arvalid;
wire			s_axi_arready;

wire	[31:0]	s_axi_rdata;
wire	[1:0]	s_axi_rresp;
wire			s_axi_rvalid;
wire			s_axi_rready;

wire			mac_irq;

tri_mode_ethernet_mac_0 tri_mode_ethernet_mac_0_inst(
	.s_axi_aclk				(s_axi_aclk),					// input wire s_axi_aclk
	.s_axi_resetn			(s_axi_resetn),					// input wire s_axi_resetn

	.gtx_clk				(gtx_clk),						// input wire gtx_clk
	.gtx_clk90				(gtx_clk90),					// input wire gtx_clk90
	.glbl_rstn				(glbl_rstn),					// input wire glbl_rstn

	.rx_axi_rstn			(rx_axi_rstn),					// input wire rx_axi_rstn
	.tx_axi_rstn			(tx_axi_rstn),					// input wire tx_axi_rstn

	//RX statistics
	.rx_statistics_vector	(rx_statistics_vector),			// output wire [27 : 0] rx_statistics_vector
	.rx_statistics_valid	(rx_statistics_valid),			// output wire rx_statistics_valid

	//RX Interface
	.rx_mac_aclk			(rx_mac_aclk),					// output wire rx_mac_aclk
	.rx_reset				(rx_reset),						// output wire rx_reset
	.rx_enable				(rx_enable),					// output wire rx_enable

	.rx_axis_filter_tuser	(rx_axis_filter_tuser),			// output wire [4 : 0] rx_axis_filter_tuser
	.rx_axis_mac_tdata		(rx_axis_mac_tdata),			// output wire [7 : 0] rx_axis_mac_tdata
	.rx_axis_mac_tvalid		(rx_axis_mac_tvalid),			// output wire rx_axis_mac_tvalid
	.rx_axis_mac_tlast		(rx_axis_mac_tlast),			// output wire rx_axis_mac_tlast
	.rx_axis_mac_tuser		(rx_axis_mac_tuser),			// output wire rx_axis_mac_tuser

	//TX statistics & IFG
	.tx_ifg_delay			(tx_ifg_delay),					// input wire [7 : 0] tx_ifg_delay
	.tx_statistics_vector	(tx_statistics_vector),			// output wire [31 : 0] tx_statistics_vector
	.tx_statistics_valid	(tx_statistics_valid),			// output wire tx_statistics_valid

	//TX Interface
	.tx_mac_aclk			(tx_mac_aclk),					// output wire tx_mac_aclk
	.tx_reset				(tx_reset),						// output wire tx_reset
	.tx_enable				(tx_enable),					// output wire tx_enable

	.tx_axis_mac_tdata		(tx_axis_mac_tdata),			// input wire [7 : 0] tx_axis_mac_tdata
	.tx_axis_mac_tvalid		(tx_axis_mac_tvalid),			// input wire tx_axis_mac_tvalid
	.tx_axis_mac_tlast		(tx_axis_mac_tlast),			// input wire tx_axis_mac_tlast
	.tx_axis_mac_tuser		(tx_axis_mac_tuser),			// input wire [0 : 0] tx_axis_mac_tuser
	.tx_axis_mac_tready		(tx_axis_mac_tready),			// output wire tx_axis_mac_tready

	//流量控制
	.pause_req				(pause_req),					// input wire pause_req
	.pause_val				(pause_val),					// input wire [15 : 0] pause_val

	//speed
	.speedis100				(speedis100),					// output wire speedis100
	.speedis10100			(speedis10100),					// output wire speedis10100

	//PHY Interface
	.rgmii_txd				(rgmii_txd),					// output wire [3 : 0] rgmii_txd
	.rgmii_tx_ctl			(rgmii_tx_ctl),					// output wire rgmii_tx_ctl
	.rgmii_txc				(rgmii_txc),					// output wire rgmii_txc

	.rgmii_rxd				(rgmii_rxd),					// input wire [3 : 0] rgmii_rxd
	.rgmii_rx_ctl			(rgmii_rx_ctl),					// input wire rgmii_rx_ctl
	.rgmii_rxc				(rgmii_rxc),					// input wire rgmii_rxc

	//Optional RGMII Interface Signal Pinout
	.inband_link_status		(inband_link_status),			// output wire inband_link_status
	.inband_clock_speed		(inband_clock_speed),			// output wire [1 : 0] inband_clock_speed
	.inband_duplex_status	(inband_duplex_status),			// output wire inband_duplex_status

	//MDIO
	.mdio					(mdio),							// inout wire mdio
	.mdc					(mdc),							// output wire mdc

	//AXI4-Lite Interface
	.s_axi_awaddr			(s_axi_awaddr),					// input wire [11 : 0] s_axi_awaddr
	.s_axi_awvalid			(s_axi_awvalid),				// input wire s_axi_awvalid
	.s_axi_awready			(s_axi_awready),				// output wire s_axi_awready

	.s_axi_wdata			(s_axi_wdata),					// input wire [31 : 0] s_axi_wdata
	.s_axi_wvalid			(s_axi_wvalid),					// input wire s_axi_wvalid
	.s_axi_wready			(s_axi_wready),					// output wire s_axi_wready

	.s_axi_bresp			(s_axi_bresp),					// output wire [1 : 0] s_axi_bresp
	.s_axi_bvalid			(s_axi_bvalid),					// output wire s_axi_bvalid
	.s_axi_bready			(s_axi_bready),					// input wire s_axi_bready

	.s_axi_araddr			(s_axi_araddr),					// input wire [11 : 0] s_axi_araddr
	.s_axi_arvalid			(s_axi_arvalid),				// input wire s_axi_arvalid
	.s_axi_arready			(s_axi_arready),				// output wire s_axi_arready

	.s_axi_rdata			(s_axi_rdata),					// output wire [31 : 0] s_axi_rdata
	.s_axi_rresp			(s_axi_rresp),					// output wire [1 : 0] s_axi_rresp
	.s_axi_rvalid			(s_axi_rvalid),					// output wire s_axi_rvalid
	.s_axi_rready			(s_axi_rready),					// input wire s_axi_rready

	//中断输出
	.mac_irq				(mac_irq)						// output wire mac_irq
);

assign	s_axi_aclk		= clk_sys;
assign	s_axi_resetn	= rst_n;

assign	glbl_rstn		= rst_n;
assign	rx_axi_rstn		= rst_n;
assign	tx_axi_rstn		= rst_n;

assign	tx_ifg_delay	= 8'd12;	//Transmitter Configuration Register (0x408) 的 bit25 为 1 时,根据该端口确定帧间隙 IFG

assign	pause_req		= 1'b0;
assign	pause_val		= 16'd0;

//RGMII
assign	eth0_txd		= rgmii_txd;
assign	eth0_tx_en		= rgmii_tx_ctl;
assign	eth0_txc		= rgmii_txc;

assign	rgmii_rxd		= eth0_rxd;
assign	rgmii_rx_ctl	= eth0_rx_dv;
assign	rgmii_rxc		= eth0_rxc;

//MDIO
assign	eth0_mdio		= mdio;
assign	eth0_mdc		= mdc;

//-------------------------- IDELAYCTRL -------------------------------------
wire	idelayctrl_ready;
wire	refclk;
wire	idelayctrl_reset;

//如果使用了 IDELAYE2 或者 ODELAYE2 原语,那么 IDELAYCTRL 原语必须被例化
IDELAYCTRL #(
	.SIM_DEVICE	("7SERIES")
)
tri_mode_ethernet_mac_idelayctrl_common_i (
	.RDY		(idelayctrl_ready),		//out
	.REFCLK		(refclk),				//in,调节精度: 200M ~ 0.78ps,300M ~ 52ps
	.RST		(idelayctrl_reset)		//in,低电平有效
);

assign	refclk				= clk_200M;
assign	idelayctrl_reset	= 1'b1;

//-------------------------- Loopback -------------------------------------
wire			FIFO_w_aclk;
wire			FIFO_w_axis_tvalid;
wire			FIFO_w_axis_tready;
wire	[7:0]	FIFO_w_axis_tdata;
wire			FIFO_w_axis_tuser;
wire			FIFO_w_axis_tlast;

wire			FIFO_r_aclk;
wire			FIFO_r_axis_tvalid;
wire			FIFO_r_axis_tready;
wire	[7:0]	FIFO_r_axis_tdata;
wire			FIFO_r_axis_tuser;
wire			FIFO_r_axis_tlast;

(* keep_hierarchy = "yes" *)
MAC_use_TEMAC MAC_use_TEMAC_inst(
	.rst_n,

	//TEMAC Interface
	.rx_mac_aclk			(rx_mac_aclk),
	.rx_reset				(rx_reset),
	.rx_enable				(rx_enable),

	.rx_axis_filter_tuser	(rx_axis_filter_tuser),
	.rx_axis_mac_tdata		(rx_axis_mac_tdata),
	.rx_axis_mac_tvalid		(rx_axis_mac_tvalid),
	.rx_axis_mac_tlast		(rx_axis_mac_tlast),
	.rx_axis_mac_tuser		(rx_axis_mac_tuser),

	.tx_mac_aclk			(tx_mac_aclk),
	.tx_reset				(tx_reset),
	.tx_enable				(tx_enable),

	.tx_axis_mac_tdata		(tx_axis_mac_tdata),
	.tx_axis_mac_tvalid		(tx_axis_mac_tvalid),
	.tx_axis_mac_tlast		(tx_axis_mac_tlast),
	.tx_axis_mac_tuser		(tx_axis_mac_tuser),
	.tx_axis_mac_tready		(tx_axis_mac_tready),

	//User Interface
	.FIFO_w_aclk			(FIFO_w_aclk),			//Packet FIFO, AXI-Stream
	.FIFO_w_axis_tvalid		(FIFO_w_axis_tvalid),
	.FIFO_w_axis_tready		(FIFO_w_axis_tready),
	.FIFO_w_axis_tdata		(FIFO_w_axis_tdata),
	.FIFO_w_axis_tuser		(FIFO_w_axis_tuser),
	.FIFO_w_axis_tlast		(FIFO_w_axis_tlast),

	.FIFO_r_aclk			(FIFO_r_aclk),
	.FIFO_r_axis_tvalid		(FIFO_r_axis_tvalid),
	.FIFO_r_axis_tready		(FIFO_r_axis_tready),
	.FIFO_r_axis_tdata		(FIFO_r_axis_tdata),
	.FIFO_r_axis_tuser		(FIFO_r_axis_tuser),
	.FIFO_r_axis_tlast		(FIFO_r_axis_tlast)
);

assign	FIFO_w_aclk			= gtx_clk;
assign	FIFO_r_aclk			= gtx_clk;

assign	FIFO_w_axis_tdata	= FIFO_r_axis_tdata;
assign	FIFO_w_axis_tuser	= FIFO_r_axis_tuser;
assign	FIFO_w_axis_tlast	= FIFO_r_axis_tlast;

assign	FIFO_w_axis_tvalid	= FIFO_w_axis_tready & FIFO_r_axis_tready;
assign	FIFO_r_axis_tready	= FIFO_r_axis_tvalid & FIFO_w_axis_tready;

//-------------------- TEMAC CFG ------------------------------
//AXI-Lite 接口控制器
wire			AXI_Lite_ctrl_wr_req;
wire	[11:0]	AXI_Lite_ctrl_wr_addr;
wire	[31:0]	AXI_Lite_ctrl_wr_data;

wire			AXI_Lite_ctrl_rd_req;
wire	[11:0]	AXI_Lite_ctrl_rd_addr;
wire	[31:0]	AXI_Lite_ctrl_rd_data;
wire			AXI_Lite_ctrl_rd_valid;

wire			AXI_Lite_ctrl_wr_busy;
wire			AXI_Lite_ctrl_rd_busy;

(* keep_hierarchy = "yes" *)
AXI_Lite_ctrl #(
	.AXI_ADDR_WIDTH		(12),
	.AXI_DATA_WIDTH		(32)
)
AXI_Lite_ctrl_inst(
	.s_axi_aclk			(s_axi_aclk),
	.s_axi_resetn		(s_axi_resetn),

	//User-Interface
	.wr_req				(AXI_Lite_ctrl_wr_req),
	.wr_addr			(AXI_Lite_ctrl_wr_addr),
	.wr_data			(AXI_Lite_ctrl_wr_data),

	.rd_req				(AXI_Lite_ctrl_rd_req),
	.rd_addr			(AXI_Lite_ctrl_rd_addr),
	.rd_data			(AXI_Lite_ctrl_rd_data),
	.rd_valid			(AXI_Lite_ctrl_rd_valid),

	.wr_busy			(AXI_Lite_ctrl_wr_busy),
	.rd_busy			(AXI_Lite_ctrl_rd_busy),

	//AXI4-Lite Interface
	.s_axi_awaddr		(s_axi_awaddr),
	.s_axi_awvalid		(s_axi_awvalid),
	.s_axi_awready		(s_axi_awready),

	.s_axi_wdata		(s_axi_wdata),
	.s_axi_wvalid		(s_axi_wvalid),
	.s_axi_wready		(s_axi_wready),

	.s_axi_bresp		(s_axi_bresp),
	.s_axi_bvalid		(s_axi_bvalid),
	.s_axi_bready		(s_axi_bready),

	.s_axi_araddr		(s_axi_araddr),
	.s_axi_arvalid		(s_axi_arvalid),
	.s_axi_arready		(s_axi_arready),

	.s_axi_rdata		(s_axi_rdata),
	.s_axi_rresp		(s_axi_rresp),
	.s_axi_rvalid		(s_axi_rvalid),
	.s_axi_rready		(s_axi_rready)
);

//自动协商
(* keep_hierarchy = "yes" *)
auto_negotiation_TEMAC #(
	.CLK_FREQ					(10),
	.MAC_ADDR					(48'h00_11_22_33_44_55),
	.PHY_ADDR					(5'h01)
)
auto_negotiation_TEMAC_inst(
	.s_axi_aclk					(s_axi_aclk),
	.s_axi_resetn				(s_axi_resetn),					//rst会重新进入INIT阶段

	//Speed Auto Config
	.inband_link_status			(inband_link_status),			//根据连接状态进行三速自适应,5s动作周期自动配置
	.inband_clock_speed			(inband_clock_speed),
	.inband_duplex_status		(inband_duplex_status),

	//Some Configures
	.enable_Promiscuous_Mode	(1'b1),							//是否开启混杂模式      以下这部分的配置都在INIT阶段执行
	.enable_IFG_Adjust			(1'b0),							//动态IFG使能,使能后TEMAC将根据tx_ifg_delay动态调整帧间隙IFG
	.enable_Jumbo_Frame			(1'b0),							//巨型帧使能
	.disable_Check				(1'b0),							//接收器错误校验失能
	.half_Duplex				(1'b0),							//全半双工

	//AXI4-Lite 接口控制
	.AXI_Lite_ctrl_wr_req		(AXI_Lite_ctrl_wr_req),
	.AXI_Lite_ctrl_wr_addr		(AXI_Lite_ctrl_wr_addr),
	.AXI_Lite_ctrl_wr_data		(AXI_Lite_ctrl_wr_data),

	.AXI_Lite_ctrl_rd_req		(AXI_Lite_ctrl_rd_req),
	.AXI_Lite_ctrl_rd_addr		(AXI_Lite_ctrl_rd_addr),
	.AXI_Lite_ctrl_rd_data		(AXI_Lite_ctrl_rd_data),
	.AXI_Lite_ctrl_rd_valid		(AXI_Lite_ctrl_rd_valid),

	.AXI_Lite_ctrl_wr_busy		(AXI_Lite_ctrl_wr_busy),
	.AXI_Lite_ctrl_rd_busy		(AXI_Lite_ctrl_rd_busy)
);

//---------------------------状态指示----------------------------
assign	LED[0]		= inband_link_status;
assign	LED[1]		= inband_duplex_status;
assign	LED[3:2]	= inband_clock_speed;

//-------------------ILA-------------------
ila_t16 ila(
	.clk		(clk_250M),

	//clocks
	.probe0		(gtx_clk),				//125M
	.probe1		(gtx_clk90),

	.probe2		(s_axi_aclk),			//10M
	.probe3		(tx_mac_aclk),			//125M
	.probe4		(rx_mac_aclk),			//2.5/25/125M

	//statistics
	.probe5		(rx_statistics_vector),
	.probe6		(rx_statistics_valid),

	.probe7		(tx_statistics_vector),
	.probe8		(tx_statistics_valid),

	//RX/TX rst & enable
	.probe9		(rx_reset),
	.probe10	(rx_enable),

	.probe11	(tx_reset),
	.probe12	(tx_enable),

	//RX
	.probe13	(rx_axis_filter_tuser),
	.probe14	(rx_axis_mac_tdata),
	.probe15	(rx_axis_mac_tvalid),
	.probe16	(rx_axis_mac_tlast),
	.probe17	(rx_axis_mac_tuser),

	//TX
	.probe18	(tx_axis_mac_tdata),
	.probe19	(tx_axis_mac_tvalid),
	.probe20	(tx_axis_mac_tlast),
	.probe21	(tx_axis_mac_tuser),
	.probe22	(tx_axis_mac_tready),

	//linked status
	.probe23	(speedis100),
	.probe24	(speedis10100),

	.probe25	(inband_link_status),
	.probe26	(inband_clock_speed),
	.probe27	(inband_duplex_status),

	//中断
	.probe28	(mac_irq),

	//AXI4-Lite
	.probe29	(s_axi_awaddr),
	.probe30	(s_axi_awvalid),
	.probe31	(s_axi_awready),

	.probe32	(s_axi_wdata),
	.probe33	(s_axi_wvalid),
	.probe34	(s_axi_wready),

	.probe35	(s_axi_bresp),
	.probe36	(s_axi_bvalid),
	.probe37	(s_axi_bready),

	.probe38	(s_axi_araddr),
	.probe39	(s_axi_arvalid),
	.probe40	(s_axi_arready),

	.probe41	(s_axi_rdata),
	.probe42	(s_axi_rresp),
	.probe43	(s_axi_rvalid),
	.probe44	(s_axi_rready)
);

endmodule

  修改上位机的网络连接速度和全半双工模式,测试能否实现三速自动协商:

  • 1000M 全双工下的环回数据

在这里插入图片描述

  • 100M 全双工下的环回数据

在这里插入图片描述

其他速度和全半双工模式下的环回结果这里不再一一展示,读者可自行尝试。

  上位机使用 wireshark 抓取环回数据,也可以看到所有报文均正常环回,如下所示

在这里插入图片描述

(END)

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今朝无言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值