10G MAC层设计系列-(2)MAC RX模块

一、概述

MAC RX模块的需要进行解码、对齐、CRC校验。

因为在空闲的时候10G PCS/PMA会一直向外吐空闲符(x07)所以需要根据开始符、结束符将有效数据从码流中截取,也就是解码。

因为开始字符的所在位置有两种形式,而结束字符的位置不确定,所以需要根据开始字符、结束字符的位置将数据进行对齐。

在将数据对齐的同时,还需要将获取到的有效数据进行CRC校验,这里就需要设计64bit CRC-32校验。
开始、结束、空闲、错误 控制字符

二、具体实现

1、检测开始、结束字符的位置

复制代码
assign w_sof = (ri_xgmii_rxc[7] && ri_xgmii_rxd[63:56] == 8'hFB) ||
               (ri_xgmii_rxc[3] && ri_xgmii_rxd[31:24] == 8'hFB) ;
assign w_sof_local = (ri_xgmii_rxc[7] && ri_xgmii_rxd[63:56] == 8'hFB) ? 0: 1;
assign w_eof = (ri_xgmii_rxc[0] && ri_xgmii_rxd[7:  0] == 8'hFE) ||
               (ri_xgmii_rxc[1] && ri_xgmii_rxd[15: 8] == 8'hFE) ||
               (ri_xgmii_rxc[2] && ri_xgmii_rxd[23:16] == 8'hFE) ||
               (ri_xgmii_rxc[3] && ri_xgmii_rxd[31:24] == 8'hFE) ||
               (ri_xgmii_rxc[4] && ri_xgmii_rxd[39:32] == 8'hFE) ||
               (ri_xgmii_rxc[5] && ri_xgmii_rxd[47:40] == 8'hFE) ||
               (ri_xgmii_rxc[6] && ri_xgmii_rxd[55:48] == 8'hFE) ||
               (ri_xgmii_rxc[7] && ri_xgmii_rxd[63:56] == 8'hFE) ;

assign w_eof_local = (ri_xgmii_rxc[1] && ri_xgmii_rxd[15: 8] == 8'hFE) ? 6 :
                     (ri_xgmii_rxc[2] && ri_xgmii_rxd[23:16] == 8'hFE) ? 5 :
                     (ri_xgmii_rxc[3] && ri_xgmii_rxd[31:24] == 8'hFE) ? 4 :
                     (ri_xgmii_rxc[4] && ri_xgmii_rxd[39:32] == 8'hFE) ? 3 :
                     (ri_xgmii_rxc[5] && ri_xgmii_rxd[47:40] == 8'hFE) ? 2 :
                     (ri_xgmii_rxc[6] && ri_xgmii_rxd[55:48] == 8'hFE) ? 1 :
                     (ri_xgmii_rxc[7] && ri_xgmii_rxd[63:56] == 8'hFE) ? 0 :
                     7;

2、获取目的MAC、源MAC、帧类型

复制代码
//获取目的MAC
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_target_mac <= 'd0;  
    else
        if(r_sof_local == 0 && r_cnt == 1)
            r_target_mac <= ri_xgmii_rxd_ff1[55:8];
        else if(r_sof_local == 1 && r_cnt == 1)
            r_target_mac <= {ri_xgmii_rxd_ff1[23:0],ri_xgmii_rxd[63:40]};
        else
            r_target_mac <= r_target_mac;
end
//获取源MAC
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_source_mac <= 'd0;
    else
        if(r_sof_local == 0 && r_cnt == 1)
            r_source_mac <= {ri_xgmii_rxd_ff1[7:0],ri_xgmii_rxd[63:24]};
        else if(r_sof_local == 1 && r_cnt == 2)
            r_source_mac <= {ri_xgmii_rxd_ff1[39:0],ri_xgmii_rxd[63:56]};
        else
            r_source_mac <= r_source_mac;
end
//获取帧类型
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_type <= 'd0; 
    else
        if(r_sof_local == 0 && r_cnt == 2)
            r_type <= ri_xgmii_rxd_ff1[23:8];
        else if(r_sof_local == 1 && r_cnt == 3)
            r_type <= ri_xgmii_rxd_ff1[55:40];
        else
            r_type <= r_type;
end

这里需要了解一下标准以太网帧的帧格式

在提取MAC字段之前需要去除前导码:56'h55555555_555555,SFD:8'hD5。

3、对齐数据

根据开始字符的位置对齐数据

复制代码
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        rm_axis_data <= 'd0;
    else
        if(r_sof_local == 0 && r_run)
            rm_axis_data <= {ri_xgmii_rxd_ff1[7:0],ri_xgmii_rxd[63:8]};
        else if(r_sof_local == 1 && r_run)
            rm_axis_data <= {ri_xgmii_rxd_ff1[39:0],ri_xgmii_rxd[63:40]};
        else
            rm_axis_data <= 'd0;
end

因为使用的AXI-Stream接口,所以需要根据结束字符的位置处理最后一次传输的KEEP信号、Valid信号、Last信号

复制代码
//keep信号处理!!!!
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        rm_axis_keep <= 'd0;
    else
        if(r_run && !r_run_ff1)
            rm_axis_keep <= 8'b1111_1111;
        else if(rm_axis_last)
            rm_axis_keep <= 'd0;
        else if(r_sof_local == 0 && w_eof)
            case(w_eof_local)
                0           : rm_axis_keep <= 8'b1000_0000;
                1           : rm_axis_keep <= 8'b1100_0000;
                2           : rm_axis_keep <= 8'b1110_0000;
                3           : rm_axis_keep <= 8'b1111_0000;
                4           : rm_axis_keep <= 8'b1111_1000;
                5           : rm_axis_keep <= 8'b1111_1100;
                6           : rm_axis_keep <= 8'b1111_1110;
                7           : rm_axis_keep <= 8'b1111_1111;
                default     : rm_axis_keep <= 8'b1111_1111;
            endcase
        else if(r_sof_local == 1 && w_eof && w_eof_local <= 3)
            case(w_eof_local)
                0           : rm_axis_keep <= 8'b1111_1000;
                1           : rm_axis_keep <= 8'b1111_1100;
                2           : rm_axis_keep <= 8'b1111_1110;
                3           : rm_axis_keep <= 8'b1111_1111;

                4           : rm_axis_keep <= 8'b1000_0000;
                5           : rm_axis_keep <= 8'b1100_0000;
                6           : rm_axis_keep <= 8'b1110_0000;
                7           : rm_axis_keep <= 8'b1111_0000;
                default     : rm_axis_keep <= 8'b1111_1111;
            endcase 
        else if(r_sof_local == 1 && r_eof && r_eof_local >= 4)
            case(r_eof_local)
                0           : rm_axis_keep <= 8'b1111_1000;
                1           : rm_axis_keep <= 8'b1111_1100;
                2           : rm_axis_keep <= 8'b1111_1110;
                3           : rm_axis_keep <= 8'b1111_1111;

                4           : rm_axis_keep <= 8'b1000_0000;
                5           : rm_axis_keep <= 8'b1100_0000;
                6           : rm_axis_keep <= 8'b1110_0000;
                7           : rm_axis_keep <= 8'b1111_0000;
                default     : rm_axis_keep <= 8'b1111_1111;
            endcase         
        else
            rm_axis_keep <= rm_axis_keep;
end

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        rm_axis_last <= 1'b0;
    else
        if(r_sof_local == 0 && w_eof)
            rm_axis_last <= 1'b1;
        else if(r_sof_local == 1 && w_eof && w_eof_local <= 3)
            rm_axis_last <= 1'b1;
        else if(r_sof_local == 1 && r_eof && r_eof_local >= 4)
            rm_axis_last <= 1'b1;
        else 
            rm_axis_last <= 1'b0;

end
///valid信号,用r_run的上升沿判断数据开始
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        rm_axis_valid <= 1'b0;
    else 
        if(rm_axis_last)
            rm_axis_valid <= 1'b0;
        else if(r_run && !r_run_ff1)
            rm_axis_valid <= 1'b1;
        else
            rm_axis_valid <= rm_axis_valid;
end

4、CRC校验

在进行字节对齐的过程中,需要进行CRC校验,在此过程中使用的标准以太网的CRC-32校验。

因为校验开始的位置是从目的MAC开始的,因此需要从目的MAC字段对齐数据。

复制代码
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_crc_data <= 'd0;
    else
        if(r_sof_local == 0)
            if(r_sof_ff2)
                r_crc_data <= {ri_xgmii_rxd_ff2[55:0],ri_xgmii_rxd_ff1[63:56]};
            else if(r_eof || r_eof_ff1)
                r_crc_data <= {ri_xgmii_rxd_ff2[55:0],ri_xgmii_rxd_ff1[63:56]};
            else if(r_run_ff1)
                r_crc_data <= {ri_xgmii_rxd_ff2[55:0],ri_xgmii_rxd_ff1[63:56]};
            else 
                r_crc_data <= 'd0; 
        else 
            if(r_sof_ff2)
                r_crc_data <= {ri_xgmii_rxd_ff2[23:0],ri_xgmii_rxd_ff1[63:24]};
            else if(r_eof || r_eof_ff1)
                r_crc_data <= {ri_xgmii_rxd_ff2[23:0],ri_xgmii_rxd_ff1[63:24]};
            else if(r_run)
                r_crc_data <= {ri_xgmii_rxd_ff2[23:0],ri_xgmii_rxd_ff1[63:24]};
            else
                r_crc_data <= 'd0;
end

在此过程中需要一个使能信号,指示需要对当前的数据进行CRC校验

复制代码
//CRC_en控制
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_crc_en <= 'd0;
    else
        if(r_sof_local == 0)
            if(r_sof_ff2)
                r_crc_en <= 1'b1;
            else if(r_eof)
                r_crc_en <= 1'b1;
            else if(r_eof_ff1)
                case(r_eof_local)
                    0           :r_crc_en <= 1'b0;
                    1           :r_crc_en <= 1'b0;
                    2           :r_crc_en <= 1'b0;
                    3           :r_crc_en <= 1'b0;
                    4           :r_crc_en <= 1'b0;
                    5           :r_crc_en <= 1'b0;
                    6           :r_crc_en <= 1'b1;//剩余一个数据,需要再次拉高crc_en
                    7           :r_crc_en <= 1'b1;//剩余两个数据,需要再次拉高crc_en
                    default     :r_crc_en <= 1'b0;
                endcase                
            else if(r_eof_ff2)
                r_crc_en <= 1'b0;
            else 
                r_crc_en <= r_crc_en; 
        else 
            if(r_sof_ff2)
                r_crc_en <= 1'b1;
            else if(w_eof)
                r_crc_en <= 1'b1;               
            else if(r_eof)
                case(r_eof_local)
                    0           :r_crc_en <= 1'b0;//
                    1           :r_crc_en <= 1'b0;
                    2           :r_crc_en <= 1'b1;
                    3           :r_crc_en <= 1'b1;
                    4           :r_crc_en <= 1'b1;
                    5           :r_crc_en <= 1'b1;
                    6           :r_crc_en <= 1'b1;
                    7           :r_crc_en <= 1'b1;
                    default     :r_crc_en <= 1'b0;
                endcase
            else if(r_eof_ff1)
                r_crc_en <= 1'b0;
            else
                r_crc_en <= r_crc_en;
end

最后也需要对CRC数据的最后一次传输的有效数据进行指示,这里加入一个CRC_KEEP信号,指示最后一次传输的有效数据。

复制代码
//CRC_KEEP处理!!!!
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_crc_keep <= 'd0;
    else
        if(r_sof_ff2)
            r_crc_keep <= 8'b1111_1111;
        // else if((!r_crc_en && r_crc_en_ff1) || r_eof_ff2)
        //     r_crc_keep <= 8'b0000_0000;
        else if(r_sof_local == 0 && r_eof)
            case(r_eof_local)
                0           : r_crc_keep <= 8'b1110_0000;
                1           : r_crc_keep <= 8'b1111_0000;
                2           : r_crc_keep <= 8'b1111_1000;
                3           : r_crc_keep <= 8'b1111_1100;
                4           : r_crc_keep <= 8'b1111_1110;
                5           : r_crc_keep <= 8'b1111_1111;
                default     : r_crc_keep <= 8'b1111_1111;
            endcase
        else if(r_sof_local == 0 && r_eof_ff1)
            case(r_eof_local)
                6           : r_crc_keep <= 8'b1000_0000;
                7           : r_crc_keep <= 8'b1100_0000;
                default     : r_crc_keep <= r_crc_keep;
            endcase   
        else if(r_sof_local == 1 && w_eof)
            case(w_eof_local)
                0           : r_crc_keep <= 8'b1111_1110;
                1           : r_crc_keep <= 8'b1111_1111;
                2           : r_crc_keep <= 8'b1111_1111;
                3           : r_crc_keep <= 8'b1111_1111;
                4           : r_crc_keep <= 8'b1111_1111;
                5           : r_crc_keep <= 8'b1111_1111;
                6           : r_crc_keep <= 8'b1111_1111;
                7           : r_crc_keep <= 8'b1111_1111;
                default     : r_crc_keep <= 8'b1111_1111;
            endcase 
        else if(r_sof_local == 1 && r_eof)
            case(r_eof_local)
                // 0           : r_crc_keep <= 8'b1111_1000;
                // 1           : r_crc_keep <= 8'b1111_1100;
                2           : r_crc_keep <= 8'b1000_0000;
                3           : r_crc_keep <= 8'b1100_0000;

                4           : r_crc_keep <= 8'b1110_0000;
                5           : r_crc_keep <= 8'b1111_0000;
                6           : r_crc_keep <= 8'b1111_1000;
                7           : r_crc_keep <= 8'b1111_1100;
                default     : r_crc_keep <= r_crc_keep;
            endcase 
        // else if(r_sof_local == 1 && r_eof && r_eof_local >= 4)
        //     case(r_eof_local)
        //         0           : r_crc_keep <= 8'b1111_1000;
        //         1           : r_crc_keep <= 8'b1111_1100;
        //         2           : r_crc_keep <= 8'b1111_1110;
        //         3           : r_crc_keep <= 8'b1111_1111;

        //         4           : r_crc_keep <= 8'b1000_0000;
        //         5           : r_crc_keep <= 8'b1100_0000;
        //         6           : r_crc_keep <= 8'b1110_0000;
        //         7           : r_crc_keep <= 8'b1111_0000;
        //         default     : r_crc_keep <= 8'b1111_1111;
        //     endcase         
        else
            r_crc_keep <= r_crc_keep;
end

5、CRC模块

CRC的生成公式采用的标准以太网的CRC-32的公式,这里可以参考此篇文章

三、总结

MAC RX模块主要的难点就在于数据KEEP信号的处理以及相应的64bit的 CRC-32模块的实现。KEEP信号要考虑到开始、结束字符的位置,因此需要处理多种情况。而CEC模块的那点主要在于对于多Bytw数据,一次输入的数据可能不是全部有效的,所以也是需要考虑多种情况。

相关推荐
nanxl136 分钟前
FPGA-DDS信号发生器
fpga开发·verilog·vivado
黄埔数据分析2 小时前
RecoNIC 入门:SmartNIC 上支持 RDMA 的计算卸载-FPGA-智能网卡-AMD-Xilinx
fpga开发
nanxl15 小时前
FPGA-数字时钟
fpga开发·verilog·vivado
尤老师FPGA18 小时前
LVDS系列9:Xilinx 7系可编程输入延迟(二)
单片机·嵌入式硬件·fpga开发
内有小猪卖1 天前
时序约束 记录
fpga开发
Cao1234567893211 天前
FPGA时钟设计
fpga开发
JNTeresa1 天前
锁存器知识点详解
fpga开发
Cao1234567893211 天前
FPGA基础之基础语法
fpga开发
一大Cpp1 天前
通过Quartus II实现Nios II编程
fpga开发
7yewh1 天前
Verilog 语法 (二)
fpga开发