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

相关推荐
DS小龙哥2 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师11 小时前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战13 小时前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩1 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技1 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望1 天前
EDA技术简介
fpga开发
淘晶驰AK1 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发
最好有梦想~1 天前
FPGA时序分析和约束学习笔记(4、IO传输模型)
笔记·学习·fpga开发