This Notebook implements a simple arbitrary CRC unit generator. It makes use of the linear properties of the CRC operation and constructs 'participation' matrices out of the binary basis vector set (1 << j)
(j
being the coordinate). A given bit-serial implementation of a CRC is probed for each of these basis vectors and the final logic constructed out of the participation matrices.
Note that no cyhdl syntax is used here. Due to its procedural nature, the logic is generated using IRL notation.
from myirl import *
You may define your own serial CRC function here. For example, the CRC feedback register for the USB protocol:
def serial_crc5_usb(c, din, M, dummy):
fb = (c >> (M-1)) ^ din
mask = (1 << M) - 1
return ((c << 1) & mask) ^ (fb << 2) | fb
Or a generic CRC with given polynom:
def serial_crc(c, din, M, POLY):
# print(type(c))
mask = (1 << M) - 1
fb = (c >> (M-1)) ^ din
rotated = (c << 1) & mask
if fb:
v = rotated ^ (POLY & mask)
else:
v = rotated
return v
To determine the CRC for a data word, we just create a function that runs the serial CRC sequence N
times:
def crc_dataword(cstart, din, crc_serial, M, POLY):
_c0 = int(cstart)
N = len(din)
for i in range(N):
_c0 = crc_serial(_c0, din[i], M, POLY)
return _c0
Create the 'participation' tables for the data and current CRC bits.
def gen_matrices(M, N, crc_s, POLY):
"Not so effective function to generate particular matrices"
A = N * [ None, ]
c0, c1 = [ Signal(intbv(0)[M:]) for _ in range(2) ]
din = intbv(1)[N+1:]
for i in range(N):
v = crc_dataword(0, din[N:], crc_s, M, POLY)
din <<= 1
A[i] = int(v)
B = M * [ None, ]
m = intbv(1)[M+1:]
din = intbv(0)[N:]
for i in range(M):
v = crc_dataword(m, din, crc_s, M, POLY)
m <<= 1
B[i] = int(v)
return A, B
This function creates the wire connections and combinatorial logic according to the participation table:
def gen_crc_xor(A, B, din, crc0, crc1):
m, n = len(B), len(A)
v = 1
connections = []
for i in range(m):
wires = []
md = ""
for j in range(n):
if A[j] & v:
wires += [ din[n-1-j] ]
md += '1' if A[j] & v else '0'
mc = ""
for j in range(m):
if B[j] & v:
wires += [ crc0[j] ]
mc += '1' if B[j] & v else '0'
v <<= 1
print(md, mc)
if len(wires) > 0:
first = wires[0]
for e in wires[1:]:
first = first ^ e
connections += [ crc1[i].set(first) ]
else:
raise(ValueError, "Bad CRC bit. Probably broken polynom.")
return connections
This actually creates the hardware. Note that we don't optimize logic here. We'll leave that to the synthesizer toolchain.
from myirl.library.basictypes import Bool
@block
def crc_unit(clk : ClkSignal, en : (Bool, bool), reset : ResetSignal,
din : Signal, crcout : Signal.Output, M, N, POLY, STARTVAL):
crc = Signal(intbv(STARTVAL)[M:])
crcnext = [ Signal(bool(), name = 'c%d' % i) for i in range(M) ]
print("generate matrices...")
M1, M2 = gen_matrices(M, N, serial_crc, POLY)
print("create xor map")
wires = gen_crc_xor(M1, M2, din, crc, crcnext)
@genprocess(clk, EDGE=clk.POS, RESET=reset)
def crc_init():
yield [ crc.set(concat(*reversed(crcnext))) ]
connections = [ crcout.set(crc) ]
return locals()
def test(DW, CW, POLYNOM):
clk = ClkSignal()
data = Signal(intbv()[DW:])
crc = Signal(intbv()[CW:])
r = ResetSignal(ResetSignal.POS_SYNC)
inst = crc_unit(clk, True, r, data, crc, CW, DW, POLYNOM, 0xffffffff)
tmp = inst.elab(targets.Verilog)
return tmp
POLYNOM = 0b100000100110000010001110110110111
tmp = test(8, 32, POLYNOM)
generate matrices... create xor map 01000001 00000000000000000000000010000010 11000011 00000000000000000000000011000011 11000111 00000000000000000000000011100011 10001110 00000000000000000000000001110001 01011101 00000000000000000000000010111010 11111011 00000000000000000000000011011111 11110110 00000000000000000000000001101111 10101101 00000000000000000000000010110101 00011011 10000000000000000000000011011000 00110110 01000000000000000000000001101100 00101101 00100000000000000000000010110100 00011011 00010000000000000000000011011000 01110111 00001000000000000000000011101110 11101110 00000100000000000000000001110111 11011100 00000010000000000000000000111011 10111000 00000001000000000000000000011101 00110001 00000000100000000000000010001100 01100010 00000000010000000000000001000110 11000100 00000000001000000000000000100011 10001000 00000000000100000000000000010001 00010000 00000000000010000000000000001000 00100000 00000000000001000000000000000100 00000001 00000000000000100000000010000000 01000011 00000000000000010000000011000010 10000110 00000000000000001000000001100001 00001100 00000000000000000100000000110000 01011001 00000000000000000010000010011010 10110010 00000000000000000001000001001101 01100100 00000000000000000000100000100110 11001000 00000000000000000000010000010011 10010000 00000000000000000000001000001001 00100000 00000000000000000000000100000100 Writing 'crc_unit' to file /tmp/myirl_crc_unit_1f_3110x/crc_unit.v DEBUG Fallback wire for s_4da5
!cat {tmp[0]}
// File generated from source: // /tmp/ipykernel_40916/3709520757.py // (c) 2016-2022 section5.ch // Modifications may be lost, edit the source file instead. `timescale 1 ns / 1 ps `include "aux.v" // Architecture myIRL module crc_unit ( input wire /* std_ulogic */ clk, input wire /* std_ulogic */ reset, input wire [7:0] din, output wire [31:0] crcout ); // Local type declarations // Signal declarations reg [31:0] crc; wire /* std_ulogic */ c31; wire /* std_ulogic */ c30; wire /* std_ulogic */ c29; wire /* std_ulogic */ c28; wire /* std_ulogic */ c27; wire /* std_ulogic */ c26; wire /* std_ulogic */ c25; wire /* std_ulogic */ c24; wire /* std_ulogic */ c23; wire /* std_ulogic */ c22; wire /* std_ulogic */ c21; wire /* std_ulogic */ c20; wire /* std_ulogic */ c19; wire /* std_ulogic */ c18; wire /* std_ulogic */ c17; wire /* std_ulogic */ c16; wire /* std_ulogic */ c15; wire /* std_ulogic */ c14; wire /* std_ulogic */ c13; wire /* std_ulogic */ c12; wire /* std_ulogic */ c11; wire /* std_ulogic */ c10; wire /* std_ulogic */ c9; wire /* std_ulogic */ c8; wire /* std_ulogic */ c7; wire /* std_ulogic */ c6; wire /* std_ulogic */ c5; wire /* std_ulogic */ c4; wire /* std_ulogic */ c3; wire /* std_ulogic */ c2; wire /* std_ulogic */ c1; wire /* std_ulogic */ c0; always @ (posedge clk ) begin : CRC_INIT if (reset == 1'b1) begin crc <= 32'hffffffff; /* default */ end else begin crc <= {c31, c30, c29, c28, c27, c26, c25, c24, c23, c22, c21, c20, c19, c18, c17, c16, c15, c14, c13, c12, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0}; end end assign c0 = (((din[6] ^ din[0]) ^ crc[24]) ^ crc[30]); assign c1 = (((((((din[7] ^ din[6]) ^ din[1]) ^ din[0]) ^ crc[24]) ^ crc[25]) ^ crc[30]) ^ crc[31]); assign c2 = (((((((((din[7] ^ din[6]) ^ din[2]) ^ din[1]) ^ din[0]) ^ crc[24]) ^ crc[25]) ^ crc[26]) ^ crc[30]) ^ crc[31]); assign c3 = (((((((din[7] ^ din[3]) ^ din[2]) ^ din[1]) ^ crc[25]) ^ crc[26]) ^ crc[27]) ^ crc[31]); assign c4 = (((((((((din[6] ^ din[4]) ^ din[3]) ^ din[2]) ^ din[0]) ^ crc[24]) ^ crc[26]) ^ crc[27]) ^ crc[28]) ^ crc[30]); assign c5 = (((((((((((((din[7] ^ din[6]) ^ din[5]) ^ din[4]) ^ din[3]) ^ din[1]) ^ din[0]) ^ crc[24]) ^ crc[25]) ^ crc[27]) ^ crc[28]) ^ crc[29]) ^ crc[30]) ^ crc[31]); assign c6 = (((((((((((din[7] ^ din[6]) ^ din[5]) ^ din[4]) ^ din[2]) ^ din[1]) ^ crc[25]) ^ crc[26]) ^ crc[28]) ^ crc[29]) ^ crc[30]) ^ crc[31]); assign c7 = (((((((((din[7] ^ din[5]) ^ din[3]) ^ din[2]) ^ din[0]) ^ crc[24]) ^ crc[26]) ^ crc[27]) ^ crc[29]) ^ crc[31]); assign c8 = ((((((((din[4] ^ din[3]) ^ din[1]) ^ din[0]) ^ crc[0]) ^ crc[24]) ^ crc[25]) ^ crc[27]) ^ crc[28]); assign c9 = ((((((((din[5] ^ din[4]) ^ din[2]) ^ din[1]) ^ crc[1]) ^ crc[25]) ^ crc[26]) ^ crc[28]) ^ crc[29]); assign c10 = ((((((((din[5] ^ din[3]) ^ din[2]) ^ din[0]) ^ crc[2]) ^ crc[24]) ^ crc[26]) ^ crc[27]) ^ crc[29]); assign c11 = ((((((((din[4] ^ din[3]) ^ din[1]) ^ din[0]) ^ crc[3]) ^ crc[24]) ^ crc[25]) ^ crc[27]) ^ crc[28]); assign c12 = ((((((((((((din[6] ^ din[5]) ^ din[4]) ^ din[2]) ^ din[1]) ^ din[0]) ^ crc[4]) ^ crc[24]) ^ crc[25]) ^ crc[26]) ^ crc[28]) ^ crc[29]) ^ crc[30]); assign c13 = ((((((((((((din[7] ^ din[6]) ^ din[5]) ^ din[3]) ^ din[2]) ^ din[1]) ^ crc[5]) ^ crc[25]) ^ crc[26]) ^ crc[27]) ^ crc[29]) ^ crc[30]) ^ crc[31]); assign c14 = ((((((((((din[7] ^ din[6]) ^ din[4]) ^ din[3]) ^ din[2]) ^ crc[6]) ^ crc[26]) ^ crc[27]) ^ crc[28]) ^ crc[30]) ^ crc[31]); assign c15 = ((((((((din[7] ^ din[5]) ^ din[4]) ^ din[3]) ^ crc[7]) ^ crc[27]) ^ crc[28]) ^ crc[29]) ^ crc[31]); assign c16 = ((((((din[5] ^ din[4]) ^ din[0]) ^ crc[8]) ^ crc[24]) ^ crc[28]) ^ crc[29]); assign c17 = ((((((din[6] ^ din[5]) ^ din[1]) ^ crc[9]) ^ crc[25]) ^ crc[29]) ^ crc[30]); assign c18 = ((((((din[7] ^ din[6]) ^ din[2]) ^ crc[10]) ^ crc[26]) ^ crc[30]) ^ crc[31]); assign c19 = ((((din[7] ^ din[3]) ^ crc[11]) ^ crc[27]) ^ crc[31]); assign c20 = ((din[4] ^ crc[12]) ^ crc[28]); assign c21 = ((din[5] ^ crc[13]) ^ crc[29]); assign c22 = ((din[0] ^ crc[14]) ^ crc[24]); assign c23 = ((((((din[6] ^ din[1]) ^ din[0]) ^ crc[15]) ^ crc[24]) ^ crc[25]) ^ crc[30]); assign c24 = ((((((din[7] ^ din[2]) ^ din[1]) ^ crc[16]) ^ crc[25]) ^ crc[26]) ^ crc[31]); assign c25 = ((((din[3] ^ din[2]) ^ crc[17]) ^ crc[26]) ^ crc[27]); assign c26 = ((((((((din[6] ^ din[4]) ^ din[3]) ^ din[0]) ^ crc[18]) ^ crc[24]) ^ crc[27]) ^ crc[28]) ^ crc[30]); assign c27 = ((((((((din[7] ^ din[5]) ^ din[4]) ^ din[1]) ^ crc[19]) ^ crc[25]) ^ crc[28]) ^ crc[29]) ^ crc[31]); assign c28 = ((((((din[6] ^ din[5]) ^ din[2]) ^ crc[20]) ^ crc[26]) ^ crc[29]) ^ crc[30]); assign c29 = ((((((din[7] ^ din[6]) ^ din[3]) ^ crc[21]) ^ crc[27]) ^ crc[30]) ^ crc[31]); assign c30 = ((((din[7] ^ din[4]) ^ crc[22]) ^ crc[28]) ^ crc[31]); assign c31 = ((din[5] ^ crc[23]) ^ crc[29]); assign crcout = crc; endmodule // crc_unit