From e03091e7e2d44f8f40e5445b84eece4d73a5bf1b Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 24 Sep 2014 22:48:36 +0200 Subject: [PATCH] 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. --- examples/basic/crc.py | 13 ++++ migen/actorlib/crc.py | 135 ++++++++++++++++++++++++++++++++++++++++++ migen/genlib/crc.py | 112 +++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 examples/basic/crc.py create mode 100644 migen/actorlib/crc.py create mode 100644 migen/genlib/crc.py diff --git a/examples/basic/crc.py b/examples/basic/crc.py new file mode 100644 index 00000000..c74b8b09 --- /dev/null +++ b/examples/basic/crc.py @@ -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)) diff --git a/migen/actorlib/crc.py b/migen/actorlib/crc.py new file mode 100644 index 00000000..58097a6a --- /dev/null +++ b/migen/actorlib/crc.py @@ -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) diff --git a/migen/genlib/crc.py b/migen/genlib/crc.py new file mode 100644 index 00000000..afd40fd5 --- /dev/null +++ b/migen/genlib/crc.py @@ -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<