进行MAC层编写。
0,协议学习:
mac为设备的物理地址,两个主机之间进行交互,各自具有唯一的物理地址。
- 前导码 :d5_55_55_55_55_55_55_55;
- 目的MAC:目的设备的mac地址;
- 源头MAC:源端设备的mac地址;
- 类型 :ARP:0806;IP:0800;RARP:8035;
- 数据 :46-1500字节,不足需要补足46字节;
- FCS :帧尾,帧校验序列,采用32为CRC校验。
1,MAC整体工程框架
MAC主要包含三个模块。MAC_RX,MAC_TX,CRC_PROCESS。
1.1,MAC_RX
MAC_RX:负责接受10G以太网IP核发送过来的XGMII_RXD,XGMII_RXC,进行处理:
module _10g_mac_rx
#(
parameter P_SOURCE_MAC = 48'h00_00_00_00_00_00,
parameter P_TARGET_MAC = 48'h00_00_00_00_00_00
)
(
input i_clk ,
input i_rst ,
input [ 63:0] i_xgmii_rxd ,
input [ 7:0] i_xgmii_rxc ,
//动态配置mac
input [ 47:0] i_set_source_mac ,
input i_set_source_mac_valid ,
input [ 47:0] i_set_target_mac ,
input i_set_target_mac_valid ,
output o_crc_error ,
output o_crc_check_valid ,
output [ 63:0] m_axis_data ,
output [ 79:0] m_axis_user ,//r_source_mac[15:0],r_type
output [ 7:0] m_axis_keep ,
output m_axis_last ,
output m_axis_valid
);
包括:
1.1.1,开始报文位置确定
64/66编码的开始帧有两种形式,在开头,与在中间。需要对这两种进行区分:
//两个不同的开始位置。
always @(posedge i_clk) begin
if (i_rst) begin
r_sof <= 'd0;
r_sof_local <= 'd0;
end
else if(r_i_xgmii_rxc[7] && r_i_xgmii_rxd[63:56] == 8'hfb) begin
r_sof <= 'd1;
r_sof_local <= 8'b1000_0000;
end
else if(r_i_xgmii_rxc[3] && r_i_xgmii_rxd[31:24] == 8'hfb) begin
r_sof <= 'd1;
r_sof_local <= 8'b0000_1000;
end
else begin
r_sof <= 'd0;
r_sof_local <= r_sof_local ;
end
end
在最高位开始,8‘b1000_0000
在中间位开始,8‘b0000_1000
1.1.2,前导码验证
根据前面开始位置的不同进行不用的验证格式:
//前导码校验
always @(posedge i_clk) begin
if (i_rst) begin
r_rec_comma <= 'd0;
end
else if(r_eof) begin
r_rec_comma <= 'd0;
end
else if(r_sof && r_sof_local == 8'b1000_0000
&& ((r_i_xgmii_rxd_1d[55: 0] == {7{8'h55}}) && r_i_xgmii_rxc_1d[6:0] == {7{1'b0}})
&& (r_i_xgmii_rxd[63:56] == 8'hd5 && r_i_xgmii_rxc[0] == 1'b0 ) ) begin//检验前导码:55_55_55_55_55_55_55_d5
r_rec_comma <= 'd1;
end
else if(r_sof && r_sof_local == 8'b0000_1000
&& (r_i_xgmii_rxd_1d[23:0] == {3{8'h55}} && r_i_xgmii_rxc_1d[2:0] == {3{1'b0}})
&& (r_i_xgmii_rxd[63:24] == 48'h55_55_55_55_d5) && r_i_xgmii_rxc[7:3] == {5{1'b0}}) begin
r_rec_comma <= 'd1;
end
else begin
r_rec_comma <= r_rec_comma;
end
end
1.1.3,进行MAC地址校验与抓取
确保接受报文的目的MAC为本板卡的MAC 。
也是根据开始报文的不同来确定的不同抓取方式
always @(posedge i_clk) begin
if (i_rst) begin
r_target_mac <= 'd0;
end
else if(r_sof_local == 8'b1000_0000 && /*r_sof &&*/ r_rec_cnt == 1) begin
r_target_mac <= r_i_xgmii_rxd_1d[55:8];
end
else if(r_sof_local == 8'b0000_1000 && /*r_sof &&*/ r_rec_cnt == 1) begin
r_target_mac <= {r_i_xgmii_rxd_1d[23:0],r_i_xgmii_rxd[63:40]};
end
else begin
r_target_mac <= r_target_mac;
end
end
always @(posedge i_clk) begin
if (i_rst) begin
r_source_mac <= 'd0;
end
else if(r_sof_local == 8'b1000_0000 && /*r_sof &&*/ r_rec_cnt == 1) begin
r_source_mac <= {r_i_xgmii_rxd_1d[7:0],r_i_xgmii_rxd[63:24]};
end
else if(r_sof_local == 8'b0000_1000 && r_rec_cnt == 2) begin
r_source_mac <= {r_i_xgmii_rxd_1d[39:0],r_i_xgmii_rxd[63:56]};
end
else begin
r_source_mac <= r_source_mac;
end
end
//0806 0800
always @(posedge i_clk) begin
if (i_rst) begin
r_type <= 'd0;
end
else if(r_sof_local == 8'b1000_0000 && r_rec_cnt == 2) begin
r_type <= r_i_xgmii_rxd_1d[23:8];
end
else if(r_sof_local == 8'b0000_1000 && r_rec_cnt == 3) begin
r_type <= r_i_xgmii_rxd_1d[55:40];
end
else begin
r_type <= r_type;
end
end
check_mac,校验mac是否一致
/************** check_mac *********************/
always @(posedge i_clk) begin
if (i_rst) begin
r_mac_check <= 'd0;
end
else if( r_rec_cnt == 2 && r_target_mac == 48'hff_ff_ff_ff_ff_ff ) begin //广播报文
r_mac_check <= 'd1;
end
else if( r_rec_cnt == 2 && r_target_mac == r_i_set_source_mac) begin
r_mac_check <= 'd1;
end
else if( r_rec_cnt == 2 && !r_target_mac != r_i_set_source_mac) begin
r_mac_check <= 'd0;
end
else begin
r_mac_check <= r_mac_check;
end
end
1.1.4,输出模块数据
mac_rx在前面完成了开始位置的确定,就可以决定后续的数据组成格式,尾端keep组成。
输出数据我们不用详细的划分,使用一套规则即可。数据的有效交个valid与last,keep来进行详细判定。在这里尝试过将数据直接划分很清楚,但是情况种类太多了。
1,输出数据
always @(posedge i_clk) begin
if (i_rst) begin
r_m_axis_data <= 'd0;
end
else if(r_sof_local == 8'b1000_0000 && r_data_run) begin
r_m_axis_data <= {r_i_xgmii_rxd_1d[7:0],r_i_xgmii_rxd[63:8]};
end
else if(r_sof_local == 8'b0000_1000 && r_data_run) begin
r_m_axis_data <= {r_i_xgmii_rxd_1d[39:0],r_i_xgmii_rxd[63:40]};
end
else begin
r_m_axis_data <= r_m_axis_data;
end
end
2,keep与last
尾端keep与last的判决情况相同,数据的组成是通过r_xgmii_rxd与r_xgmii_rxd_1d共同组成的。我们将MAC的协议都抽取出来之后,就是完整的数据。但是我们进行了字节对齐,用于方便后续的开发。实际上整体的总目标就是将数据抽取出来之后,进行字节对齐。但因为尾端数据进行组帧时,因各种因素导致last与keep分情况来讨论(这相比于分数据要好很多!)
这个部分是我第一次接触到尾端重组,花费了很长时间才调好。后续的所有模块几乎都需要这样进行重组。然后一边改动,一边跑仿真慢慢确定了。大家学习的时候最好按照如下步骤:
- 确定数据组成形式
- 用纸画出一些短数据的组成与尾端keep的划分,进行初步尝试
- 实际上尾端keep与last信号根据输入数据的尾端有效keep只会有一个周期的差别。(1,当前周期刚好可以表示;2,在前一个周期数据就处理完了,本周期的数据都不关心,即提前一个周期)。
- 编写8种类last_keep进行仿真验证,可以用一个task来编写,我放在下面了
testbench_task_last_keep
initial begin
w_xgmii_rxd <= {8{8'h07}};
w_xgmii_rxc <= {8{1'b1}} ;
wait(!w_xgmii_clk_rst);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b1000_0000);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0100_0000);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0010_0000);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0001_0000);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0000_1000);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0000_0100);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0000_0010);
repeat(10) @(posedge w_xgmii_clk);
xgmii_rxd_send_sof_1000_0000(8'b0000_0001);
#1000;
$stop;
end
task xgmii_rxd_send_sof_1000_0000( input [7:0] end_sof_local);
begin:xgmii_rxd_send_task
integer i;
w_xgmii_rxd <= {8{8'h07}};
w_xgmii_rxc <= {8{1'b1}} ;
@(posedge w_xgmii_clk);
w_xgmii_rxd <= 64'hfb555555_55555555;
w_xgmii_rxc <= 8'b1000_0000 ;
@(posedge w_xgmii_clk);
w_xgmii_rxd <= 64'hd5ffffff_ffffff01;
w_xgmii_rxc <= 8'b0000_0000 ;
@(posedge w_xgmii_clk);
w_xgmii_rxd <= 64'h02030405_060806_01;
w_xgmii_rxc <= 8'b0000_0000 ;
@(posedge w_xgmii_clk);
w_xgmii_rxd <= 64'h02030405_06070809;
w_xgmii_rxc <= 8'b0000_0000 ;
@(posedge w_xgmii_clk);
// w_xgmii_rxd <= 64'h0a0b0c0d_0e0fa0a1;
w_xgmii_rxd <= 64'h0a0b0c0d_29ADAED2; //测试crc校验
w_xgmii_rxc <= 8'b0000_0000 ;
@(posedge w_xgmii_clk);
w_xgmii_rxd <= 64'hfdfdfdfd_fdfdfdfd;
w_xgmii_rxc <= end_sof_local ;
@(posedge w_xgmii_clk);
w_xgmii_rxd <= {8{8'h07}};
w_xgmii_rxc <= {8{1'b1}} ;
end
endtask
keep last
else if(r_sof_local == 8'b0000_1000 && w_r_eof && (w_r_eof_local[7] || w_r_eof_local[6] || w_r_eof_local[5] || w_r_eof_local[4])) begin
case (w_r_eof_local)
8'b1000_0000: r_m_axis_keep <= 8'b1111_1000;//提前一个周期
8'b0100_0000: r_m_axis_keep <= 8'b1111_1100;//提前一个周期
8'b0010_0000: r_m_axis_keep <= 8'b1111_1110;//提前一个周期
8'b0001_0000: r_m_axis_keep <= 8'b1111_1111;//提前一个周期
8'b0000_1000: r_m_axis_keep <= 8'b1000_0000;
8'b0000_0100: r_m_axis_keep <= 8'b1100_0000;
8'b0000_0010: r_m_axis_keep <= 8'b1110_0000;
8'b0000_0001: r_m_axis_keep <= 8'b1111_0000;
default : r_m_axis_keep <= 8'b1111_1111;
endcase
end
else if(r_sof_local == 8'b0000_1000 && r_eof && !(r_eof_local[7] || r_eof_local[6] || r_eof_local[5] || r_eof_local[4])) begin
case (r_eof_local)
8'b1000_0000: r_m_axis_keep <= 8'b1111_1000;//提前一个周期
8'b0100_0000: r_m_axis_keep <= 8'b1111_1100;//提前一个周期
8'b0010_0000: r_m_axis_keep <= 8'b1111_1110;//提前一个周期
8'b0001_0000: r_m_axis_keep <= 8'b1111_1111;//提前一个周期
8'b0000_1000: r_m_axis_keep <= 8'b1000_0000;
8'b0000_0100: r_m_axis_keep <= 8'b1100_0000;
8'b0000_0010: r_m_axis_keep <= 8'b1110_0000;
8'b0000_0001: r_m_axis_keep <= 8'b1111_0000;
default : r_m_axis_keep <= 8'b1111_1111;
endcase
end
else if(r_sof_local==8'b1000_0000 && w_r_eof) begin
r_m_axis_last <= 'd1;
end
else if(r_sof_local == 8'b0000_1000 && w_r_eof && (w_r_eof_local[7] || w_r_eof_local[6] || w_r_eof_local[5] || w_r_eof_local[4])) begin
r_m_axis_last <= 'd1;
end
else if(r_sof_local == 8'b0000_1000 && r_eof && !(r_eof_local[7] || r_eof_local[6] || r_eof_local[5] || r_eof_local[4])) begin
r_m_axis_last <= 'd1;
end
1.1.5,CRC问题
mac中还需要对最后的FCS进行校验,这个地方是更加的恐怖!!!
我们按照前面的流程,需要对接受到的数据,从目的MAC开始到FCS之前进行本地的CRC计算,与接受到的报文中的FCS进行比较。这里又设计到了如何将要进行的CRC数据提取出来,同时,CRC的尾端任然需要尾端crc_keep的处理。
还有,我们在千兆网中的CRC校验模块是参考的这个:CRC生成verilog
但是这个是8bit的数据进行的CRC,我们需要的是同时进行64bit数据的crc,而且还需要根据尾端keep输出最终结果。
crc_64_process:
//消耗一个时钟周期进行crc计算
CRC32_64bKEEP CRC32_64bKEEP_inst(
.i_clk (i_clk ),
.i_rst (i_rst ),
.i_en (r_crc_en ),
.i_data_1 (r_crc_data[63:56] ),
.i_data_2 (r_crc_data[55:48] ),
.i_data_3 (r_crc_data[47:40] ),
.i_data_4 (r_crc_data[39:32] ),
.i_data_5 (r_crc_data[31:24] ),
.i_data_6 (r_crc_data[23:16] ),
.i_data_7 (r_crc_data[15:8] ),
.i_data_8 (r_crc_data[7 :0] ),
.o_crc_1 (w_crc_result_1 ),//8‘b1000_0000
.o_crc_2 (w_crc_result_2 ),//8'b1100_0000
.o_crc_3 (w_crc_result_3 ),//8'b1110_0000
.o_crc_4 (w_crc_result_4 ),//8'b1111_0000
.o_crc_5 (w_crc_result_5 ),//8'b1111_1000
.o_crc_6 (w_crc_result_6 ),//8'b1111_1100
.o_crc_7 (w_crc_result_7 ),//8'b1111_1110
.o_crc_8 (w_crc_result_8 ) //8'bb111_1111
);
仿真如下:
我们需要将自身计算出来的CRC与接受报文中携带的CRC进行最后的比较,得到crc_check信号,用与后续的CRC_PROCESS模块。
always @(posedge i_clk) begin
if (i_rst) begin
r_o_crc_error <= 'd0;
r_o_crc_check_valid <= 'd0;
end
else if(r_pe_crc_valid && r_crc_out_valid) begin //计算完之后进行比较
r_o_crc_error <= (r_pe_crc_result == r_oil_crc_data) ? 1'b0 : 1'b1;
r_o_crc_check_valid <= 'd1;
end
else begin
r_o_crc_error <= 'd0;
r_o_crc_check_valid <= 'd0;
end
end
1.2,CRC_PROCESS
我们在MAC_RX处理完数据之后,需要进行CRC的处理。但是,因为CRC需要计算,我们将数据字节对齐之后,当前数据的CRC计算有效才刚刚得到。
所以需要一个模块来专门处理,实现如下功能:
- 将得到的数据存储起来
- 如果之后该段数据的crc_error为低,代表数据正确,那么输出该段数据,如果crc_error为高,代表数据错误,丢弃该段数据。
要实现如上功能需要使用多个双端口ram存入数据,数据长度,类型,尾端keep,使用多个fifo存入上述ram的写端口地址,而丢弃数据的方法就是将写入地址回溯至上一段数据写入的初始地址。
//存放输入进来的数据
BRAM_CRC_Process_data_64x256 bram_data_u0_inst (
.clka (i_clk ),// input wire clka
.ena (r_s_axis_valid ),// input wire ena
.wea (r_s_axis_valid ),// input wire [0 : 0] wea
.addra (r_bram_data_u0_a_addr ),// input wire [7 : 0] addra
.dina (r_s_axis_data ),// input wire [63 : 0] dina
.clkb (i_clk ),// input wire clkb
.enb (r_bram_data_u0_b_en ),// input wire enb
.addrb (r_bram_data_u0_b_addr ),// input wire [7 : 0] addrb
.doutb (w_bram_data_u0_b_data ) // output wire [63 : 0] doutb
);
//输入数据的长度
BRAM_CRC_Process_len bram_len_u1_inst (
.clka (i_clk ),// input wire clka
.ena (r_s_axis_last ),// input wire ena
.wea (r_s_axis_last ),// input wire [0 : 0] wea
.addra (r_bram_len_u1_a_addr ),// input wire [4 : 0] addra
.dina (r_s_axis_user[79-:16] ),// input wire [15 : 0] dina
.clkb (i_clk ),// input wire clkb
.enb (r_bram_len_u1_b_en ),// input wire enb
.addrb (r_bram_len_u1_b_addr ),// input wire [4 : 0] addrb
.doutb (w_bram_len_u1_b_data ) // output wire [15 : 0] doutb
);
//keep信号
BRAM_CRC_Process_keep bram_keep_u2_inst (
.clka (i_clk ),// input wire clka
.ena (r_s_axis_last ),// input wire ena
.wea (r_s_axis_last ),// input wire [0 : 0] wea
.addra (r_bram_keep_u2_a_addr ),// input wire [4 : 0] addra
.dina (r_s_axis_keep ),// input wire [7 : 0] dina
.clkb (i_clk ),// input wire clkb
.enb (r_bram_keep_u2_b_en ),// input wire enb
.addrb (r_bram_keep_u2_b_addr ),// input wire [4 : 0] addrb
.doutb (w_bram_keep_u2_b_data ) // output wire [7 : 0] doutb
);
//user信号
BRAM_CRC_Process_user bram_user_u3_inst (
.clka (i_clk ),// input wire clka
.ena (r_s_axis_last ),// input wire ena
.wea (r_s_axis_last ),// input wire [0 : 0] wea
.addra (r_bram_user_u3_a_addr ),// input wire [4 : 0] addra
.dina (r_s_axis_user ),// input wire [79 : 0] dina
.clkb (i_clk ),// input wire clkb
.enb (r_bram_user_u3_b_en ),// input wire enb
.addrb (r_bram_user_u3_b_addr ),// input wire [4 : 0] addrb
.doutb (w_bram_user_u3_b_data ) // output wire [79 : 0] doutb
);
//输入进的crc_error进行缓存
FIFO_CRC_Process_crc_check fifo_crc_check_u0_inst (
.rst (i_rst ),// input wire rst
.wr_clk (i_clk ),// input wire wr_clk
.rd_clk (i_clk ),// input wire rd_clk
.din (i_crc_error ),// input wire [0 : 0] din
.wr_en (i_crc_check_valid ),// input wire wr_en
.rd_en (r_fifo_crc_rd_en ),// input wire rd_en
.dout (w_fifo_crc_u0_dout ),// output wire [0 : 0] dout
.full (w_fifo_crc_u0_full ),// output wire full
.empty (w_fifo_crc_u0_empty ) // output wire empty
);
//存放数据的初始地址
FIFO_CRC_Process_init_addr fifo_data_init_addr_u1_inst (
.rst (i_rst ),// input wire rst
.wr_clk (i_clk ),// input wire wr_clk
.rd_clk (i_clk ),// input wire rd_clk
.din ({8'd0,r_data_init_addr} ),// input wire [15 : 0] din
.wr_en (!r_s_axis_valid && r_s_axis_valid_1d),// input wire wr_en
.rd_en (w_fifo_init_addr_rd_en ),// input wire rd_en
.dout (w_data_init_addr_dout ),// output wire [15 : 0] dout
.full ( ),// output wire full
.empty ( ) // output wire empty
);
//存放长度的初始地址
FIFO_CRC_Process_init_addr fifo_len_init_addr_u2_inst (
.rst (i_rst ),// input wire rst
.wr_clk (i_clk ),// input wire wr_clk
.rd_clk (i_clk ),// input wire rd_clk
.din ({11'd0,r_len_init_addr} ),// input wire [15 : 0] din
.wr_en (r_s_axis_last_1d ),// input wire wr_en
.rd_en (w_fifo_init_addr_rd_en ),// input wire rd_en
.dout (w_len_init_addr_dout ),// output wire [15 : 0] dout
.full ( ),// output wire full
.empty ( ) // output wire empty
);
CRC_PROCESS
module CRC_Process
(
input i_clk ,
input i_rst ,
input i_crc_error ,
input i_crc_check_valid ,
input [ 63:0] s_axis_data ,
input [ 79:0] s_axis_user ,//r_source_mac[15:0],r_type
input [ 7:0] s_axis_keep ,
input s_axis_last ,
input s_axis_valid ,
output [ 63:0] m_axis_data ,
output [ 79:0] m_axis_user ,//r_source_mac[15:0],r_type
output [ 7:0] m_axis_keep ,
output m_axis_last ,
output m_axis_valid
);
仿真波形如下,总共八段数据,第一个CRC检测正确,其余均错误。所以最后只输出第一段数据。
1.3,MAC_TX
在了解了MAC_RX之后,MAC_TX就是逆向。进行数据的组帧,我们的开始位置统一使用8‘b1000_0000。方便后续的开发。
module _10g_mac_tx
#(
parameter P_SOURCE_MAC = 48'h00_00_00_00_00_00,
parameter P_TARGET_MAC = 48'h00_00_00_00_00_00
)
(
input i_clk ,
input i_rst ,
//动态配置mac
input [ 47:0] i_set_source_mac ,
input i_set_source_mac_valid ,
input [ 47:0] i_set_target_mac ,
input i_set_target_mac_valid ,
input [ 63:0] s_axis_data ,
input [ 79:0] s_axis_user ,//len(16) + source_mac(48) + type(16)
input [ 7:0] s_axis_keep ,
input s_axis_last ,
input s_axis_valid ,
output [ 63:0] o_xgmii_txd ,
output [ 7:0] o_xgmii_txc
);
待发送数据:
组好帧之后发送:
整体模块
为了方便开发,采取AXIS_STREAM流形式开发,_10g_mac_module整体模块如下:
module _10g_mac_module
#(
parameter P_SOURCE_MAC = 48'h00_00_00_00_00_00,
parameter P_TARGET_MAC = 48'h00_00_00_00_00_00
)
(
input i_xgmii_clk ,
input i_xgmii_rst ,
input [ 63:0] i_xgmii_rxd ,
input [ 7:0] i_xgmii_rxc ,
output [ 63:0] o_xgmii_txd ,
output [ 7:0] o_xgmii_txc ,
//动态配置mac
input [ 47:0] i_set_source_mac ,
input i_set_source_mac_valid ,
input [ 47:0] i_set_target_mac ,
input i_set_target_mac_valid ,
output [ 63:0] m_axis_data ,
output [ 79:0] m_axis_user ,//24'd0,6'dsource_mac,2'dtype
output [ 7:0] m_axis_keep ,
output m_axis_last ,
output m_axis_valid ,
input [ 63:0] s_axis_data ,
input [ 79:0] s_axis_user ,//24'd0,6'dsource_mac,2'dtype
input [ 7:0] s_axis_keep ,
input s_axis_last ,
input s_axis_valid
);
/************** mac_rx *********************/
wire w_mac2crc_crc_error ;
wire w_mac2crc_crc_check_valid ;
wire [ 63:0] w_mac_2_crc_axis_data ;
wire [ 79:0] w_mac_2_crc_axis_user ;
wire [ 7:0] w_mac_2_crc_axis_keep ;
wire w_mac_2_crc_axis_last ;
wire w_mac_2_crc_axis_valid ;
_10g_mac_rx
#(
.P_SOURCE_MAC (P_SOURCE_MAC ),
.P_TARGET_MAC (P_TARGET_MAC )
)
_10g_mac_rx_inst
(
.i_clk (i_xgmii_clk ),
.i_rst (i_xgmii_rst ),
.i_xgmii_rxd (i_xgmii_rxd ),
.i_xgmii_rxc (i_xgmii_rxc ),
//动态配置mac
.i_set_source_mac (i_set_source_mac ),
.i_set_source_mac_valid (i_set_source_mac_valid ),
.i_set_target_mac (i_set_target_mac ),
.i_set_target_mac_valid (i_set_target_mac_valid ),
.o_crc_error (w_mac2crc_crc_error ),
.o_crc_check_valid (w_mac2crc_crc_check_valid ),
.m_axis_data (w_mac_2_crc_axis_data ),
.m_axis_user (w_mac_2_crc_axis_user ),//r_len[15:0],r_source_mac[47:0],r_type[15:0]
.m_axis_keep (w_mac_2_crc_axis_keep ),
.m_axis_last (w_mac_2_crc_axis_last ),
.m_axis_valid (w_mac_2_crc_axis_valid )
);
_10g_mac_tx
#(
.P_SOURCE_MAC (P_SOURCE_MAC ),
.P_TARGET_MAC (P_TARGET_MAC )
)
_10g_mac_tx_inst
(
.i_clk (i_xgmii_clk ),
.i_rst (i_xgmii_rst ),
//动态配置mac
.i_set_source_mac (i_set_source_mac ),
.i_set_source_mac_valid (i_set_source_mac_valid ),
.i_set_target_mac (i_set_target_mac ),
.i_set_target_mac_valid (i_set_target_mac_valid ),
.s_axis_data (s_axis_data ),
.s_axis_user (s_axis_user ),
.s_axis_keep (s_axis_keep ),
.s_axis_last (s_axis_last ),
.s_axis_valid (s_axis_valid ),
.o_xgmii_txd (o_xgmii_txd ),
.o_xgmii_txc (o_xgmii_txc )
);
CRC_Process CRC_Process_inst
(
.i_clk (i_xgmii_clk ),
.i_rst (i_xgmii_rst ),
.i_crc_error (w_mac2crc_crc_error ),
// .i_crc_error (1'b0 ),
.i_crc_check_valid (w_mac2crc_crc_check_valid ),
.s_axis_data (w_mac_2_crc_axis_data ),
.s_axis_user (w_mac_2_crc_axis_user ),//r_source_mac[15:0],r_type
.s_axis_keep (w_mac_2_crc_axis_keep ),
.s_axis_last (w_mac_2_crc_axis_last ),
.s_axis_valid (w_mac_2_crc_axis_valid ),
.m_axis_data (m_axis_data ),
.m_axis_user (m_axis_user ),//r_source_mac[15:0],r_type
.m_axis_keep (m_axis_keep ),
.m_axis_last (m_axis_last ),
.m_axis_valid (m_axis_valid )
);
使用mac_tx得到额txd进行loopback:
仿真波形如下:
test1:
发送数据;
回环解包数据:
test2:
发送数据;回环解包数据:
可以看到根据rx输出的keep信号。数据是完全一致的。多余的数据为以太网传输中产生的FCS。
2,上板验证
1,整体流程:
为了测试是否到达10G网速,编写了一个接近满速率的发送模块,每隔两个gap发送1488个数据。
module mac_speed_test
#(
parameter P_LAST_KEEP = 8'b1111_1111 ,
parameter P_SOURCE_MAC = 48'h00_00_00_00_00_00,
parameter P_TARGET_MAC = 48'h00_00_00_00_00_00
)
(
input i_clk ,
input i_rst ,
input [ 63:0] s_axis_data ,
input [ 79:0] s_axis_user ,
input [ 7:0] s_axis_keep ,
input s_axis_valid ,
input s_axis_last ,
output [ 63:0] m_axis_data ,
output [ 79:0] m_axis_user ,
output [ 7:0] m_axis_keep ,
output m_axis_valid ,
output m_axis_last
);
localparam P_SEND_LEN = 16'd186 ;//8*186 == 1488
localparam P_SEND_GAP = 2 ;//2个clk(12.8ns) 帧间隔:12byte
帧间隔参考:以太网帧间隔
间隔时间为12byte。按照64/66编码,只需要两个clk即可。而以太网MAC的数据段范围:46-1500。但是必须是8的倍数。选择1488或者1496。本次选择1488,1496等待后续测试。
仿真如下:进行满速率测试
整体框架:
2,上板抓取:
(只判断CRC校验功能与慢速率功能,因为现在只实现了MAC层)
等待网卡到达。。。
(12.13更新,网卡已到达)
1,实物链接图:
需要购买万兆网卡。
2,在上板中发现的错误:
1,万兆网的前导码是6个55+1个D5。与千兆网相比减少了一个55。
这是IP传出的XGMII_rx信号,可以看到只有6个55:
因为我的mac代码是按照7个55+1个D5的处理的,所以对rxd,rxc进行了处理,添加了一个55。如下图所示:这样可以避免重新编写mac_rx层(提示:mac_tx发送给XGMII_TXD的信号前导码也是6个55+1个D5。也要注意!!!)
2。CRC校验,需要对生成模块的CRC_DATA进行endian,不然会发送接受都错误!
可以看到报文中的CRC与FPGA计算产生的CRC是大小端相反的。这里修改了之后就可以在PC机中用wireshark抓取到接受报文。
3,测速:
在9.8Gbps,9.9Gbps上下浮动
wireshark抓取: