Skip to content

Instantly share code, notes, and snippets.

@brabect1
Last active January 21, 2025 18:23
Show Gist options
  • Save brabect1/9dca224918fbbebc9d7ecdb8e7b50795 to your computer and use it in GitHub Desktop.
Save brabect1/9dca224918fbbebc9d7ecdb8e7b50795 to your computer and use it in GitHub Desktop.
LFSR based implementation of CRC-8-CCITT #crc #lfsr #SystemVerilog

LFSR based implementation of CRC-8-CCITT

/*
Copyright 2025 Tomas Brabec
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Changelog:
2025, Jan, Tomas Brabec
- Created.
*/
// CRC-8-CCITT: x^8 + x^2 + x + 1
//
// +----------------------------------------------------------------+
// | +-->NOT---> Data_out |
// | | |
// v | +--+--+--+--+--+--+ +--+ +--+ |
// +<--XOR------+---| 7| 6| 5| 4| 3| 2|<--XOR<--| 1|<--XOR<--| 0|<---+ +-- Serial_Input
// | +--+--+--+--+--+--+ ^ +--+ ^ +--+ |
// | | | |
// +---------------------------------------+------------+------------+
// Fig.1. Principal schematic of CRC-CCITT computation block
// (so-called Galois form of LFSR)
//
//
// Output <--- XOR <-------------- Input_A
// ^
// | ___
// | / |---------- CE (Count Enable)
// +--( & |
// \ |---------- Input_B
// ---
// Fig.2. Schematic of modified XOR gate
// (Output=Input_A when CE=0 and the LFSR behaves like a simple shift register)
//
// Use the following link for calculating reference CRC:
// https://crccalc.com/?crc=00,%2078,%20f0&method=CRC-8/ROHC&datatype=hex&outtype=hex
/**
* Implements LFSR (Linear Feedback Shift Register) in Galois form implementing the
* CRC-8-CCITT with polynomial x^8+x^2+x+1.
*
* Serial data would come LSB first; e.g. 96h would come in as the following sequence of bits:
* 0 1 1 0 1 0 0 1.
*
* `se` (shift enable) signals valid input data. `ce` (count enable) signals when to calculate
* CRC and when to simply shift-in the serial input data.
*
* Serial data out is meant to shift-out the inverted value of CRC, LSB first, during the post
* computation LFSR reload (i.e. se=1 and ce=0). The bit stream can be simply concatenated to
* the input stream and to yield the magic constant in the validating CRC module.
*
* The parallel data out represents the LFSR value. Note that the CRC value is then bit-reverse
* of the LFSR value.
*/
module crc_8_ccitt(
input logic clk,
input logic rst_n,
input logic din,
output logic dout,
output logic[7:0] lfsr,
input logic ce, // count enable
input logic se // shift enable (or data valid)
);
logic[7:0] lfsr_comb;
logic lfsr_in;
assign dout = ~lfsr[7];
assign lfsr_in = (lfsr[7] & ce) ^ din;
assign lfsr_comb = {lfsr[6:0] ^ {5'd0, (ce & lfsr_in), (ce & lfsr_in)}, lfsr_in};
always_ff @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
lfsr <= 8'hFF;
end
else if (se) begin
lfsr <= lfsr_comb;
end
end
endmodule
module tb;
logic clk;
logic rst_n = 1'b1;
logic ce;
logic se;
logic din;
logic dout;
logic[7:0] lfsr_act;
logic[7:0] lfsr_chk;
crc_8_ccitt u_crc_act(
.clk,
.rst_n,
.ce,
.se,
.din,
.dout,
.lfsr(lfsr_act)
);
logic chkin;
assign chkin = (se & ~ce) ? dout : din;
crc_8_ccitt u_crc_chk(
.clk,
.rst_n,
.ce(se),
.se,
.din(chkin),
.dout(),
.lfsr(lfsr_chk)
);
initial begin
clk = 1'b0;
while (1) begin
#10ns;
clk = ~clk;
end
end
logic[7:0] chk_reg;
always_ff @(posedge clk) begin
if (se & ~ce) begin
chk_reg <= {chkin, chk_reg[7:1]};
end
end
task reset;
rst_n = 1'b0;
repeat(3) @(negedge clk);
rst_n = 1'b1;
endtask
task calculate(
input logic[7:0] data[$]
);
automatic string s;
s = "";
foreach(data[i]) begin
s = {s, i==0 ? "" : ", ", $sformatf("%2hh", data[i])};
end
$display("\n%0s\ndata=%0s", "---------------", s);
@(posedge clk); #1;
se = 1;
ce = 1;
foreach(data[i]) begin
//for (int j=7; j >=0; j--) begin
for (int j=0; j < 8; j++) begin
din = data[i][j];
@(posedge clk); #1;
end
end
ce = 0;
// Note: The CRC value is bit reversed of the LFSR value. That is,
// if LFSR is [n:0], then CRC value is [0:n].
$display("\n act=%2hh\n chk=%2hh\n crc=%2hh\n~crc=%2hh", lfsr_act, lfsr_chk, {<<{lfsr_act}}, ~{<<{lfsr_act}});
din = 1;
repeat($size(lfsr_act)) @(posedge clk); #1;
se = 0;
din = 0;
$display("\nact=%2hh\nchk=%2hh\nreg=%2hh", lfsr_act, lfsr_chk, chk_reg);
reset();
repeat(16) @(negedge clk);
endtask
initial begin
automatic logic[7:0] data[$];
din = 0;
se = 0;
ce = 0;
reset();
data.delete();
data.push_back(8'h00);
calculate(data);
data.delete();
data.push_back(8'h11);
calculate(data);
data.delete();
data.push_back(8'h88);
calculate(data);
data.delete();
data.push_back(8'h88);
data.push_back(8'h11);
calculate(data);
data.delete();
data.push_back(8'h11);
data.push_back(8'h88);
calculate(data);
// data.delete();
// data.push_back(8'h00);
// data.push_back(8'he1);
// data.push_back(8'hf0);
// calculate(data);
//
// data.delete();
// data.push_back(8'h00);
// data.push_back(8'hf0);
// data.push_back(8'he1);
// calculate(data);
//
// data.delete();
// data.push_back(8'h00);
// data.push_back(8'h0f);
// data.push_back(8'h87);
// calculate(data);
data.delete();
data.push_back(8'h00);
calculate(data);
data.delete();
data.push_back(8'h00);
data.push_back(8'h87);
data.push_back(8'h0f);
calculate(data);
data.delete();
data.push_back(8'h00);
data.push_back(8'h78);
data.push_back(8'hf0);
calculate(data);
repeat(16) @(negedge clk);
reset();
$stop;
end
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment