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数据,一次输入的数据可能不是全部有效的,所以也是需要考虑多种情况。

相关推荐
吉大一菜鸡11 分钟前
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
笔记·学习·fpga开发
9527华安6 小时前
FPGA实现MIPI转FPD-Link车载同轴视频传输方案,基于IMX327+FPD953架构,提供工程源码和技术支持
fpga开发·架构·mipi·imx327·fpd-link·fpd953
热爱学习地派大星6 小时前
FPGA远程升级 -- FLASH控制
fpga开发
szxinmai主板定制专家14 小时前
【国产NI替代】基于国产FPGA+兆易创新GD32F450的全国产16振动+2转速(24bits)高精度终端采集板卡
fpga开发
szxinmai主板定制专家16 小时前
【国产NI替代】基于FPGA的32通道(24bits)高精度终端采集核心板卡
大数据·人工智能·fpga开发
HIZYUAN1 天前
AGM FPGA如何配置上拉或者下拉电阻
fpga开发
∑狸猫不是猫1 天前
(13)CT137A- 简易音乐盒设计
fpga开发
ThreeYear_s1 天前
基于FPGA 的4位密码锁 矩阵键盘 数码管显示 报警仿真
fpga开发·矩阵·计算机外设
Anin蓝天(北京太速科技-陈)1 天前
252-8路SATAII 6U VPX高速存储模块
fpga开发
如何学会学习?1 天前
2. FPGA基础了解--全局网络
fpga开发