用Verilog實(shí)現(xiàn)CRC-8的串行計(jì)算,G(D)=D8+D2+D+1,計(jì)算流程如下圖所示:
一、分析
CRC循環(huán)冗余校驗(yàn)碼(Cyclic Redundancy Check),檢錯(cuò)碼。
(1)該題目所述為CRC-8,即輸出8位CRC校驗(yàn)值,給定一段長(zhǎng)為N-bit的有效輸入序列,輸出(N+8)-bit的數(shù)據(jù),其中前N-bit數(shù)據(jù)為輸入的原始數(shù)據(jù),添加的8-bit數(shù)據(jù)為CRC校驗(yàn)數(shù)據(jù);
(2)該CRC-8的生成多項(xiàng)式為G(D)=D8+D2+D+1,對(duì)CRC進(jìn)行簡(jiǎn)化表示時(shí)可以忽略最高位的D8,結(jié)合圖示中三個(gè)異或運(yùn)算的位置更容易理解生成多項(xiàng)式,8位CRC有8個(gè)寄存器C0~C7,根據(jù)多項(xiàng)式,C0、C1和C2的輸入是由異或運(yùn)算而來(lái);
二、Verilog編程
1. 并行計(jì)算,串行輸出
對(duì)于輸入位寬為1的輸入,這種方法的計(jì)算非常簡(jiǎn)單,直接根據(jù)生成多項(xiàng)式運(yùn)算。
(注意!如果輸入位寬不為1,則需要根據(jù)多項(xiàng)式進(jìn)行化簡(jiǎn),可以使用http://outputlogic.com/?page_id=321生成運(yùn)算邏輯和代碼)
1.1 自己一步一步寫(xiě)
(1)先定義8個(gè)寄存器reg [7:0]
reg [7:0] crc_reg_q;// 寄存器 Q 輸出端
reg [7:0] crc_reg_d;// 寄存器 D 輸入端
(對(duì)crc_reg_d,使用always賦值就定義成reg,使用assign就定義成wire)
(2)異或計(jì)算
寄存器0的輸入crc_reg_d[0],來(lái)自寄存器7的輸出crc_reg_q[7]和數(shù)據(jù)輸入data_in異或運(yùn)算,即:
crc_reg_d [0] = crc_reg_q[7] ^ data_in;
同理可得:
always @ (*)
begin
crc_reg_d[0] = crc_reg_q[7] ^ data_in;
crc_reg_d[1] = crc_reg_q[7] ^ data_in ^ crc_reg_q[0];
crc_reg_d[2] = crc_reg_q[7] ^ data_in ^ crc_reg_q[1];
crc_reg_d[3] = crc_reg_q[2];
crc_reg_d[4] = crc_reg_q[3];
crc_reg_d[5] = crc_reg_q[4];
crc_reg_d[6] = crc_reg_q[5];
crc_reg_d[7] = crc_reg_q[6];
end
上述使用組合邏輯實(shí)現(xiàn)異或運(yùn)算和數(shù)據(jù)的傳遞,另外,對(duì)于每個(gè)寄存器的輸入到輸出(D端到Q端),使用時(shí)序邏輯,并且按照題目要求,設(shè)置初始值為全1,將數(shù)據(jù)有效標(biāo)志作為控制邏輯:
always @ (posedge clk)
begin
if(rst)
crc_reg_q <= 8’hff;
elsebegin
if(data_valid )
crc_reg_q<= crc_reg_d;// 輸入數(shù)據(jù)有效就更新值
else
crc_reg_q<= crc_reg_q;// 輸入數(shù)據(jù)無(wú)效就等待
end
end
(3)串行輸出
上述已經(jīng)實(shí)現(xiàn)了并行的 CRC,計(jì)算出的 CRC 結(jié)果就是直接的 8 位 CRC,按照題目要求,需要串行輸出 CRC 結(jié)果。
思路:寫(xiě)一個(gè)計(jì)數(shù)器,當(dāng)需要輸出 CRC 時(shí),串行計(jì)數(shù)輸出,實(shí)現(xiàn)并串轉(zhuǎn)換。這里,由于題目給了一個(gè)信號(hào) crc_start,我把這個(gè)信號(hào)作為 CRC 的標(biāo)志,當(dāng) data_valid 有效時(shí)表示輸入的是數(shù)據(jù),當(dāng) data_valid 無(wú)效且crc_start 有效表示數(shù)據(jù)輸入完畢,該輸出 CRC 了。
reg [2:0] count;
always @ (posedge clk)
begin
if(rst)begin
crc_out <= 0;
count<= 0;
end
elsebegin
if(data_valid) begin
crc_out <= data_in;
crc_valid <= 1'b0;
end
elseif(crc_start)begin
count <= count + 1'b1;
crc_out <= crc_reg_q [7-count];
crc_valid <= 1'b1;
end
elsebegin
crc_valid <= 1'b0;
end
end
end
1.2 CRC Generator自動(dòng)生成
在Step1中,根據(jù)要求,1處表示輸入數(shù)據(jù)位寬為1,2處CRC輸出8位,3處選擇自定義CRC的多項(xiàng)式,4處點(diǎn)擊運(yùn)用設(shè)定,然后進(jìn)入Step2。
根據(jù)生成多項(xiàng)式,勾選1、X1、X2即可(對(duì)應(yīng)1+D1+D2,最高位的D8不用管)。
//-----------------------------------------------------------------------------
// Copyright (C) 2009 OutputLogic.com
// This source file may be used and distributedwithout restriction
// provided that this copyright statement is notremoved from the file
// and that any derivative work contains theoriginal copyright notice
// and the associated disclaimer.
//
// THIS SOURCE FILE IS PROVIDED "ASIS" AND WITHOUT ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, WITHOUTLIMITATION, THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND FITNESS FORA PARTICULAR PURPOSE.
//-----------------------------------------------------------------------------
// CRC module for data[0:0] , crc[7:0]=1+x^1+x^2+x^8;
//-----------------------------------------------------------------------------
module crc(
input [0:0] data_in,
input crc_en,
output [7:0] crc_out,
input rst,
input clk);
reg [7:0] lfsr_q,lfsr_c;
assign crc_out = lfsr_q;
always @(*) begin
lfsr_c[0] = lfsr_q[7] ^ data_in[0];
lfsr_c[1] = lfsr_q[0] ^ lfsr_q[7]^ data_in[0];
lfsr_c[2] = lfsr_q[1] ^ lfsr_q[7]^ data_in[0];
lfsr_c[3] = lfsr_q[2];
lfsr_c[4] = lfsr_q[3];
lfsr_c[5] = lfsr_q[4];
lfsr_c[6] = lfsr_q[5];
lfsr_c[7] = lfsr_q[6];
end // always
always @(posedge clk, posedge rst) begin
if(rst) begin
lfsr_q <= {8{1'b1}};
end
else begin
lfsr_q <= crc_en ? lfsr_c: lfsr_q;
end
end // always
endmodule // crc
將上述代碼按照題目要求改變輸入輸出的名稱(chēng),并進(jìn)行串并轉(zhuǎn)換(并->串)即可。
1.3 easics自動(dòng)生成
https://www.easics.com/crctool
(1)1處選擇CRC的生成多項(xiàng)式,這里與1.2的不同在于,要把最高位的D8選上,easics能識(shí)別的CRC協(xié)議更多;
(2)2處自動(dòng)識(shí)別出這個(gè)CRC多項(xiàng)式其實(shí)是CRC8 ATM HEC協(xié)議使用的 CRC;
(3)3處設(shè)置輸入數(shù)據(jù)位寬為1;
(4)選擇生成Verilog代碼;
(5)下載代碼。
仔細(xì)閱讀代碼注釋?zhuān)⒁猓?/span>
convention: the first serial bit is D[0]
數(shù)據(jù)的最低位先輸出,此代碼將會(huì)把低位作為異或移出位,而上面已經(jīng)提到的兩種方法均是將最高位作為移出位去異或,所以,代碼中需要稍作修改,將d[0]改成d[7],d[1]改成d[6],…,以此類(lèi)推,c[0]- c[7]不要變。
有興趣的可以去看看【大小端問(wèn)題】,在不同處理器、不同協(xié)議中非常常見(jiàn)。
///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 1999-2008 Easics NV.
// This source file may be used and distributedwithout restriction
// provided that this copyright statement is notremoved from the file
// and that any derivative work contains theoriginal copyright notice
// and the associated disclaimer.
//
// THIS SOURCE FILE IS PROVIDED "AS IS"AND WITHOUT ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, WITHOUTLIMITATION, THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR APARTICULAR PURPOSE.
//
// Purpose : synthesizable CRC function
// *polynomial: x^8 + x^2 + x^1 + 1
// * datawidth: 1
//
// Info : tools@easics.be
// http://www.easics.com
///////////////////////////////////////////////////////////////////////////////
module CRC8_D1;
//polynomial: x^8 + x^2 + x^1 + 1
// datawidth: 1
//convention: the first serial bit is D[0]
function[7:0] nextCRC8_D1;
inputData;
input[7:0] crc;
reg [0:0]d;
reg [7:0]c;
reg [7:0]newcrc;
begin
d[0] =Data;
c = crc;
newcrc[0]= d[0] ^ c[7];
newcrc[1]= d[0] ^ c[0] ^ c[7];
newcrc[2]= d[0] ^ c[1] ^ c[7];
newcrc[3]= c[2];
newcrc[4]= c[3];
newcrc[5]= c[4];
newcrc[6]= c[5];
newcrc[7]= c[6];
nextCRC8_D1 = newcrc;
end
endfunction
endmodule
2. 串行計(jì)算,串行輸出(函數(shù)function用法)
CRC計(jì)算的重要思想是不斷的消除最高位。
(1) 新建函數(shù)function
Verilog函數(shù)名為next_crc,輸入信號(hào)為 data_in 和 current_crc,輸出信號(hào)為8位的新 crc。
函數(shù)功能為:根據(jù)輸入信號(hào)data_in,跳轉(zhuǎn)CRC的狀態(tài);
函數(shù)的設(shè)計(jì)邏輯為:
(a)將CRC寄存器的數(shù)據(jù)左移1位,低位補(bǔ)零,得到
{current_crc[6:0],1'b0} (其中{ }為位拼接符);
(b)新輸入的數(shù)據(jù)data_in和移出的CRC最高位做異或得到
current_crc[7]^data_in;
(c)使用位拼接符對(duì)異或結(jié)果進(jìn)行位擴(kuò)展,CRC-8進(jìn)行8位的擴(kuò)展,得到
{8{current_crc[7]^data_in}};
(d)擴(kuò)展后的數(shù)據(jù)和生成多項(xiàng)式進(jìn)行與運(yùn)算,得到
{8{current_crc[7]^data_in}}&(8'h07);
(e)將(a)的數(shù)據(jù)和(d)的數(shù)據(jù)進(jìn)行異或運(yùn)算,得到CRC結(jié)果;
next_crc = {current_crc[6:0],1'b0} ^({8{current_crc[7]^data_in}} & (8'h07));
上述,(a)是對(duì)CRC低位的處理,(b)-(d)是對(duì)CRC最高位的處理。
8’h07從何而來(lái)?
因?yàn)樯啥囗?xiàng)式G(D) = D8+D2+D1+D0,前面提到了最高位的D8不用管,那么使用8位去表示為 0000_0111,即低3位為1,其余為0,即8’h07。
function [7:0] next_crc;
inputdata_in;
input[7:0] current_crc;
begin
next_crc = {current_crc[6:0],1'b0} ^ ({8{current_crc[7]^data_in}} &(8'h07));
end
endfunction
(2) 調(diào)用function函數(shù)
初始化時(shí)給寄存器賦值全為1,當(dāng)數(shù)據(jù)有效時(shí),進(jìn)行CRC計(jì)算。
reg [7:0] crc_reg;
always @ (posedge clk)
begin
if(rst)begin
crc_reg <= 8'hff;
end
elsebegin
if(data_valid) begin
crc_reg <= next_crc(data_in, crc_reg);
end
end
end
(3) 串行輸出(串并轉(zhuǎn)換)
按照上面的老方法,串并轉(zhuǎn)換(并->串)。
輸入32個(gè)1,先輸出輸入的32個(gè)1,緊跟著輸出32個(gè)1的CRC校驗(yàn)值8’b00001111。
三、原理圖設(shè)計(jì)
使用Quartus原理圖設(shè)計(jì),調(diào)用DFF觸發(fā)器和XOR異或門(mén)搭建題目所示的CRC邏輯。
這里沒(méi)有做data_valid的控制,輸入數(shù)據(jù)是連續(xù)的32個(gè)1,輸入完成后的CRC值是8’h0f,串行輸出8位二進(jìn)制數(shù)據(jù) 8’b00001111。
四、擴(kuò)展
1. 并行計(jì)算并行輸出
(1)對(duì)于單bit輸入的序列,只要將并行計(jì)算串行輸出的串并轉(zhuǎn)換去掉,直接輸出8-bit的CRC校驗(yàn)值即可;
(2)對(duì)于多bit同時(shí)輸入的序列,通過(guò)介紹的兩個(gè)在線平臺(tái)去生成運(yùn)算邏輯(筆試肯定不會(huì)喪心病狂到考多bit并行);
2. 查表法
實(shí)際工程中,為了減少運(yùn)算量,還經(jīng)常使用一種查表法,將CRC的校驗(yàn)表直接存儲(chǔ)在ROM中,進(jìn)行索引查找,常見(jiàn)的CRC表可以自行去查找,這里只是拋磚引玉。
-
寄存器
+關(guān)注
關(guān)注
31文章
5317瀏覽量
120003 -
crc
+關(guān)注
關(guān)注
0文章
199瀏覽量
29434 -
Verilog
+關(guān)注
關(guān)注
28文章
1343瀏覽量
109983
原文標(biāo)題:手撕代碼 | CRC校驗(yàn)碼的多種Verilog實(shí)現(xiàn)方式
文章出處:【微信號(hào):處芯積律,微信公眾號(hào):處芯積律】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論