diff --git a/liteethernet/common.py b/liteethernet/common.py new file mode 100644 index 000000000..177943ee5 --- /dev/null +++ b/liteethernet/common.py @@ -0,0 +1,14 @@ +from migen.fhdl.std import * +from migen.flow.actor import EndpointDescription + +eth_mtu = 1532 +eth_preamble = 0xD555555555555555 +buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) + +def eth_description(dw): + layout = [ + ("d", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(layout, packetized=True) diff --git a/liteethernet/mac/__init__.py b/liteethernet/mac/__init__.py index a7ea0c2c2..e9b2ccfaa 100644 --- a/liteethernet/mac/__init__.py +++ b/liteethernet/mac/__init__.py @@ -1,66 +1,9 @@ -# This file is Copyright (c) 2014 Florent Kermarrec -# License: BSD +from liteethernet.common import * +from liteethernet.mac import LiteEthernetMAC -from migen.fhdl.std import * - -from migen.bus import wishbone -from migen.actorlib.fifo import AsyncFIFO -from migen.actorlib.structuring import Converter, Pipeline -from migen.bank.eventmanager import SharedIRQ -from migen.bank.description import * -from migen.fhdl.simplify import * - -from misoclib.ethmac.common import * -from misoclib.ethmac.preamble import PreambleInserter, PreambleChecker -from migen.actorlib.crc import CRC32Inserter, CRC32Checker -from misoclib.ethmac.last_be import TXLastBE, RXLastBE -from misoclib.ethmac.sram import SRAMWriter, SRAMReader - -class EthMAC(Module, AutoCSR): - def __init__(self, phy, interface="wishbone", with_hw_preamble_crc=True, endianness="be"): - # Preamble / CRC (optional) - if with_hw_preamble_crc: - self._hw_preamble_crc = CSRStatus(reset=1) - # Preamble insert/check - preamble_inserter = PreambleInserter(phy.dw) - preamble_checker = PreambleChecker(phy.dw) - self.submodules += RenameClockDomains(preamble_inserter, "eth_tx") - self.submodules += RenameClockDomains(preamble_checker, "eth_rx") - - # CRC insert/check - crc32_inserter = CRC32Inserter(eth_description(phy.dw)) - crc32_checker = CRC32Checker(eth_description(phy.dw)) - self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") - self.submodules += RenameClockDomains(crc32_checker, "eth_rx") - - # Delimiters - tx_last_be = TXLastBE(phy.dw) - rx_last_be = RXLastBE(phy.dw) - self.submodules += RenameClockDomains(tx_last_be, "eth_tx") - self.submodules += RenameClockDomains(rx_last_be, "eth_rx") - - # Converters - reverse = endianness == "be" - tx_converter = Converter(eth_description(32), eth_description(phy.dw), reverse=reverse) - rx_converter = Converter(eth_description(phy.dw), eth_description(32), reverse=reverse) - self.submodules += RenameClockDomains(tx_converter, "eth_tx") - self.submodules += RenameClockDomains(rx_converter, "eth_rx") - - # Cross Domain Crossing - tx_cdc = AsyncFIFO(eth_description(32), 4) - rx_cdc = AsyncFIFO(eth_description(32), 4) - self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) - self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) - - # Graph - if with_hw_preamble_crc: - rx_pipeline = [phy, preamble_checker, crc32_checker, rx_last_be, rx_converter, rx_cdc] - tx_pipeline = [tx_cdc, tx_converter, tx_last_be, crc32_inserter, preamble_inserter, phy] - else: - rx_pipeline = [phy, rx_last_be, rx_converter, rx_cdc] - tx_pipeline = [tx_cdc, tx_converter, tx_last_be, phy] - self.submodules.rx_pipeline = Pipeline(*rx_pipeline) - self.submodules.tx_pipeline = Pipeline(*tx_pipeline) +class LiteEthernetMAC(Module, AutoCSR): + def __init__(self, phy, frontend="wishbone", with_hw_preamble_crc=True, endianness="be"): + self.submodules.core = LiteEthernetMAC(phy, with_hw_preamble, endianness) if interface == "wishbone": nrxslots = 2 diff --git a/liteethernet/mac/core/__init__.py b/liteethernet/mac/core/__init__.py index e69de29bb..a361d5dcb 100644 --- a/liteethernet/mac/core/__init__.py +++ b/liteethernet/mac/core/__init__.py @@ -0,0 +1,54 @@ + +from liteethernet.common import * +from liteethernet.mac.common import * +from liteethernet.mac.preamble import PreambleInserter, PreambleChecker +from liteethernet.mac.crc import CRC32Inserter, CRC32Checker +from liteethernet.mac.last_be import TXLastBE, RXLastBE + +class LiteEthernetMACCore(Module, AutoCSR): + def __init__(self, phy, with_hw_preamble_crc=True, endianness="be"): + # Preamble / CRC (optional) + if with_hw_preamble_crc: + self._hw_preamble_crc = CSRStatus(reset=1) + # Preamble insert/check + preamble_inserter = PreambleInserter(phy.dw) + preamble_checker = PreambleChecker(phy.dw) + self.submodules += RenameClockDomains(preamble_inserter, "eth_tx") + self.submodules += RenameClockDomains(preamble_checker, "eth_rx") + + # CRC insert/check + crc32_inserter = CRC32Inserter(eth_description(phy.dw)) + crc32_checker = CRC32Checker(eth_description(phy.dw)) + self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") + self.submodules += RenameClockDomains(crc32_checker, "eth_rx") + + # Delimiters + tx_last_be = TXLastBE(phy.dw) + rx_last_be = RXLastBE(phy.dw) + self.submodules += RenameClockDomains(tx_last_be, "eth_tx") + self.submodules += RenameClockDomains(rx_last_be, "eth_rx") + + # Converters + reverse = endianness == "be" + tx_converter = Converter(eth_description(32), eth_description(phy.dw), reverse=reverse) + rx_converter = Converter(eth_description(phy.dw), eth_description(32), reverse=reverse) + self.submodules += RenameClockDomains(tx_converter, "eth_tx") + self.submodules += RenameClockDomains(rx_converter, "eth_rx") + + # Cross Domain Crossing + tx_cdc = AsyncFIFO(eth_description(32), 4) + rx_cdc = AsyncFIFO(eth_description(32), 4) + self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) + self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) + + # Graph + if with_hw_preamble_crc: + rx_pipeline = [phy, preamble_checker, crc32_checker, rx_last_be, rx_converter, rx_cdc] + tx_pipeline = [tx_cdc, tx_converter, tx_last_be, crc32_inserter, preamble_inserter, phy] + else: + rx_pipeline = [phy, rx_last_be, rx_converter, rx_cdc] + tx_pipeline = [tx_cdc, tx_converter, tx_last_be, phy] + self.submodules.rx_pipeline = Pipeline(*rx_pipeline) + self.submodules.tx_pipeline = Pipeline(*tx_pipeline) + + self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source diff --git a/liteethernet/mac/core/crc.py b/liteethernet/mac/core/crc.py new file mode 100644 index 000000000..8a058faeb --- /dev/null +++ b/liteethernet/mac/core/crc.py @@ -0,0 +1,284 @@ +from migen.fhdl.std import * +from migen.genlib.fsm import FSM, NextState +from migen.genlib.record import * +from migen.genlib.misc import optree, chooser +from migen.genlib.crc import * +from migen.flow.actor import Sink, Source +from migen.actorlib.fifo import SyncFIFO + +from collections import OrderedDict + +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 = OrderedDict() + 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)] + for j in range(width-1): + if (polynom & (1<<(j+1))): + curval[j] += feedback + curval[j] = _optimize_eq(curval[j]) + curval.insert(0, feedback) + + # 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 + init = 2**width-1 + 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=self.init) + 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(self.engine.next != self.check) + ] + +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 = Sink(layout) + self.source = source = Source(layout) + self.busy = Signal() + + ### + + dw = flen(sink.d) + crc = crc_class(dw) + fsm = FSM(reset_state="IDLE") + self.submodules += crc, fsm + + fsm.act("IDLE", + crc.reset.eq(1), + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("COPY"), + ) + ) + fsm.act("COPY", + crc.ce.eq(sink.stb & source.ack), + crc.d.eq(sink.d), + Record.connect(sink, source), + source.eop.eq(0), + If(sink.stb & sink.eop & source.ack, + NextState("INSERT"), + ) + ) + ratio = crc.width//dw + if ratio > 1: + cnt = Signal(max=ratio, reset=ratio-1) + cnt_done = Signal() + fsm.act("INSERT", + source.stb.eq(1), + chooser(crc.value, cnt, source.d, reverse=True), + If(cnt_done, + source.eop.eq(1), + If(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 - source.ack) + ) + else: + fsm.act("INSERT", + source.stb.eq(1), + source.eop.eq(1), + source.d.eq(crc.value), + If(source.ack, NextState("IDLE")) + ) + 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 without CRC and "error" set to 0 + on eop when CRC OK / set to 1 when CRC KO. + """ + def __init__(self, crc_class, layout): + self.sink = sink = Sink(layout) + self.source = source = Source(layout) + self.busy = Signal() + + ### + + dw = flen(sink.d) + crc = crc_class(dw) + self.submodules += crc + ratio = crc.width//dw + + error = Signal() + fifo = InsertReset(SyncFIFO(layout, ratio + 1)) + self.submodules += fifo + + fsm = FSM(reset_state="RESET") + self.submodules += fsm + + fifo_in = Signal() + fifo_out = Signal() + fifo_full = Signal() + + self.comb += [ + fifo_full.eq(fifo.fifo.level == ratio), + fifo_in.eq(sink.stb & (~fifo_full | fifo_out)), + fifo_out.eq(source.stb & source.ack), + + Record.connect(sink, fifo.sink), + fifo.sink.stb.eq(fifo_in), + self.sink.ack.eq(fifo_in), + + source.stb.eq(sink.stb & fifo_full), + source.sop.eq(fifo.source.sop), + source.eop.eq(sink.eop), + fifo.source.ack.eq(fifo_out), + source.payload.eq(fifo.source.payload), + + source.error.eq(sink.error | crc.error), + ] + + fsm.act("RESET", + crc.reset.eq(1), + fifo.reset.eq(1), + NextState("IDLE"), + ) + fsm.act("IDLE", + crc.d.eq(sink.d), + If(sink.stb & sink.sop & sink.ack, + crc.ce.eq(1), + NextState("COPY") + ) + ) + fsm.act("COPY", + crc.d.eq(sink.d), + If(sink.stb & sink.ack, + crc.ce.eq(1), + If(sink.eop, + NextState("RESET") + ) + ) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + +class CRC32Checker(CRCChecker): + def __init__(self, layout): + CRCChecker.__init__(self, CRC32, layout) diff --git a/liteethernet/mac/core/last_be.py b/liteethernet/mac/core/last_be.py index da55faee1..555e523f5 100644 --- a/liteethernet/mac/core/last_be.py +++ b/liteethernet/mac/core/last_be.py @@ -2,7 +2,8 @@ from migen.fhdl.std import * from migen.genlib.record import * from migen.flow.actor import Sink, Source -from misoclib.ethmac.common import * +from liteethernet.common import * +from liteethernet.mac.common import * class TXLastBE(Module): def __init__(self, d_w): diff --git a/liteethernet/mac/core/preamble.py b/liteethernet/mac/core/preamble.py index fe2078fee..f1c760347 100644 --- a/liteethernet/mac/core/preamble.py +++ b/liteethernet/mac/core/preamble.py @@ -4,7 +4,8 @@ from migen.genlib.misc import chooser from migen.genlib.record import * from migen.flow.actor import Sink, Source -from misoclib.ethmac.common import * +from liteethernet.common import * +from liteethernet.ethmac.common import * class PreambleInserter(Module): def __init__(self, d_w): diff --git a/liteethernet/mac/frontend/sram.py b/liteethernet/mac/frontend/sram.py index 32c76f3c0..c51c0ecbe 100644 --- a/liteethernet/mac/frontend/sram.py +++ b/liteethernet/mac/frontend/sram.py @@ -6,7 +6,8 @@ from migen.flow.actor import Sink, Source from migen.bank.description import * from migen.bank.eventmanager import * -from misoclib.ethmac.common import * +from liteethernet.common import * +from liteethernet.mac.common import * class SRAMWriter(Module, AutoCSR): def __init__(self, depth, nslots=2): diff --git a/liteethernet/mac/frontend/wishbone.py b/liteethernet/mac/frontend/wishbone.py new file mode 100644 index 000000000..56601e126 --- /dev/null +++ b/liteethernet/mac/frontend/wishbone.py @@ -0,0 +1,39 @@ +from liteethernet.common import * +from liteethernet.mac import LiteEthernetMAC + +class LiteEthernetMACWishboneInterface(Module, AutoCSR): + def __init__(self, nrxslots=2, ntxslots=2): + self.sink = Sink(mac_description(dw)) + self.source = Source(max_description(dw)) + self.bus = wishbone.Interface() + + ### + + # SRAM Storage + sram_depth = buffer_depth//(32//8) + self.submodules.sram_writer = SRAMWriter(sram_depth, nrxslots) + self.submodules.sram_reader = SRAMReader(sram_depth, ntxslots) + self.submodules.ev = SharedIRQ(self.sram_writer.ev, self.sram_reader.ev) + self.comb += [ + Record.connect(self.sink, self.sram_writer.sink), + Record.connect(self.sram_reader.source, self.source) + ] + + # Interface + wb_rx_sram_ifs = [wishbone.SRAM(self.sram_writer.mems[n], read_only=True) + for n in range(nrxslots)] + # TODO: FullMemoryWE should move to Mibuild + wb_tx_sram_ifs = [FullMemoryWE(wishbone.SRAM(self.sram_reader.mems[n], read_only=False)) + for n in range(ntxslots)] + wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs + + wb_slaves = [] + decoderoffset = log2_int(sram_depth) + decoderbits = log2_int(len(wb_sram_ifs)) + for n, wb_sram_if in enumerate(wb_sram_ifs): + def slave_filter(a, v=n): + return a[decoderoffset:decoderoffset+decoderbits] == v + wb_slaves.append((slave_filter, wb_sram_if.bus)) + self.submodules += wb_sram_if + wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True) + self.submodules += wb_con diff --git a/liteethernet/mac/phy/__init__.py b/liteethernet/phy/__init__.py similarity index 100% rename from liteethernet/mac/phy/__init__.py rename to liteethernet/phy/__init__.py diff --git a/liteethernet/mac/phy/gmii.py b/liteethernet/phy/gmii.py similarity index 97% rename from liteethernet/mac/phy/gmii.py rename to liteethernet/phy/gmii.py index ebdd02352..c1b8243d7 100644 --- a/liteethernet/mac/phy/gmii.py +++ b/liteethernet/phy/gmii.py @@ -3,14 +3,12 @@ from migen.flow.actor import Sink, Source from migen.bank.description import * from migen.genlib.resetsync import AsyncResetSynchronizer -from misoclib.ethmac.common import * +from liteethernet.common import * class GMIIPHYTX(Module): def __init__(self, pads): self.sink = sink = Sink(eth_description(8)) - ### - self.sync += [ pads.tx_er.eq(0), pads.tx_en.eq(sink.stb), @@ -21,9 +19,7 @@ class GMIIPHYTX(Module): class GMIIPHYRX(Module): def __init__(self, pads): self.source = source = Source(eth_description(8)) - ### - dv_d = Signal() self.sync += dv_d.eq(pads.dv) @@ -45,9 +41,7 @@ class GMIIPHYRX(Module): class GMIIPHYCRG(Module, AutoCSR): def __init__(self, clock_pads, pads): self._reset = CSRStorage() - ### - self.clock_domains.cd_eth_rx = ClockDomain() self.clock_domains.cd_eth_tx = ClockDomain() self.specials += [ diff --git a/liteethernet/mac/phy/loopback.py b/liteethernet/phy/loopback.py similarity index 95% rename from liteethernet/mac/phy/loopback.py rename to liteethernet/phy/loopback.py index 50c9f995d..04a51d07e 100644 --- a/liteethernet/mac/phy/loopback.py +++ b/liteethernet/phy/loopback.py @@ -3,14 +3,12 @@ from migen.flow.actor import Sink, Source from migen.bank.description import * from migen.genlib.record import * -from misoclib.ethmac.common import * +from liteethernet.common import * class LoopbackPHYCRG(Module, AutoCSR): def __init__(self): self._reset = CSRStorage() - ### - self.clock_domains.cd_eth_rx = ClockDomain() self.clock_domains.cd_eth_tx = ClockDomain() self.comb += [ diff --git a/liteethernet/mac/phy/mii.py b/liteethernet/phy/mii.py similarity index 98% rename from liteethernet/mac/phy/mii.py rename to liteethernet/phy/mii.py index 17f2ba7b4..a58b52b5d 100644 --- a/liteethernet/mac/phy/mii.py +++ b/liteethernet/phy/mii.py @@ -4,14 +4,12 @@ from migen.flow.actor import Sink, Source from migen.bank.description import * from migen.genlib.resetsync import AsyncResetSynchronizer -from misoclib.ethmac.common import * +from liteethernet.common import * class MIIPHYTX(Module): def __init__(self, pads): self.sink = sink = Sink(eth_description(8)) - ### - tx_en_r = Signal() tx_data_r = Signal(4) self.sync += [ @@ -48,9 +46,7 @@ class MIIPHYTX(Module): class MIIPHYRX(Module): def __init__(self, pads): self.source = source = Source(eth_description(8)) - ### - sop = source.sop set_sop = Signal() clr_sop = Signal() @@ -102,9 +98,7 @@ class MIIPHYRX(Module): class MIIPHYCRG(Module, AutoCSR): def __init__(self, clock_pads, pads): self._reset = CSRStorage() - ### - self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy) self.clock_domains.cd_eth_rx = ClockDomain()