mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
link: add CRC and testbench
This commit is contained in:
parent
449daedab7
commit
8062298668
4 changed files with 168 additions and 1 deletions
106
lib/sata/link/crc.py
Normal file
106
lib/sata/link/crc.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.genlib.misc import optree
|
||||
|
||||
class CRCEngine(Module):
|
||||
"""Cyclic Redundancy Check Engine
|
||||
|
||||
Compute next CRC value from last CRC value and data input using
|
||||
an optimized asynchronous LFSR.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
width : int
|
||||
Width of the data bus and CRC.
|
||||
polynom : int
|
||||
Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC)
|
||||
|
||||
Attributes
|
||||
----------
|
||||
d : in
|
||||
Data input.
|
||||
last : in
|
||||
last CRC value.
|
||||
next :
|
||||
next CRC value.
|
||||
"""
|
||||
def __init__(self, width, polynom):
|
||||
self.d = Signal(width)
|
||||
self.last = Signal(width)
|
||||
self.next = Signal(width)
|
||||
|
||||
###
|
||||
|
||||
|
||||
def _optimize_eq(l):
|
||||
"""
|
||||
Replace even numbers of XORs in the equation
|
||||
with an equivalent XOR
|
||||
"""
|
||||
d = {}
|
||||
for e in l:
|
||||
if e in d:
|
||||
d[e] += 1
|
||||
else:
|
||||
d[e] = 1
|
||||
r = []
|
||||
for key, value in d.items():
|
||||
if value%2 != 0:
|
||||
r.append(key)
|
||||
return r
|
||||
|
||||
new = Signal(32)
|
||||
self.comb += new.eq(self.last ^ self.d)
|
||||
|
||||
# compute and optimize CRC's LFSR
|
||||
curval = [[("new", i)] for i in range(width)]
|
||||
for i in range(width):
|
||||
feedback = curval.pop()
|
||||
curval.insert(0, feedback)
|
||||
for j in range(1, width-1):
|
||||
if (polynom&(1<<j)):
|
||||
curval[j] += feedback
|
||||
curval[j] = _optimize_eq(curval[j])
|
||||
|
||||
# implement logic
|
||||
for i in range(width):
|
||||
xors = []
|
||||
for t, n in curval[i]:
|
||||
if t == "new":
|
||||
xors += [new[n]]
|
||||
self.comb += self.next[i].eq(optree("^", xors))
|
||||
|
||||
@DecorateModule(InsertReset)
|
||||
@DecorateModule(InsertCE)
|
||||
class SATACRC(Module):
|
||||
"""SATA CRC
|
||||
|
||||
Implement a SATA CRC generator/checker
|
||||
|
||||
Attributes
|
||||
----------
|
||||
value : out
|
||||
CRC value (used for generator).
|
||||
error : out
|
||||
CRC error (used for checker).
|
||||
"""
|
||||
width = 32
|
||||
polynom = 0x04C11DB7
|
||||
init = 0x52325032
|
||||
check = 0xC704DD7B
|
||||
def __init__(self):
|
||||
self.d = Signal(self.width)
|
||||
self.value = Signal(self.width)
|
||||
self.error = Signal()
|
||||
|
||||
###
|
||||
|
||||
self.submodules.engine = CRCEngine(self.width, self.polynom)
|
||||
reg_i = Signal(self.width, reset=self.init)
|
||||
self.sync += reg_i.eq(self.engine.next)
|
||||
self.comb += [
|
||||
self.engine.d.eq(self.d),
|
||||
self.engine.last.eq(reg_i),
|
||||
|
||||
self.value.eq(reg_i),
|
||||
self.error.eq(self.engine.next != self.check)
|
||||
]
|
|
@ -1,4 +1,4 @@
|
|||
MSCDIR = ../../../
|
||||
MSCDIR = ../../../../
|
||||
PYTHON = python3
|
||||
|
||||
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
|
||||
|
@ -9,6 +9,7 @@ CFLAGS =-Wall -O0
|
|||
crc_tb:
|
||||
$(CC) $(CFLAGS) $(INC) -o crc crc.c
|
||||
./crc /> crc_ref
|
||||
$(CMD) crc_tb.py
|
||||
|
||||
scrambler_tb:
|
||||
$(CC) $(CFLAGS) $(INC) -o scrambler scrambler.c
|
||||
|
|
52
lib/sata/link/test/crc_tb.py
Normal file
52
lib/sata/link/test/crc_tb.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
from subprocess import check_output
|
||||
|
||||
from migen.fhdl.std import *
|
||||
|
||||
from lib.sata.std import *
|
||||
from lib.sata.link.crc import *
|
||||
|
||||
def check(ref, res):
|
||||
shift = 0
|
||||
while((ref[0] != res[0]) and (len(res)>1)):
|
||||
res.pop(0)
|
||||
shift += 1
|
||||
length = min(len(ref), len(res))
|
||||
errors = 0
|
||||
for i in range(length):
|
||||
if ref.pop(0) != res.pop(0):
|
||||
errors += 1
|
||||
return shift, length, errors
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
self.submodules.crc = SATACRC()
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
|
||||
# init CRC
|
||||
selfp.crc.d = 0x12345678
|
||||
selfp.crc.ce = 1
|
||||
selfp.crc.reset = 1
|
||||
yield
|
||||
selfp.crc.reset = 0
|
||||
|
||||
# get C code results
|
||||
ref = []
|
||||
f = open("crc_ref", "r")
|
||||
for l in f:
|
||||
ref.append(int(l, 16))
|
||||
f.close()
|
||||
|
||||
# log results
|
||||
res = []
|
||||
for i in range(256):
|
||||
res.append(selfp.crc.value)
|
||||
yield
|
||||
|
||||
# check results
|
||||
s, l, e = check(ref, res)
|
||||
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
|
||||
|
||||
if __name__ == "__main__":
|
||||
from migen.sim.generic import run_simulation
|
||||
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True)
|
8
lib/sata/std.py
Normal file
8
lib/sata/std.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from migen.fhdl.std import *
|
||||
|
||||
def phy_layout(dw):
|
||||
layout = [
|
||||
("p_packetized", True),
|
||||
("d", dw)
|
||||
]
|
||||
return layout
|
Loading…
Reference in a new issue