万兆以太网MAC设计(5)MAC_TX模块设计以及上板带宽测试

发布于:2024-04-23 ⋅ 阅读:(18) ⋅ 点赞:(0)

前言

MAC_RX的设计暂时告一段落,本节将开始进行MAC_TX的设计。

一、模块功能

  1. 接收上层用户的AXIS数据,将其转换为XGMII进接口的数据发送给IP核。
  2. 可接受AXIS数据流,可支持数据包之间的间隔最小为一个时钟周期
  3. 目的MAC以及源MAC等参数可动态配置
  4. 流控,万兆以太网帧间隔为9.6ns,用户时钟频率为156.25Mhz,周期为6.4ns,因此XGMII接口数据之前至少间隔2个时钟周期

模块接口如下:

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         
);

二、实现方式

  1. 将接收到的AXIS数据先存入FIFO,将包头组织完毕后从FIFO当中读取数据进行填充
  2. 组帧同时要进行CRC计算并且在末尾填充,需要注意,在尾端填充CRC数据时,根据尾端keep信号有多种情况,会存在数据额外需要一个时钟周期进行传输CRC数据。

包含子模块如下:

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接口的核心代码如下:

//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侧发送是否符合要求。

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
在这里插入图片描述