目录
实验任务
CRC 生成 Verilog 实现
电路生成原理
模块设计图
CRC 生成时序图
具体代码实现
上板验证
实验任务
在上一篇介绍了 CRC 校验码的原理,如何计算 CRC 校验码,这篇介绍如何利用 Verilog 实现CRC 校验码的生成。
什么是 CRC 校验码?
CRC 生成 Verilog 实现
CRC 校验分为发送方和接收方,根据项目的要求,对串口输入的数据由 UART_RX 模块生成的并行数据,通过以Verilog计算这个并行数据的 CRC 校验码,并以并行的形式输出。由于是 UART 协议的数据,所以数据的位宽为8位,CRC 的位宽也为8位,因此多项式的最高次幂也为8。
电路生成原理
本次使用的多项式为 x^8 + x^2 + x^1 + 1,对应的二进制数为 100000111 根据多项式得到的电路图如下图所示,数据从最右端输入,多项式中的含有幂的项,会经过一个异或运算,其他的项则不用改变。
模块设计图
模块的设计示意图如下图所示,对于输入由四路信号组成,分别是时钟信号、复位信号、输入数据信号、输入数据同步的脉冲信号;对于输出由两个信号组成,计算得到的 CRC 校验码以及 CRC校验码同步的脉冲信号。
CRC 生成时序图
以下是 CRC 生成的时序图,图中每个信号之间的关系都描述的很清楚。
具体代码实现
以下是 CRC 生成的代码,需要说明的是:其中例化的 UART_RX 模块是自己写的一个 UART 接收模块,它的作用是将串口接收到的串行数据转换成并行数据输出以及输出相应的同步脉冲信号,作为 CRC 生成模块的输入信号。
`timescale 1ns / 1ps
//
// Company:
// Engineer: Linest-5
// Create Date: 2022/04/22
// Design Name:
// Module Name: CRC_GEN
// Project Name:
// Target Devices:
// Tool Versions:
// Description: 对串口接收到的8位并行数据生成出8位的CRC校验码输出
// Dependencies:
// Revision:
// Additional Comments:
// 1 polynomial: x^8 + x^2 + x^1 + 1
// 2 data width: 8
// 3 高位在左,低位在右
// 4 CRC初始值:全1
// 5 CRC并行输出
//module CRC_GEN #(parameter BAUD_RATE = 'd9600, //波特率parameter CLK_FREQ = 'd50000000, //时钟周期parameter BAUD_CNT_MAX = CLK_FREQ/BAUD_RATE)(input clk, input rst_n, input rx, //输入的串行数据 output wire dir //输出控制RS485的方向
); wire [7:0] po_data; //UART_RX模块输出的并行数据wire po_flag; //UART_RX模块输出的并行数据同步信号reg feedback; //通过判定输入数据的值给后面的位以反馈reg crc_flag; //crc开始计算标志信号reg [7:0] crc_reg; //CRC计算过程寄存reg [7:0] crc_tran; //输出计算得到的crcreg crc_sync; //过渡crc数据的同步信号reg [7:0] crc_out; //实际输出的crcreg crc_valid; //crc输出有效标志信号,和输出的并行crc数据同步assign dir = 0; //使能max3485输入//对po_flag打一拍生成crc_flagalways @(posedge clk or negedge rst_n) beginif (rst_n == 'd0) begincrc_flag <= 'd0;endelse begincrc_flag <= po_flag;endend//对crc_flag打一拍生成crc_syncalways @(posedge clk or negedge rst_n) beginif (rst_n == 'd0) begincrc_sync <= 'd0;endelse begincrc_sync <= crc_flag;endend//输出CRCalways @(posedge clk or negedge rst_n) beginif(rst_n == 'd0) crc_tran <= 'd0; //crc寄存器初始值else if(crc_flag == 'd0) //crc输出延时一个周期让crc恢复到初始值crc_tran <= 'd0; else if(crc_flag == 'd1) begin crc_tran <= crc_reg; //在crc_flag为高的时候将计算得到的crc输出endend//计算CRC过程integer i;always @( po_data or crc_tran) begincrc_reg = crc_tran;for(i=7; i>=0; i=i-1)beginfeedback = crc_reg[7] ^ po_data[i];crc_reg[7] = crc_reg[6];crc_reg[6] = crc_reg[5];crc_reg[5] = crc_reg[4];crc_reg[4] = crc_reg[3];crc_reg[3] = crc_reg[2];crc_reg[2] = crc_reg[1] ^ feedback;crc_reg[1] = crc_reg[0] ^ feedback;crc_reg[0] = feedback;endend//寄存crc值输出always @(posedge clk or negedge rst_n) beginif (rst_n == 'd0) begincrc_out <= 'd0;endelse if (crc_sync == 'd1) begincrc_out <= crc_tran;endelse begincrc_out <= crc_out;endend//对crc_sync打一拍得到crc数据有效信号always @(posedge clk or negedge rst_n) beginif (rst_n == 'd0) begincrc_valid <= 'd0;endelse begincrc_valid <= crc_sync;endendUART_RX #(.BAUD_RATE(BAUD_RATE),.CLK_FREQ(CLK_FREQ),.BAUD_CNT_MAX(BAUD_CNT_MAX)) inst_UART_RX (.clk (clk),.rst_n (rst_n),.rx (rx),.po_data (po_data),.po_flag (po_flag));ila_0 CRC_GEN (.clk(clk), // input wire clk.probe0(po_data), // input wire [7:0] probe0 .probe1(po_flag), // input wire [0:0] probe1 .probe2(crc_flag), // input wire [0:0] probe2 .probe3(crc_reg), // input wire [7:0] probe3 .probe4(crc_tran), // input wire [7:0] probe4 .probe5(crc_sync), // input wire [0:0] probe5 .probe6(crc_out), // input wire [7:0] probe6 .probe7(crc_valid) // input wire [0:0] probe7);endmodule
上板验证
将相应的模块添加到文件中,板子上电即可验证。
将除法信号设置为 crc_valid,即输出 CRC 数据的同步信号,触发类型设置为下降沿,在串口中输入 66(16进制),就可以看到输出的 CRC 校验码为 00110101。
利用 CRC(循环冗余校验)在线计算验证是否结果正确。
CRC(循环冗余校验)在线计算_ip33.com
验证结果正确!