文章目录
前言
MAC_RX的设计暂时告一段落,本节将开始进行MAC_TX的设计。
一、模块功能
- 接收上层用户的AXIS数据,将其转换为XGMII进接口的数据发送给IP核。
- 可接受AXIS数据流,可支持数据包之间的间隔最小为一个时钟周期
- 目的MAC以及源MAC等参数可动态配置
- 流控,万兆以太网帧间隔为9.6ns,用户时钟频率为156.25Mhz,周期为6.4ns,因此XGMII接口数据之前至少间隔2个时钟周期
模块接口如下:
c
module TEN_GIG_MAC_TX#(
parameter P_SRC_MAC = 48'h00_00_00_00_00_00,
parameter P_DST_MAC = 48'h00_00_00_00_00_00
)(
input i_clk ,
input i_rst ,
input [47:0] i_dynamic_src_mac ,
input i_dynamic_src_valid ,
input [47:0] i_dynamic_dst_mac ,
input i_dynamic_dst_valid ,
input [63:0] s_axis_tdata ,
input [79:0] s_axis_tuser ,
input [7 :0] s_axis_tkeep ,
input s_axis_tlast ,
input s_axis_tvalid ,
output s_axis_tready ,
output [63:0] o_xgmii_txd ,
output [7 :0] o_xgmii_txc
);
二、实现方式
- 将接收到的AXIS数据先存入FIFO,将包头组织完毕后从FIFO当中读取数据进行填充
- 组帧同时要进行CRC计算并且在末尾填充,需要注意,在尾端填充CRC数据时,根据尾端keep信号有多种情况,会存在数据额外需要一个时钟周期进行传输CRC数据。
包含子模块如下:
c
FIFO_64X256 FIFO_64X256_data_tx (
.clk (i_clk ),
.srst (i_rst ),
.din (rs_axis_tdata ),
.wr_en (rs_axis_tvalid ),
.rd_en (r_fifo_data_rden ),
.dout (w_fifo_data_dout ),
.full (w_fifo_data_full ),
.empty (w_fifo_data_empty )
);
FIFO_32X32 FIFO_32X32_len_type (
.clk (i_clk ),
.srst (i_rst ),
.din ({rs_axis_tuser[79:64],rs_axis_tuser[15:0]}),
.wr_en (rs_axis_tlast ),
.rd_en (r_fifo_len_type_rden ),
.dout (w_fifo_len_type_dout ),
.full (w_fifo_len_type_full ),
.empty (w_fifo_len_type_empty )
);
FIFO_8X32 FIFO_8X32_tail_keep (
.clk (i_clk ),
.srst (i_rst ),
.din (rs_axis_tkeep ),
.wr_en (rs_axis_tlast ),
.rd_en (r_fifo_keep_rden ),
.dout (w_fifo_keep_dout ),
.full (w_fifo_keep_full ),
.empty (w_fifo_keep_empty )
);
CRC32_64bKEEP CRC32_64bKEEP_u0(
.i_clk (i_clk ),
.i_rst (i_rst ),
.i_en (r_crc_en ),
.i_data (r_crc_data[63:56] ),
.i_data_1 (r_crc_data[55:48] ),
.i_data_2 (r_crc_data[47:40] ),
.i_data_3 (r_crc_data[39:32] ),
.i_data_4 (r_crc_data[31:24] ),
.i_data_5 (r_crc_data[23:16] ),
.i_data_6 (r_crc_data[15: 8] ),
.i_data_7 (r_crc_data[7 : 0] ),
.o_crc_8 (w_crc_8_big ),
.o_crc_1 (w_crc_1_big ),
.o_crc_2 (w_crc_2_big ),
.o_crc_3 (w_crc_3_big ),
.o_crc_4 (w_crc_4_big ),
.o_crc_5 (w_crc_5_big ),
.o_crc_6 (w_crc_6_big ),
.o_crc_7 (w_crc_7_big )
);
产生XGMII接口的核心代码如下:
c
//r_xgmii_txc还需要考虑最后多加4byte的CRC
always @(posedge i_clk or posedge i_rst)begin
if(i_rst)
r_xgmii_txc <= 8'b1111_1111;
else if(r_fifo_len_type_rden)
r_xgmii_txc <= 8'b1000_0000;
else if(r_pkt_cnt == r_data_len + 3 && r_tail_keep >= 8'b1111_1000)
case (r_tail_keep)
8'b1111_1111 : r_xgmii_txc <= 8'b0001_1111;
8'b1111_1110 : r_xgmii_txc <= 8'b0011_1111;
8'b1111_1100 : r_xgmii_txc <= 8'b0111_1111;
8'b1111_1000 : r_xgmii_txc <= 8'b1111_1111;
default : r_xgmii_txc <= 8'b0000_0000;
endcase
else if(r_pkt_cnt == r_data_len + 2 && r_tail_keep < 8'b1111_1000)
case (r_tail_keep)
8'b1111_0000 : r_xgmii_txc <= 8'b0000_0001;
8'b1110_0000 : r_xgmii_txc <= 8'b0000_0011;
8'b1100_0000 : r_xgmii_txc <= 8'b0000_0111;
8'b1000_0000 : r_xgmii_txc <= 8'b0000_1111;
default : r_xgmii_txc <= 8'b0000_0000;
endcase
else if(r_send_data)
r_xgmii_txc <= 8'b0000_0000;
else
r_xgmii_txc <= 8'b1111_1111;
end
always @(posedge i_clk or posedge i_rst)begin
if(i_rst)
r_send_data <= 'd0;
else if(r_pkt_cnt == r_data_len + 3 && r_tail_keep >= 8'b1111_1000)
r_send_data <= 'd0;
else if(r_pkt_cnt == r_data_len + 2 && r_tail_keep < 8'b1111_1000)
r_send_data <= 'd0;
else if(r_fifo_len_type_rden)
r_send_data <= 'd1;
else
r_send_data <= r_send_data;
end
always @(posedge i_clk or posedge i_rst)begin
if(i_rst)
ro_xgmii_txd <= {8{P_FRAME_IDLE}};
else if(r_pkt_cnt_3d == r_data_len + 2)
case (r_tail_keep_1d)
8'b1111_1111 : ro_xgmii_txd <= {r_xgmii_txd_2d[63: 8],r_crc_result[31:24]};
8'b1111_1110 : ro_xgmii_txd <= {r_xgmii_txd_2d[63:16],r_crc_result[31:16]};
8'b1111_1100 : ro_xgmii_txd <= {r_xgmii_txd_2d[63:24],r_crc_result[31: 8]};
8'b1111_1000 : ro_xgmii_txd <= {r_xgmii_txd_2d[63:32],r_crc_result[31: 0]};
8'b1111_0000 : ro_xgmii_txd <= {r_xgmii_txd_2d[63:40],r_crc_result[31: 0],P_FRAME_END};
8'b1110_0000 : ro_xgmii_txd <= {r_xgmii_txd_2d[63:48],r_crc_result[31: 0],P_FRAME_END,P_FRAME_IDLE};
8'b1100_0000 : ro_xgmii_txd <= {r_xgmii_txd_2d[63:56],r_crc_result[31: 0],P_FRAME_END,P_FRAME_IDLE,P_FRAME_IDLE};
8'b1000_0000 : ro_xgmii_txd <= {r_crc_result[31: 0],P_FRAME_END,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE};
default : ro_xgmii_txd <= {8{P_FRAME_IDLE}};
endcase
else if(r_pkt_cnt_3d == r_data_len + 3 && r_tail_keep >= 8'b1111_1000)
case (r_tail_keep_1d)
8'b1111_1111 : ro_xgmii_txd <= {r_crc_result[23:0],P_FRAME_END,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE};
8'b1111_1110 : ro_xgmii_txd <= {r_crc_result[15:0],P_FRAME_END,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE};
8'b1111_1100 : ro_xgmii_txd <= {r_crc_result[7 :0],P_FRAME_END,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE};
8'b1111_1000 : ro_xgmii_txd <= {P_FRAME_END,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE,P_FRAME_IDLE};
default : ro_xgmii_txd <= {8{P_FRAME_IDLE}};
endcase
else
ro_xgmii_txd <= r_xgmii_txd_2d;
end
三、仿真
模块AXIS_test_module会产生各种不同尾端keep的数据包,用于测试发送端的各种情况,包长为1488byte,间隔为6个时钟周期(AXIS用户端包间隔为6,则XGMII处数据间隔为2,俩者之间相差4),通过查看主机侧网卡接收带宽检查FPGA侧发送是否符合要求。
c
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
rm_axis_tkeep <= 8'hff;
else if(r_send_cnt == P_SEND_LEN - 1 && w_axis_active)
rm_axis_tkeep <= 8'hff;
else if(r_send_cnt == P_SEND_LEN - 2 && w_axis_active)
case (r_pkt_cnt)
0 : rm_axis_tkeep <= 8'b1111_1111;
1 : rm_axis_tkeep <= 8'b1111_1110;
2 : rm_axis_tkeep <= 8'b1111_1100;
3 : rm_axis_tkeep <= 8'b1111_1000;
4 : rm_axis_tkeep <= 8'b1111_0000;
5 : rm_axis_tkeep <= 8'b1110_0000;
6 : rm_axis_tkeep <= 8'b1100_0000;
7 : rm_axis_tkeep <= 8'b1000_0000;
default: rm_axis_tkeep <= 8'b1111_1111;
endcase
else
rm_axis_tkeep <= 8'hff;
end
四、上板测速
接收带宽可达9.9G