add generic CRCEngine, CRC32, CRCInserter and CRCChecker

CRCEngine implements a generic and optimized CRC LFSR. It will enable generation of CRC generators and checkers.
CRC32 is an implementation of IEEE 802.3 CRC using the CRCEngine.
CRC32Inserter and CRC32Checker have been tested on an ethernet MAC.
This commit is contained in:
Florent Kermarrec 2014-09-24 22:48:36 +02:00 committed by Sebastien Bourdeauducq
parent a03570ccca
commit e03091e7e2
3 changed files with 260 additions and 0 deletions

13
examples/basic/crc.py Normal file
View File

@ -0,0 +1,13 @@
from migen.fhdl.std import *
from migen.genlib.crc import CRC32
from migen.fhdl import verilog
class Example(Module):
def __init__(self, width):
crc32 = CRC32(width)
self.submodules += crc32
self.ios = {crc32.reset, crc32.ce,
crc32.d, crc32.value, crc32.error}
example = Example(8)
print(verilog.convert(example, example.ios))

135
migen/actorlib/crc.py Normal file
View File

@ -0,0 +1,135 @@
from migen.fhdl.std import *
from migen.genlib.fsm import FSM, NextState
from migen.genlib.record import *
from migen.genlib.misc import chooser
from migen.genlib.crc import *
from migen.flow.actor import Sink, Source
class CRCInserter(Module):
"""CRC Inserter
Append a CRC at the end of each packet.
Parameters
----------
layout : layout
Layout of the dataflow.
Attributes
----------
sink : in
Packets input without CRC.
source : out
Packets output with CRC.
"""
def __init__(self, crc_class, layout):
self.sink = Sink(layout, True)
self.source = Source(layout, True)
self.busy = Signal()
###
dw = flen(self.sink.payload.d)
self.submodules.crc = crc_class(dw)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.crc.reset.eq(1),
self.sink.ack.eq(1),
If(self.sink.stb & self.sink.sop,
self.sink.ack.eq(0),
NextState("COPY"),
)
)
fsm.act("COPY",
self.crc.ce.eq(self.sink.stb & self.source.ack),
self.crc.d.eq(self.sink.payload.d),
Record.connect(self.sink, self.source),
self.source.eop.eq(0),
If(self.sink.stb & self.sink.eop & self.source.ack,
NextState("INSERT"),
)
)
ratio = self.crc.width//dw
cnt = Signal(max=ratio, reset=ratio-1)
cnt_done = Signal()
fsm.act("INSERT",
self.source.stb.eq(1),
chooser(self.crc.value, cnt, self.source.payload.d, reverse=True),
If(cnt_done,
self.source.eop.eq(1),
If(self.source.ack, NextState("IDLE"))
)
)
self.comb += cnt_done.eq(cnt == 0)
self.sync += \
If(fsm.ongoing("IDLE"),
cnt.eq(cnt.reset)
).Elif(fsm.ongoing("INSERT") & ~cnt_done,
cnt.eq(cnt - self.source.ack)
)
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
class CRC32Inserter(CRCInserter):
def __init__(self, layout):
CRCInserter.__init__(self, CRC32, layout)
class CRCChecker(Module):
"""CRC Checker
Check CRC at the end of each packet.
Parameters
----------
layout : layout
Layout of the dataflow.
Attributes
----------
sink : in
Packets input with CRC.
source : out
Packets output with CRC and "discarded" set to 0
on eop if CRC OK / set to 1 is CRC KO.
"""
def __init__(self, crc_class, layout):
self.sink = Sink(layout, True)
self.source = Source(layout, True)
self.busy = Signal()
###
dw = flen(self.sink.payload.d)
self.submodules.crc = crc_class(dw)
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
self.crc.reset.eq(1),
self.sink.ack.eq(self.sink.stb),
If(self.sink.stb & self.sink.sop,
self.sink.ack.eq(0),
NextState("COPY")
)
)
fsm.act("COPY",
Record.connect(self.sink, self.source),
self.crc.ce.eq(self.sink.stb & (self.sink.ack | self.sink.eop)),
self.crc.d.eq(self.sink.payload.d),
If(self.sink.stb & self.sink.eop,
self.sink.ack.eq(0),
self.source.stb.eq(0),
NextState("CHECK")
)
)
fsm.act("CHECK",
Record.connect(self.sink, self.source),
self.source.discarded.eq(self.crc.error),
If(self.source.stb & self.source.ack, NextState("IDLE"))
)
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
class CRC32Checker(CRCChecker):
def __init__(self, layout):
CRCChecker.__init__(self, CRC32, layout)

112
migen/genlib/crc.py Normal file
View File

@ -0,0 +1,112 @@
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
----------
dat_width : int
Width of the data bus.
width : int
Width of the 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, dat_width, width, polynom):
self.d = Signal(dat_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
# compute and optimize CRC's LFSR
curval = [[("state", i)] for i in range(width)]
for i in range(dat_width):
feedback = curval.pop() + [("din", i)]
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 == "state":
xors += [self.last[n]]
elif t == "din":
xors += [self.d[n]]
self.comb += self.next[i].eq(optree("^", xors))
@DecorateModule(InsertReset)
@DecorateModule(InsertCE)
class CRC32(Module):
"""IEEE 802.3 CRC
Implement an IEEE 802.3 CRC generator/checker.
Parameters
----------
dat_width : int
Width of the data bus.
Attributes
----------
d : in
Data input.
value : out
CRC value (used for generator).
error : out
CRC error (used for checker).
"""
width = 32
polynom = 0x04C11DB7
check = 0xC704DD7B
def __init__(self, dat_width):
self.d = Signal(dat_width)
self.value = Signal(self.width)
self.error = Signal()
###
self.submodules.engine = CRCEngine(dat_width, self.width, self.polynom)
reg = Signal(self.width, reset=2**self.width-1)
self.sync += reg.eq(self.engine.next)
self.comb += [
self.engine.d.eq(self.d),
self.engine.last.eq(reg),
self.value.eq(~reg[::-1]),
self.error.eq(reg != self.check)
]