CRC校验——一道乐鑫笔试题
乐鑫笔试
题目:请将下面这段 C 语言描述的串行处理过程,转换为单拍完成的并行处理,并用可综合的 Verilog 来描述。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
unsigned char cal_table_high_first(unsigned char value) { unsigned char i; unsigned char checksum = value; for (i=8; i>0; --i) { if (checksum & 0x80) checksum = (checksum << 1) ^ 0x31; else checksum = (checksum << 1); } return checksum; }
可以看出其实为CRC校验 CRC-8:x^8+x^5+x^4+1 0x31(0x131)
算法C语言实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> int main(){ unsignedchar cal_table_high_first(unsignedcharvalue); unsignedchar data; for (unsignedchar i = 0; i < 16;++i) { data= cal_table_high_first(i); printf("value =0x%0x:check_sum=0x%0x \n", i, data); } getchar(); } unsignedchar cal_table_high_first(unsignedcharvalue) { unsignedchar i; unsigned char check_sum = value; for (i = 8; i > 0;--i) { if (check_sum &0x80) { check_sum= (check_sum << 1) ^ 0x31; } else { check_sum= (check_sum << 1); } } return check_sum; }
输出结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 value =0x0:check_sum=0x0 2 value =0x1:check_sum=0x31 3 value =0x2:check_sum=0x62 4 value =0x3:check_sum=0x53 5 value =0x4:check_sum=0xc4 6 value =0x5:check_sum=0xf5 7 value =0x6:check_sum=0xa6 8 value =0x7:check_sum=0x97 9 value =0x8:check_sum=0xb9 10 value =0x9:check_sum=0x88 11 value =0xa:check_sum=0xdb 12 value =0xb:check_sum=0xea 13 value =0xc:check_sum=0x7d 14 value =0xd:check_sum=0x4c 15 value =0xe:check_sum=0x1f 16 value =0xf:check_sum=0x2e
C语言作为参考模型,用于后续Verilog的功能仿真。
该算法逻辑如下:
输入一个8bit的数,首先判断最高位是否为1,如果为1则左移一位,并且和8‘b00110001异或;如果最高位不为1则左移一位。此过程执行8次。
此时我们来看一下异或操作的真值表
我们可以看出:任何数与0异或都等于它本身,即0^x=x。所以我们可以把算法流程变换为:
8'h31 = 8'b00110001, 8'h00 = 8'b00000000,设左移前最高位为M,可以将判断左移前最高位是否为1的过程省略,直接与8'b00MM000M异或,此时流程图可以简化为:
由此,我们可以将循环解开,设输入为一个8bit数C[7:0],下面为解循环过程。
根据上述结果,可以用verilog描述。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 module loop1( 2 input clk, 3 input rst_n, 4 input [7:0] check_sum, 5 output reg [7:0] check_sum_o 6 ); 7 //reg [7:0] check_sum_o; 8 always @ (posedge clk or negedge rst_n) 9 if(!rst_n) 10 begin 11 check_sum_o<= 8'h0; 12 end 13 else 14 begin 15 check_sum_o[7]<= check_sum[3]^check_sum[2]^check_sum[5]; 16 check_sum_o[6]<= check_sum[2]^check_sum[1]^check_sum[4]^check_sum[7]; 17 check_sum_o[5]<= check_sum[1]^check_sum[7]^check_sum[0]^check_sum[3]^check_sum[6]; 18 check_sum_o[4]<= check_sum[7]^check_sum[0]^check_sum[3]^check_sum[6]; 19 check_sum_o[3]<= check_sum[3]^check_sum[7]^check_sum[6]; 20 check_sum_o[2]<= check_sum[2]^check_sum[6]^check_sum[5]; 21 check_sum_o[1]<= check_sum[1]^check_sum[5]^check_sum[4]^check_sum[7]; 22 check_sum_o[0]<= check_sum[0]^check_sum[4]^check_sum[3]^check_sum[6]; 23 end 24 25 endmodule
testbench:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 module loop1_tb; 2 reg clk; 3 reg rst_n; 4 reg [7:0] check_sum; 5 wire [7:0] check_sum_o; 6 always #1 clk=~clk; 7 initial 8 begin 9 clk = 0; 10 rst_n = 0; 11 #10 12 rst_n = 1; 13 for (check_sum=0;check_sum<16;check_sum=check_sum+1) 14 begin 15 #2 16 //check_sum = i; 17 $display ("check_sum = %h",check_sum_o); 18 if (check_sum == 15) $stop; 19 end 20 //$stop; 21 end 22 loop1 loop1_i1( 23 .clk(clk), 24 .rst_n(rst_n), 25 .check_sum(check_sum), 26 .check_sum_o(check_sum_o) 27 ); 28 endmodule
打印结果为:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # check_sum = 00 2 # check_sum = 31 3 # check_sum = 62 4 # check_sum = 53 5 # check_sum = c4 6 # check_sum = f5 7 # check_sum = a6 8 # check_sum = 97 9 # check_sum = b9 10 # check_sum = 88 11 # check_sum = db 12 # check_sum = ea 13 # check_sum = 7d 14 # check_sum = 4c 15 # check_sum = 1f 16 # check_sum = 2e
loop2.v的实现和loop1.v类似,只是代码量更少。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 module loop2( 2 input clk, 3 input rst_n, 4 input [7:0] check_sum, 5 output reg [7:0] check_sum_o 6 ); 7 integer i; 8 //reg [7:0] check_sum_o; 9 reg [7:0] ccc; 10 always @ (posedge clk or negedge rst_n) 11 if(!rst_n) 12 begin 13 check_sum_o= 8'h0; 14 end 15 else 16 begin 17 ccc = check_sum; 18 for(i=0;i<8;i=i+1) 19 begin 20 ccc ={ccc[6:0],1'b0}^({8{ccc[7]}} & 8'h31); 21 end 22 check_sum_o = ccc; 23 end 24 25 endmodule
其实也可以将C语言函数封装成Verilog的function,然后在在单周期内进行赋值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
module loop3( input clk, input rst_n, input [7:0] check_sum, output reg [7:0] check_sum_o ); integer i; function [7:0] cal_table_high_first; input[7:0] value; reg[7:0] ccc ; reg[7:0] flag ; begin ccc= value; for(i=0;i<8;i=i+1) begin flag= ccc & 8'h80 ; if(flag!= 0 ) ccc = (ccc <<1) ^ 8'h31 ; elseccc = (ccc <<1) ; end cal_table_high_first= ccc; end endfunction always @ (posedge clk or negedge rst_n) if(!rst_n) begin check_sum_o= 8'h0; end else begin check_sum_o<= cal_table_high_first(check_sum) ; end endmodule
综上,loop1.v和loop2.v的主要贡献是解开了算法实现的if-else判断。至于loop3.v中,将C语言描述的功能封装成fucntion,直接单周期完成赋值的实现方式在逻辑综合后是否增加了if-else判断语句的硬件开销不在本文讨论范围内。
这和设计者施加的时序约束和综合工具算法有关。