前面已经完成了协议的解析分类,但是不能对分类好的数据帧再进行相关的校验计算,白白浪费时间,为降低延时,所以需要在分类的同时完成校验计算。

校验,就是通过增加冗余位来验证数据完整性的一个操作,对于谁是真正的“国际巨星”,我们不能章口就莱,还得上来试试。

“你说你演的比我好,那咱们上来试试”

在以太网帧中有两类校验,一个是checksum(校验和),一个是利用CRC算法的FCS(帧校验序列),所以本文针对这两种校验进行说明。


Checksum计算

Checksum,校验和,从名字可以知道就是通过累加数据的方式完成的校验计算,以下所举例的checksum计算是以太网帧数据报文中使用的方法。

  • 那该怎么计算呢?

举个栗子,我有一组数据:{8'hf0, 8'hf1, 8'hf2, 8'hf3, 8'hf4, 8'hf5, 8'hf6, 8'hf7, 8'hf8, 8'hf9, 8'hfa}等11字节的数据,我该怎么计算其checksum呢?

  1. 将数据组成16bit的一组数据,即{16'hf0f1, 16'hf2f3, 16'hf4f5, 16'hf6f7, 16'hf8f9, 16'hfa00},其中若为奇数个字节,则最后一个字节置于16bit的高八位上;
  2. 将这新组成的数据进行累加计算,即16'hf0f1+16'hf2f3+16'hf4f5+16'hf6f7+16'hf8f9+16'hfa00 = 32'h5c2c9;
  3. 若2中计算的结果高位溢出了,则将高位再累加到低位上(循环累加),即 16'h0005 + 16'hc2c9 = 16'hc2ce,如果还有溢出则再次将高位累加到低位上;
  4. 对3中的计算结果按位取反,即~16'hc2ce = 16'h3d31,得到的16'h3d31即checksum的值;
  • 怎么验证checksum是对的呢?

比如同样告知上述的一组数据,然后告知checksum为16'h3d31,那验证的办法可以是:

  1. 重新计算这组数据的checksum,然后与已知的比较,看是否一致;
  2. 在进行16bit数据累加的计算中,把16'h3d31也累加上,即32'h5c2c9 + 16'h3d31 = 32'h5fffa,然后将溢出的高16位加到低16位上,即16'h0005 + 16'hfffa = 16'hffff,如果为16位全1,则说明校验正确,否则则校验失败;
  • 那协议栈需要计算哪些Checksum呢?

目前设计的支持ARP/ICMP/UDP三个协议,那么直接从其报文格式中可以发现,ARP中没有Checksum字段,而在ICMP中含有IP首部的Checksum字段和ICMP首部的Checksum字段,同样UDP中也含有IP首部Checksum和UDP首部Checksum,所有存在三个Checksum的计算,接下来就依次说明其计算过程。

  • IP首部checksum

IP首部的组成如下:

IP首部Checksum计算部分

其首部的Checksum需计算首部20字节(如果有option则更长一点,本文暂时不考虑),即将上图中各字段按照16bit划分(Checksum先填16'h0),再按照之前说的进行计算。

笔者在图中填充上wireshark任意抓获了一帧数据的IP首部部分,可以计算验证,即:

  1. 16'4500 + 16'h0028 + 16'ha140 + 16'h4000 + 16'h6706 + 16'h0000 + 16'h0d6b + 16'h04fe + 16'hc0a8 + 16'hd07c = 32'h330fb
  2. 16'0003 + 16'h30fb = 16'h30fe
  3. ~16'h30fe = 16'hcf01 (计算正确!)

换成verilog则如下:

    always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginip_checksum_clac_32_0   <=  32'h0;ip_checksum_clac_32_1   <=  32'h0;ip_checksum_clac_32     <=  32'h0;  //16bit累加结果ip_checksum_clac_temp   <=  32'h0;  //循环累加ip_checksum_clac        <=  16'h0;  //checksum计算结果endelse beginip_checksum_clac_32_0   <=  {ip_version,ip_header_length,service_type} + total_length + identification + {ip_flag,fragment_offset} + {time_to_live,protocol};ip_checksum_clac_32_1   <=  16'h0 + src_ip[31:16] + src_ip[15:0] + dst_ip[31:16] + dst_ip[15:0];ip_checksum_clac_32     <=  ip_checksum_clac_32_0 + ip_checksum_clac_32_1;ip_checksum_clac_temp   <=  ip_checksum_clac_32[31:16] + ip_checksum_clac_32[15:0];ip_checksum_clac        <=  ~{{15'h0,ip_checksum_clac_temp[16]}+ip_checksum_clac_temp[15:0]};endend

  • ICMP首部checksum

对于ICMP的checksum不再举例说明计算过程了,只说明参与计算的数据构成。

ICMP的Checksum计算部分

ICMP的Checksum计算如上图所示,包括首部和选项数据部分,同样计算时将Checksum字段置零进行计算,即16'h0000 + 16'h0000 + 16'h0001 + 16'h0013 + 16'h6162 + ....... + 16'h6869,同样再进行循环累加和按位取反操作。

(实现RTL可参考笔者之后上传的)

  • UDP首部checksum

而UDP首部中的Checksum字段计算与前面有略微区别,它与TCP首部的Checksum计算一样,需要将伪首部(pseudo header)加到计算当中,其中UDP的伪首部组成如下图所示:

UDP首部Checksum计算部分

其伪首部包括:源IP(32),目的IP(32),预留(8),协议(8),UDP长度(8);

同时再将UDP首部的端口号和UDP长度字段加入计算,需要注意的是伪首部中的UDP长度和UDP首部中的长度一致,所以上图的累加应该是:16'hc0a8 + 16'h0001 + 16'hc0a8 + 16'h000a + ....... + 16'h9900,(字节数为奇数,注意最后一个16bit的低八位需填0)。

(实现RTL可参考笔者之后上传的)

FCS计算

FCS,frame check sequence,帧校验序列,位于以太网帧的尾部,如下图所示,用于校验以太网数据帧是否出错。

以太网帧结构

前面说过,FCS是采用CRC32算法得到的4字节长度的校验序列,其中CRC算法入门可以参考文章:

循环冗余校验(CRC)算法入门引导_Ivan 的专栏-CSDN博客_crc校验​blog.csdn.net

当然,也可以直接参考Xilinx的官方手册xapp209[1],同时该文档也提供了FCS的实现结构,如下图所示,其中crc_reg[31:0]即每次计算得到结果,而crc[7:0]即计算结束后输出4字节FCS。

基于CRC32的FCS实现结构图

其中使用的CRC32的硬件实现算法可详见论文[2],具体的硬件实现代码如下图所示。

CRC32的硬件实现

上述实现结构翻译成verilog就是:

always @ (posedge clk or negedge rst_n)beginif (!rst_n) begincrc_reg             <=  32'hFFFFFFFF;crc                 <=  8'hFF;endelse if (load_init) begincrc_reg             <=  32'hFFFFFFFF;crc                 <=  8'hFF;endelse if (calc & d_valid) begincrc_reg             <=  next_crc;crc                 <=  ~{next_crc[24], next_crc[25], next_crc[26], next_crc[27],next_crc[28], next_crc[29], next_crc[30], next_crc[31]};endelse if (~calc & d_valid) begincrc_reg             <=  {crc_reg[23:0], 8'hFF};crc                 <=  ~{crc_reg[16], crc_reg[17], crc_reg[18], crc_reg[19],crc_reg[20], crc_reg[21], crc_reg[22], crc_reg[23]};end
end//
// CRC XOR equations
//assign next_crc[0] = crc_reg[30] ^ din[7] ^ crc_reg[24] ^ din[1];
assign next_crc[1] = crc_reg[30] ^ crc_reg[31] ^ din[6] ^ din[7] ^ crc_reg[24] ^ din[0] ^ crc_reg[25] ^ din[1];
assign next_crc[2] = crc_reg[30] ^ crc_reg[31] ^ din[5] ^ din[6] ^ din[7] ^ crc_reg[24] ^ crc_reg[25] ^ crc_reg[26] ^ din[0] ^ din[1];
assign next_crc[3] = din[4] ^ crc_reg[31] ^ din[5] ^ din[6] ^ crc_reg[25] ^ crc_reg[26] ^ crc_reg[27] ^ din[0];
assign next_crc[4] = crc_reg[30] ^ din[4] ^ din[5] ^ din[7] ^ crc_reg[24] ^ crc_reg[26] ^ crc_reg[27] ^ crc_reg[28] ^ din[1] ^ din[3];
assign next_crc[5] = crc_reg[30] ^ din[4] ^ crc_reg[31] ^ din[6] ^ din[7] ^ crc_reg[24] ^ crc_reg[25] ^ crc_reg[27] ^ crc_reg[28] ^ crc_reg[29] ^ din[0] ^ din[1] ^ din[2] ^ din[3];
assign next_crc[6] = crc_reg[30] ^ crc_reg[31] ^ din[5] ^ din[6] ^ crc_reg[25] ^ crc_reg[26] ^ crc_reg[28] ^ crc_reg[29] ^ din[0] ^ din[1] ^ din[2] ^ din[3];
assign next_crc[7] = din[4] ^ din[5] ^ crc_reg[31] ^ din[7] ^ crc_reg[24] ^ crc_reg[26] ^ crc_reg[27] ^ crc_reg[29] ^ din[0] ^ din[2];
assign next_crc[8] = din[4] ^ din[6] ^ din[7] ^ crc_reg[24] ^ crc_reg[25] ^ crc_reg[27] ^ crc_reg[28] ^ crc_reg[0] ^ din[3];
assign next_crc[9] = din[5] ^ din[6] ^ crc_reg[25] ^ crc_reg[26] ^ crc_reg[28] ^ crc_reg[29] ^ din[2] ^ crc_reg[1] ^ din[3];
assign next_crc[10] = din[4] ^ din[5] ^ din[7] ^ crc_reg[24] ^ crc_reg[26] ^ crc_reg[27] ^ crc_reg[29] ^ din[2] ^ crc_reg[2];
assign next_crc[11] = din[4] ^ crc_reg[3] ^ din[6] ^ din[7] ^ crc_reg[24] ^ crc_reg[25] ^ crc_reg[27] ^ crc_reg[28] ^ din[3];
assign next_crc[12] = crc_reg[30] ^ din[5] ^ crc_reg[4] ^ din[6] ^ din[7] ^ crc_reg[24] ^ crc_reg[25] ^ crc_reg[26] ^ crc_reg[28] ^ crc_reg[29] ^ din[1] ^ din[2] ^ din[3];
assign next_crc[13] = din[4] ^ crc_reg[30] ^ crc_reg[31] ^ din[5] ^ din[6] ^ crc_reg[5] ^ crc_reg[25] ^ crc_reg[26] ^ crc_reg[27] ^ crc_reg[29] ^ din[0] ^ din[1] ^ din[2];
assign next_crc[14] = din[4] ^ crc_reg[30] ^ din[5] ^ crc_reg[31] ^ crc_reg[6] ^ crc_reg[26] ^ crc_reg[27] ^ crc_reg[28] ^ din[0] ^ din[1] ^ din[3];
assign next_crc[15] = din[4] ^ crc_reg[31] ^ crc_reg[7] ^ crc_reg[27] ^ crc_reg[28] ^ crc_reg[29] ^ din[0] ^ din[2] ^ din[3];
assign next_crc[16] = din[7] ^ crc_reg[24] ^ crc_reg[8] ^ crc_reg[28] ^ crc_reg[29] ^ din[2] ^ din[3];
assign next_crc[17] = crc_reg[30] ^ din[6] ^ crc_reg[25] ^ crc_reg[9] ^ crc_reg[29] ^ din[1] ^ din[2];
assign next_crc[18] = crc_reg[30] ^ din[5] ^ crc_reg[31] ^ crc_reg[26] ^ din[0] ^ din[1] ^ crc_reg[10];
assign next_crc[19] = din[4] ^ crc_reg[31] ^ crc_reg[27] ^ din[0] ^ crc_reg[11];
assign next_crc[20] = crc_reg[12] ^ crc_reg[28] ^ din[3];
assign next_crc[21] = crc_reg[13] ^ crc_reg[29] ^ din[2];
assign next_crc[22] = crc_reg[14] ^ din[7] ^ crc_reg[24];
assign next_crc[23] = crc_reg[30] ^ din[6] ^ din[7] ^ crc_reg[24] ^ crc_reg[15] ^ crc_reg[25] ^ din[1];
assign next_crc[24] = crc_reg[31] ^ din[5] ^ din[6] ^ crc_reg[25] ^ crc_reg[16] ^ crc_reg[26] ^ din[0];
assign next_crc[25] = din[4] ^ din[5] ^ crc_reg[26] ^ crc_reg[17] ^ crc_reg[27];
assign next_crc[26] = crc_reg[30] ^ din[4] ^ din[7] ^ crc_reg[24] ^ crc_reg[27] ^ crc_reg[18] ^ crc_reg[28] ^ din[1] ^ din[3];
assign next_crc[27] = crc_reg[31] ^ din[6] ^ crc_reg[25] ^ crc_reg[28] ^ crc_reg[19] ^ crc_reg[29] ^ din[0] ^ din[2] ^ din[3];
assign next_crc[28] = crc_reg[30] ^ din[5] ^ crc_reg[26] ^ crc_reg[29] ^ din[1] ^ din[2] ^ crc_reg[20];
assign next_crc[29] = din[4] ^ crc_reg[30] ^ crc_reg[21] ^ crc_reg[31] ^ crc_reg[27] ^ din[0] ^ din[1];
assign next_crc[30] = crc_reg[31] ^ crc_reg[22] ^ crc_reg[28] ^ din[0] ^ din[3];
assign next_crc[31] = crc_reg[23] ^ crc_reg[29] ^ din[2];

  • 顺手再推荐一个网站

可以直接生成CRC的verilog或者VHDL源码:

CRC Generation Tool​www.easics.com

crctool 操作说明
下载得到的CRC计算的verilog源码

得到上图的所示的源码后,可以发现其为一个function,可以对模块添加输入输出,并调用该function即可进行CRC计算。

网页的工具还可以任意勾选多项式并生成源码,方便使用。

不过,有时候网页工具打不开,不知道为啥。。。


以上即协议栈设计中的校验部分,为确保接收的数据帧的正确,笔者在接收协议分类中即进行了校验计算,当前帧校验无误后再向后级模块传输处理,此外,对于需发送的数据帧在进行封包的过程中完成校验计算,使可进行发送。

若有不足之处还望批评指正!


十二点过九分:UDP/IP硬件协议栈设计(四):ARP​zhuanlan.zhihu.com


参考

  1. ^Xilinx手册xapp209 https://www.xilinx.com/support/documentation/application_notes/xapp209.pdf
  2. ^Rajesh Nair, Gerry Ryan and Farivar Farzaneh, A Symbol Based Algorithm for Hardware Implementation of Cyclic Redundancy Check (CRC), Bay Networks. https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=623934

crc32校验算法_UDP/IP硬件协议栈设计(三):校验相关推荐

  1. kali ip查询_UDP/IP硬件协议栈设计(一):缘起

    算算也该准备毕业了,正好毕业设计中需要使用UDP进行PC与FPGA之间的通信,而直接实现UDP硬件协议栈想想也并不复杂,"兵马未动.粮草先行",等完成UDP之后再开始毕设课题的研究 ...

  2. 异或校验算法 c语言程序,C# 异或校验算法

    C# 的异或校验算法 直接上代码 public partial class FormCRC : Form { public FormCRC() { InitializeComponent(); } p ...

  3. php crc16校验算法,PHP串口通信中计算crc16校验码

    最近使用PHP开发串口通信业务,在发送485Modbus命令时,基本都要计算CRC16校验码.网上搜索了很多文章,很多都计算的不对.本文记录搜索到的正确的计算方法. 代码如下:/** * crc16计 ...

  4. crc16检验 python_Python CRC16校验算法

    def crc16(x, invert): a = 0xFFFF b = 0xA001 for byte in x: a ^= ord(byte) for i in range(8): last = ...

  5. 为何TCP/IP协议栈设计成沙漏型的

    前几天有人回复我的一篇文章问,为何TCP/IP协议栈设计成沙漏型的.这个问题问得好! 我先不谈为何它如此设计,我一个80后根本就没有资格去评论上世纪80年代已经臻于成熟的一个设计,我只是说一下目前的趋 ...

  6. 【转载】CRC32校验算法C语言版(查表法)

    先放原文链接:CRC32校验算法C语言版(查表法) 这几天搞串口通信,用到CRC32,把以前用到的东西整理一下,方便以后使用. STM32F103 芯片自带的CRC32硬件算法,匹配上位机CRC32算 ...

  7. 计算机网络透明网桥的算法,网基课程设计曼切斯特编码模拟透明网桥自习域转发帧算法模拟IP分片模拟.doc...

    网基课程设计曼切斯特编码模拟透明网桥自习域转发帧算法模拟IP分片模拟 计算机网络基础 课 程 设 计 报 告 学号: 姓名: 班级: 题号:01.06.07 题目:曼切斯特编码模拟.透明网桥自学习域转 ...

  8. 【以太网硬件TCP/IP协议栈】硬件协议栈W5500应用

    硬件协议栈和软件协议的区别: MCU+MAC+PHY方案 传统的以太网接入方案如下图,由 MCU+MAC+PHY 再加入网络接口实现以太网的物理连接,通过在主控芯片中植入TCP/IP 协议代码实现通信 ...

  9. C#:实现CRC32校验算法(附完整源码)

    C#:实现CRC32校验算法 /// <summary>/// 计算给定长度数据的32位CRC/// </summary>/// <param name="da ...

最新文章

  1. 《程序设计解题策略》——1.6 利用左偏树实现优先队列的合并
  2. SAP RETAIL初阶之商品主数据WM视图
  3. 2021年夏天找SAP新项目的几点感想
  4. Linux下显示前10个占用空间最大的文件或目录命令
  5. 给定两个数r和n_输出r的n次方 java_滴滴出行2018编程题
  6. 2021 年 ICT 行业预测
  7. 混迹于IT纯屌界中独一无二的丸子
  8. 利用jquery.validate异步验证用户名是否存在
  9. mysql简单增删改查(CRUD)
  10. 网络游戏简易分区服务器架构详解
  11. ubuntu mongodb安装
  12. Notes Twenty one days-渗透攻击-红队-权限提升
  13. 网站创业项目商业计划书的写法
  14. Findbugs使用指南及扫描内容解释
  15. 五险一金 | 2020年企业社保缴费为零问题
  16. Python策略模式实例
  17. 微服务调用链追踪中心搭建
  18. 诺基亚7plus支持html,【诺基亚7Plus评测】诺基亚7Plus评测:三蔡司镜头“全面”来袭(全文)_诺基亚 7 Plus(4GB RAM/全网通)_手机评测-中关村在线...
  19. Pollard-Rho Algorithm简述
  20. POCO中的异常处理和调试

热门文章

  1. 舍 bpftrace 而取 systemtap 的代价和思考
  2. 值传递和引用传递传的到底是啥?
  3. 0基础如何系统的学习Python? 只要完成这 9 步
  4. CSDN 联合 18 家大厂招聘直播,10 小时突破百万热度!
  5. Python 10 行以内代码能有什么高端操作?| 原力计划
  6. 珍稀干货!阿里 Web 音视频开发趟坑指南
  7. 男性玩家占78.8%、90后玩家占近50%、最多人选择中国风链游……《2019链游玩家需求调研报告》重磅发布!...
  8. 流行于机器学习竞赛的Boosting,一文讲透足够了
  9. 神龙神龙你擦亮眼,阿里巴巴要“上天”!
  10. TensorFlow Lite 实现首次移植到 Arduino!