上一篇文章中我们对 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)