#!/usr/bin/env python # coding: utf-8 # # CRC checker generation # # 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. # In[1]: from myirl import * # ## Example serial CRC # # You may define your own serial CRC function here. For example, the CRC feedback register for the USB protocol: # In[2]: 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: # In[3]: 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: # In[4]: 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. # In[5]: 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: # In[6]: 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 # ## The CRC unit # # This actually creates the hardware. Note that we don't optimize logic here. We'll leave that to the synthesizer toolchain. # In[7]: @block def crc_unit(clk, en, reset, din, crcout, 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() # In[8]: 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.VHDL) return tmp # In[9]: POLYNOM = 0b100000100110000010001110110110111 tmp = test(8, 32, POLYNOM) # In[10]: get_ipython().system('cat {tmp[0]}') # ### TODO: Co-Simulation against 'golden' implementations