文章目录
- 前言
- 一、模块功能
- 二、实现过程
- 三、仿真
- 总结
前言
上文介绍的MAC_RX模块当中增加了CRC校验和比对的功能,本文将根据CRC校验的结果,来决定将数据输出到上层用户还是丢弃。
一、模块功能
- 接收MAC_RX模块输出的AXIS数据,存入本地环形RAM当中
- 根据MAC_RX模块给出的CRC校验结果,来决定是否输出数据,只有正确的情况下才会从RAM当中将数据吐出,否则不输出,下一帧数据将会直接覆盖错误数据。
module CRC_process(input i_clk ,input i_rst ,input [63:0] s_axis_rdata ,input [79:0] s_axis_ruser ,input [7 :0] s_axis_rkeep ,input s_axis_rlast ,input s_axis_rvalid ,input i_crc_error ,input i_crc_valid ,output [63:0] m_axis_rdata ,output [79:0] m_axis_ruser ,output [7 :0] m_axis_rkeep ,output m_axis_rlast ,output m_axis_rvalid
);
二、实现过程
BRAM_SD_64X256 BRAM_SD_64X256_data (.clka (i_clk ), // input wire clka.ena (rs_axis_rvalid ), // input wire ena.wea (rs_axis_rvalid ), // input wire [0 : 0] wea.addra (r_ram_data_addra ), // input wire [7 : 0] addra.dina (rs_axis_rdata ), // input wire [63 : 0] dina.clkb (i_clk ), // input wire clkb.enb (r_ram_data_enb ), // input wire enb.addrb (r_ram_data_addrb ), // input wire [7 : 0] addrb.doutb (w_ram_data_doutb ) // output wire [63 : 0] doutb
);BRAM_SD_16X32 BRAM_SD_16X32_len (.clka (i_clk ), // input wire clka .ena (rs_axis_rlast ), // input wire ena .wea (rs_axis_rlast ), // input wire [0 : 0] wea .addra (r_ram_len_addra ), // input wire [4 : 0] addra .dina (rs_axis_ruser[79:64]), // input wire [15 : 0] dina .clkb (i_clk ), // input wire clkb .enb (r_ram_len_enb ), // input wire enb .addrb (r_ram_len_addrb ), // input wire [4 : 0] addrb .doutb (w_ram_len_doutb ) // output wire [15 : 0] doutb
);BRAM_SD_8X32 BRAM_SD_8X32_keep (.clka (i_clk ), // input wire clka .ena (rs_axis_rlast ), // input wire ena .wea (rs_axis_rlast ), // input wire [0 : 0] wea .addra (r_ram_keep_addra ), // input wire [4 : 0] addra .dina (rs_axis_rkeep ), // input wire [7 : 0] dina .clkb (i_clk ), // input wire clkb .enb (r_ram_keep_enb ), // input wire enb .addrb (r_ram_keep_addrb ), // input wire [4 : 0] addrb .doutb (w_ram_keep_doutb ) // output wire [7 : 0] doutb
);BRAM_SD_64X32 BRAM_SD_64X32_user (.clka (i_clk ), // input wire clka.ena (rs_axis_rlast ), // input wire ena.wea (rs_axis_rlast ), // input wire [0 : 0] wea.addra (r_ram_user_addra ), // input wire [4 : 0] addra.dina (rs_axis_ruser[63:0]), // input wire [63 : 0] dina.clkb (i_clk ), // input wire clkb.enb (r_ram_user_enb ), // input wire enb.addrb (r_ram_user_addrb ), // input wire [4 : 0] addrb.doutb (w_ram_user_doutb ) // output wire [63 : 0] doutb
);
通过4个简单双端口的BRAM实现:
- 数据、长度信息(在axis_user当中)、尾端keep以及user数据分别存入对应RAM当中。
- 当CRC正确时,各个RAM的地址保持不变,下一帧数据紧随其后被填充进RAM,如果CRC错误,则地址回退到上一次结束的地址,即丢弃刚刚写入的错误数据。
- 得到一次CRC正确数据r_recv_flag加1,输出一次数据r_send_flag加1,俩者不相等说明此时RAM里存在数据,拉高r_run
- 当r_run被拉高表示需要开始输出RAM当中的各种信息,并且通过AXIS接口形式传递给上层模块。
该部分核心代码:
//输入数据进如ram,起始地址由r_data_start_addra决定,len和keep同理
always @(posedge i_clk or posedge i_rst)beginif(i_rst)r_ram_data_addra <= 'd0;else if(ri_crc_valid_1d && ri_crc_error_1d)r_ram_data_addra <= r_data_start_addra;//crc错误则回退到上次起始地址写入新数据else if(rs_axis_rvalid)r_ram_data_addra <= r_ram_data_addra + 1;elser_ram_data_addra <= r_ram_data_addra;
endalways @(posedge i_clk or posedge i_rst)beginif(i_rst)r_ram_len_addra <= 'd0;else if(ri_crc_valid_1d && ri_crc_error_1d)r_ram_len_addra <= r_len_start_addra;//crc错误则回退到上次起始地址写入新数据else if(rs_axis_rlast)r_ram_len_addra <= r_ram_len_addra + 1;elser_ram_len_addra <= r_ram_len_addra;
endalways @(posedge i_clk or posedge i_rst)beginif(i_rst)r_ram_keep_addra <= 'd0;else if(ri_crc_valid_1d && ri_crc_error_1d)r_ram_keep_addra <= r_keep_start_addra;//crc错误则回退到上次起始地址写入新数据else if(rs_axis_rlast)r_ram_keep_addra <= r_ram_keep_addra + 1;elser_ram_keep_addra <= r_ram_keep_addra;
endalways @(posedge i_clk or posedge i_rst)beginif(i_rst)r_ram_user_addra <= 'd0;else if(ri_crc_valid_1d && ri_crc_error_1d)r_ram_user_addra <= r_user_start_addra;//crc错误则回退到上次起始地址写入新数据else if(rs_axis_rlast)r_ram_user_addra <= r_ram_user_addra + 1;elser_ram_user_addra <= r_ram_user_addra;
end// 当数据CRC正确,那么记录此时地址,作为下一帧数据的开始地址,否则保持不变,
// 下一帧数据进来后依旧从上上帧数据结束位置开始写入,即覆盖(丢掉了)CRC错误数据
always @(posedge i_clk or posedge i_rst)beginif(i_rst)beginr_data_start_addra <= 'd0;r_len_start_addra <= 'd0;r_keep_start_addra <= 'd0;r_user_start_addra <= 'd0;endelse if(ri_crc_valid && !ri_crc_error)beginr_data_start_addra <= r_ram_data_addra;r_len_start_addra <= r_ram_len_addra ;r_keep_start_addra <= r_ram_keep_addra;r_user_start_addra <= r_ram_user_addra;endelse beginr_data_start_addra <= r_data_start_addra;r_len_start_addra <= r_len_start_addra ;r_keep_start_addra <= r_keep_start_addra;r_user_start_addra <= r_user_start_addra;end
end//得到一次正确数据r_recv_flag加1,输出一次数据r_send_flag加1
//俩者不相等说明此时ram里存在数据,拉高r_run
always @(posedge i_clk or posedge i_rst)beginif(i_rst)r_recv_flag <= 'd0;else if(ri_crc_valid && !ri_crc_error)r_recv_flag <= r_recv_flag + 'd1;elser_recv_flag <= r_recv_flag;
endalways @(posedge i_clk or posedge i_rst)beginif(i_rst)r_send_flag <= 'd0;else if(rm_axis_rlast)r_send_flag <= r_send_flag + 'd1;elser_send_flag <= r_send_flag;
end//r_run指示当前正在输出数据
always @(posedge i_clk or posedge i_rst)beginif(i_rst)r_run <= 'd0;else if(rm_axis_rlast)r_run <= 'd0;else if((r_recv_flag != r_send_flag) && !rm_axis_rvalid)r_run <= 'd1;elser_run <= r_run;
end
三、仿真
黄线时刻CRC发生错误,因此并没有向上层输出AIS数据,并且地址会回退到上一次正确数据输入结束的地址,重新写入新的数据并且进行CRC判断。,
总结
完整代码参考:https://github.com/shun6-6/Ten_gig_eth_design