mac: import files from MiSoC
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 27 Jan 2015 22:59:06 +0000 (23:59 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 27 Jan 2015 22:59:06 +0000 (23:59 +0100)
15 files changed:
liteethernet/mac/__init__.py
liteethernet/mac/common.py [new file with mode: 0644]
liteethernet/mac/core/last_be.py [new file with mode: 0644]
liteethernet/mac/core/preamble.py [new file with mode: 0644]
liteethernet/mac/frontend/sram.py [new file with mode: 0644]
liteethernet/mac/phy/gmii.py [new file with mode: 0644]
liteethernet/mac/phy/loopback.py [new file with mode: 0644]
liteethernet/mac/phy/mii.py [new file with mode: 0644]
liteethernet/mac/test/Makefile [new file with mode: 0644]
liteethernet/mac/test/__init__.py [new file with mode: 0644]
liteethernet/mac/test/common.py [new file with mode: 0644]
liteethernet/mac/test/crc_tb.py [new file with mode: 0644]
liteethernet/mac/test/ethmac_tb.gtkw [new file with mode: 0644]
liteethernet/mac/test/ethmac_tb.py [new file with mode: 0644]
liteethernet/mac/test/preamble_tb.py [new file with mode: 0644]

index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a7ea0c2c2938fda07777a3fc9d19b330770253cf 100644 (file)
@@ -0,0 +1,111 @@
+# This file is Copyright (c) 2014 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+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)
+
+               if interface == "wishbone":
+                       nrxslots = 2
+                       ntxslots = 2
+
+                       self.bus = wishbone.Interface()
+
+                       # SRAM Memories
+                       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)
+
+                       # Connect to pipelines
+                       self.comb += [
+                               self.rx_pipeline.source.connect(self.sram_writer.sink),
+                               self.sram_reader.source.connect(self.tx_pipeline.sink)
+                       ]
+
+                       # 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
+
+               elif interface == "lasmi":
+                       raise NotImplementedError
+
+               elif interface == "expose":
+                       # expose pipelines endpoints
+                       self.sink = tx_pipeline.sink
+                       self.source = rx_pipeline.source
+
+               else:
+                       raise ValueError("EthMAC only supports Wishbone, LASMI or expose interfaces")
diff --git a/liteethernet/mac/common.py b/liteethernet/mac/common.py
new file mode 100644 (file)
index 0000000..177943e
--- /dev/null
@@ -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/core/last_be.py b/liteethernet/mac/core/last_be.py
new file mode 100644 (file)
index 0000000..da55fae
--- /dev/null
@@ -0,0 +1,39 @@
+from migen.fhdl.std import *
+from migen.genlib.record import *
+from migen.flow.actor import Sink, Source
+
+from misoclib.ethmac.common import *
+
+class TXLastBE(Module):
+       def __init__(self, d_w):
+               self.sink = sink = Sink(eth_description(d_w))
+               self.source = source = Source(eth_description(d_w))
+
+               ###
+
+               ongoing = Signal()
+               self.sync += \
+                       If(self.sink.stb & self.sink.ack,
+                               If(sink.sop,
+                                       ongoing.eq(1)
+                               ).Elif(sink.last_be,
+                                       ongoing.eq(0)
+                               )
+                       )
+               self.comb += [
+                       Record.connect(self.sink, self.source),
+                       self.source.eop.eq(self.sink.last_be),
+                       self.source.stb.eq(self.sink.stb & (self.sink.sop | ongoing))
+               ]
+
+class RXLastBE(Module):
+       def __init__(self, d_w):
+               self.sink = sink = Sink(eth_description(d_w))
+               self.source = source = Source(eth_description(d_w))
+
+               ###
+
+               self.comb += [
+                       Record.connect(self.sink, self.source),
+                       self.source.last_be.eq(self.sink.eop)
+               ]
diff --git a/liteethernet/mac/core/preamble.py b/liteethernet/mac/core/preamble.py
new file mode 100644 (file)
index 0000000..fe2078f
--- /dev/null
@@ -0,0 +1,145 @@
+from migen.fhdl.std import *
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.misc import chooser
+from migen.genlib.record import *
+from migen.flow.actor import Sink, Source
+
+from misoclib.ethmac.common import *
+
+class PreambleInserter(Module):
+       def __init__(self, d_w):
+               self.sink = Sink(eth_description(d_w))
+               self.source = Source(eth_description(d_w))
+
+               ###
+
+               preamble = Signal(64, reset=eth_preamble)
+               cnt_max = (64//d_w)-1
+               cnt = Signal(max=cnt_max+1)
+               clr_cnt = Signal()
+               inc_cnt = Signal()
+
+               self.sync += \
+                       If(clr_cnt,
+                               cnt.eq(0)
+                       ).Elif(inc_cnt,
+                               cnt.eq(cnt+1)
+                       )
+
+               fsm = FSM(reset_state="IDLE")
+               self.submodules += fsm
+               fsm.act("IDLE",
+                       self.sink.ack.eq(1),
+                       clr_cnt.eq(1),
+                       If(self.sink.stb & self.sink.sop,
+                               self.sink.ack.eq(0),
+                               NextState("INSERT"),
+                       )
+               )
+               fsm.act("INSERT",
+                       self.source.stb.eq(1),
+                       self.source.sop.eq(cnt==0),
+                       chooser(preamble, cnt, self.source.d),
+                       If(cnt == cnt_max,
+                               If(self.source.ack, NextState("COPY"))
+                       ).Else(
+                               inc_cnt.eq(self.source.ack)
+                       )
+               )
+               fsm.act("COPY",
+                       Record.connect(self.sink, self.source),
+                       self.source.sop.eq(0),
+
+                       If(self.sink.stb & self.sink.eop & self.source.ack,
+                               NextState("IDLE"),
+                       )
+               )
+
+class PreambleChecker(Module):
+       def __init__(self, d_w):
+               self.sink = Sink(eth_description(d_w))
+               self.source = Source(eth_description(d_w))
+
+               ###
+
+               preamble = Signal(64, reset=eth_preamble)
+               cnt_max = (64//d_w) - 1
+               cnt = Signal(max=cnt_max+1)
+               clr_cnt = Signal()
+               inc_cnt = Signal()
+
+               self.sync += \
+                       If(clr_cnt,
+                               cnt.eq(0)
+                       ).Elif(inc_cnt,
+                               cnt.eq(cnt+1)
+                       )
+
+               discard = Signal()
+               clr_discard = Signal()
+               set_discard = Signal()
+
+               self.sync += \
+                       If(clr_discard,
+                               discard.eq(0)
+                       ).Elif(set_discard,
+                               discard.eq(1)
+                       )
+
+               sop = Signal()
+               clr_sop = Signal()
+               set_sop = Signal()
+               self.sync += \
+                       If(clr_sop,
+                               sop.eq(0)
+                       ).Elif(set_sop,
+                               sop.eq(1)
+                       )
+
+               ref = Signal(d_w)
+               match = Signal()
+               self.comb += [
+                       chooser(preamble, cnt, ref),
+                       match.eq(self.sink.d == ref)
+               ]
+
+               fsm = FSM(reset_state="IDLE")
+               self.submodules += fsm
+
+               fsm.act("IDLE",
+                       self.sink.ack.eq(1),
+                       clr_cnt.eq(1),
+                       clr_discard.eq(1),
+                       If(self.sink.stb & self.sink.sop,
+                               clr_cnt.eq(0),
+                               inc_cnt.eq(1),
+                               clr_discard.eq(0),
+                               set_discard.eq(~match),
+                               NextState("CHECK"),
+                       )
+               )
+               fsm.act("CHECK",
+                       self.sink.ack.eq(1),
+                       If(self.sink.stb,
+                               set_discard.eq(~match),
+                               If(cnt == cnt_max,
+                                       If(discard | (~match),
+                                               NextState("IDLE")
+                                       ).Else(
+                                               set_sop.eq(1),
+                                               NextState("COPY")
+                                       )
+                               ).Else(
+                                       inc_cnt.eq(1)
+                               )
+                       )
+               )
+               fsm.act("COPY",
+                       Record.connect(self.sink, self.source),
+                       self.source.sop.eq(sop),
+                       clr_sop.eq(self.source.stb & self.source.ack),
+
+                       If(self.source.stb & self.source.eop & self.source.ack,
+                               NextState("IDLE"),
+                       )
+               )
diff --git a/liteethernet/mac/frontend/sram.py b/liteethernet/mac/frontend/sram.py
new file mode 100644 (file)
index 0000000..32c76f3
--- /dev/null
@@ -0,0 +1,251 @@
+from migen.fhdl.std import *
+from migen.genlib.fifo import SyncFIFO
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.misc import chooser
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.bank.eventmanager import *
+
+from misoclib.ethmac.common import *
+
+class SRAMWriter(Module, AutoCSR):
+       def __init__(self, depth, nslots=2):
+               self.sink = sink = Sink(eth_description(32))
+               self.crc_error = Signal()
+
+               slotbits = max(log2_int(nslots), 1)
+               lengthbits = log2_int(depth*4) # length in bytes
+
+               self._slot = CSRStatus(slotbits)
+               self._length = CSRStatus(lengthbits)
+
+               self.submodules.ev = EventManager()
+               self.ev.available = EventSourceLevel()
+               self.ev.finalize()
+
+               ###
+
+               # packet dropped if no slot available
+               sink.ack.reset = 1
+
+               # length computation
+               cnt = Signal(lengthbits)
+               clr_cnt = Signal()
+               inc_cnt = Signal()
+               inc_val = Signal(3)
+               self.comb += \
+                       If(sink.last_be[3],
+                               inc_val.eq(1)
+                       ).Elif(sink.last_be[2],
+                               inc_val.eq(2)
+                       ).Elif(sink.last_be[1],
+                               inc_val.eq(3)
+                       ).Else(
+                               inc_val.eq(4)
+                       )
+               self.sync += \
+                       If(clr_cnt,
+                               cnt.eq(0)
+                       ).Elif(inc_cnt,
+                               cnt.eq(cnt+inc_val)
+                       )
+
+               # slot computation
+               slot = Signal(slotbits)
+               inc_slot = Signal()
+               self.sync += \
+                       If(inc_slot,
+                               If(slot == nslots-1,
+                                       slot.eq(0),
+                               ).Else(
+                                       slot.eq(slot+1)
+                               )
+                       )
+               ongoing = Signal()
+               discard = Signal()
+
+               # status fifo
+               fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots)
+               self.submodules += fifo
+
+               # fsm
+               fsm = FSM(reset_state="IDLE")
+               self.submodules += fsm
+
+               fsm.act("IDLE",
+                       inc_cnt.eq(sink.stb),
+                       If(sink.stb & sink.sop,
+                               ongoing.eq(1),
+                               If(fifo.writable,
+                                       NextState("WRITE")
+                               )
+                       )
+               )
+               fsm.act("WRITE",
+                       inc_cnt.eq(sink.stb),
+                       ongoing.eq(1),
+                       If(sink.stb & sink.eop,
+                               If((sink.error & sink.last_be) != 0,
+                                       NextState("DISCARD")
+                               ).Else(
+                                       NextState("TERMINATE")
+                               )
+                       )
+               )
+               fsm.act("DISCARD",
+                       clr_cnt.eq(1),
+                       NextState("IDLE")
+               )
+               fsm.act("TERMINATE",
+                       clr_cnt.eq(1),
+                       inc_slot.eq(1),
+                       fifo.we.eq(1),
+                       fifo.din.slot.eq(slot),
+                       fifo.din.length.eq(cnt),
+                       NextState("IDLE")
+               )
+
+               self.comb += [
+                       fifo.re.eq(self.ev.available.clear),
+                       self.ev.available.trigger.eq(fifo.readable),
+                       self._slot.status.eq(fifo.dout.slot),
+                       self._length.status.eq(fifo.dout.length),
+               ]
+
+               # memory
+               mems = [None]*nslots
+               ports = [None]*nslots
+               for n in range(nslots):
+                       mems[n] = Memory(32, depth)
+                       ports[n] = mems[n].get_port(write_capable=True)
+                       self.specials += ports[n]
+               self.mems = mems
+
+               cases = {}
+               for n, port in enumerate(ports):
+                       cases[n] = [
+                               ports[n].adr.eq(cnt[2:]),
+                               ports[n].dat_w.eq(sink.d),
+                               If(sink.stb & ongoing,
+                                       ports[n].we.eq(0xf)
+                               )
+                       ]
+               self.comb += Case(slot, cases)
+
+
+class SRAMReader(Module, AutoCSR):
+       def __init__(self, depth, nslots=2):
+               self.source = source = Source(eth_description(32))
+
+               slotbits = max(log2_int(nslots), 1)
+               lengthbits = log2_int(depth*4) # length in bytes
+               self.lengthbits = lengthbits
+
+               self._start = CSR()
+               self._ready = CSRStatus()
+               self._slot = CSRStorage(slotbits)
+               self._length = CSRStorage(lengthbits)
+
+               self.submodules.ev = EventManager()
+               self.ev.done = EventSourcePulse()
+               self.ev.finalize()
+
+               ###
+
+               # command fifo
+               fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots)
+               self.submodules += fifo
+               self.comb += [
+                       fifo.we.eq(self._start.re),
+                       fifo.din.slot.eq(self._slot.storage),
+                       fifo.din.length.eq(self._length.storage),
+                       self._ready.status.eq(fifo.writable)
+               ]
+
+               # length computation
+               cnt = Signal(lengthbits)
+               clr_cnt = Signal()
+               inc_cnt = Signal()
+
+               self.sync += \
+                       If(clr_cnt,
+                               cnt.eq(0)
+                       ).Elif(inc_cnt,
+                               cnt.eq(cnt+4)
+                       )
+
+               # fsm
+               first = Signal()
+               last  = Signal()
+               last_d = Signal()
+
+               fsm = FSM(reset_state="IDLE")
+               self.submodules += fsm
+
+               fsm.act("IDLE",
+                       clr_cnt.eq(1),
+                       If(fifo.readable,
+                               NextState("CHECK")
+                       )
+               )
+               fsm.act("CHECK",
+                       If(~last_d,
+                               NextState("SEND"),
+                       ).Else(
+                               NextState("END"),
+                       )
+               )
+               length_lsb = fifo.dout.length[0:2]
+               fsm.act("SEND",
+                       source.stb.eq(1),
+                       source.sop.eq(first),
+                       source.eop.eq(last),
+                       If(last,
+                               If(length_lsb == 3,
+                                       source.last_be.eq(0b0010)
+                               ).Elif(length_lsb == 2,
+                                       source.last_be.eq(0b0100)
+                               ).Elif(length_lsb == 1,
+                                       source.last_be.eq(0b1000)
+                               ).Else(
+                                       source.last_be.eq(0b0001)
+                               )
+                       ),
+                       If(source.ack,
+                               inc_cnt.eq(~last),
+                               NextState("CHECK")
+                       )
+               )
+               fsm.act("END",
+                       fifo.re.eq(1),
+                       self.ev.done.trigger.eq(1),
+                       NextState("IDLE")
+               )
+
+               # first/last computation
+               self.sync += [
+                       If(fsm.ongoing("IDLE"),
+                               first.eq(1)
+                       ).Elif(source.stb & source.ack,
+                               first.eq(0)
+                       )
+               ]
+               self.comb += last.eq(cnt + 4 >= fifo.dout.length)
+               self.sync += last_d.eq(last)
+
+               # memory
+               rd_slot = fifo.dout.slot
+
+               mems = [None]*nslots
+               ports = [None]*nslots
+               for n in range(nslots):
+                       mems[n] = Memory(32, depth)
+                       ports[n] = mems[n].get_port()
+                       self.specials += ports[n]
+               self.mems = mems
+
+               cases = {}
+               for n, port in enumerate(ports):
+                       self.comb += ports[n].adr.eq(cnt[2:])
+                       cases[n] = [source.d.eq(port.dat_r)]
+               self.comb += Case(rd_slot, cases)
diff --git a/liteethernet/mac/phy/gmii.py b/liteethernet/mac/phy/gmii.py
new file mode 100644 (file)
index 0000000..ebdd023
--- /dev/null
@@ -0,0 +1,76 @@
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+from misoclib.ethmac.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),
+                       pads.tx_data.eq(sink.d)
+               ]
+               self.comb += sink.ack.eq(1)
+
+class GMIIPHYRX(Module):
+       def __init__(self, pads):
+               self.source = source = Source(eth_description(8))
+
+               ###
+
+               dv_d = Signal()
+               self.sync += dv_d.eq(pads.dv)
+
+               sop = Signal()
+               eop = Signal()
+               self.comb += [
+                       sop.eq(pads.dv & ~dv_d),
+                       eop.eq(~pads.dv & dv_d)
+               ]
+               self.sync += [
+                       source.stb.eq(pads.dv),
+                       source.sop.eq(sop),
+                       source.d.eq(pads.rx_data)
+               ]
+               self.comb += source.eop.eq(eop)
+
+# CRG is the only Xilinx specific module.
+# TODO: use generic code or add support for others vendors
+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 += [
+                       Instance("ODDR",
+                               p_DDR_CLK_EDGE="SAME_EDGE",
+                               i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0,
+                               i_D1=1, i_D2=0, o_Q=clock_pads.gtx,
+                       ),
+                       Instance("BUFG", i_I=clock_pads.rx, o_O=self.cd_eth_rx.clk),
+               ]
+               self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk)
+
+               reset = self._reset.storage
+               self.comb += pads.rst_n.eq(~reset)
+               self.specials += [
+                       AsyncResetSynchronizer(self.cd_eth_tx, reset),
+                       AsyncResetSynchronizer(self.cd_eth_rx, reset),
+               ]
+
+class GMIIPHY(Module, AutoCSR):
+       def __init__(self, clock_pads, pads):
+               self.dw = 8
+               self.submodules.crg = GMIIPHYCRG(clock_pads, pads)
+               self.submodules.tx = RenameClockDomains(GMIIPHYTX(pads), "eth_tx")
+               self.submodules.rx = RenameClockDomains(GMIIPHYRX(pads), "eth_rx")
+               self.sink, self.source = self.tx.sink, self.rx.source
diff --git a/liteethernet/mac/phy/loopback.py b/liteethernet/mac/phy/loopback.py
new file mode 100644 (file)
index 0000000..50c9f99
--- /dev/null
@@ -0,0 +1,33 @@
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.record import *
+
+from misoclib.ethmac.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 += [
+                       self.cd_eth_rx.clk.eq(ClockSignal()),
+                       self.cd_eth_tx.clk.eq(ClockSignal())
+               ]
+
+               reset = self._reset.storage
+               self.comb += [
+                       self.cd_eth_rx.rst.eq(reset),
+                       self.cd_eth_tx.rst.eq(reset)
+               ]
+
+class LoopbackPHY(Module, AutoCSR):
+       def __init__(self):
+               self.dw = 8
+               self.submodules.crg = LoopbackPHYCRG()
+               self.sink = sink = Sink(eth_description(8))
+               self.source = source = Source(eth_description(8))
+               self.comb += Record.connect(self.sink, self.source)
diff --git a/liteethernet/mac/phy/mii.py b/liteethernet/mac/phy/mii.py
new file mode 100644 (file)
index 0000000..17f2ba7
--- /dev/null
@@ -0,0 +1,128 @@
+from migen.fhdl.std import *
+from migen.genlib.fsm import FSM, NextState
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+from misoclib.ethmac.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 += [
+                       pads.tx_er.eq(0),
+                       pads.tx_en.eq(tx_en_r),
+                       pads.tx_data.eq(tx_data_r),
+               ]
+
+               fsm = FSM(reset_state="IDLE")
+               self.submodules += fsm
+               fsm.act("IDLE",
+                       sink.ack.eq(1),
+                       If(sink.stb & sink.sop,
+                               sink.ack.eq(0),
+                               NextState("SEND_LO")
+                       )
+               )
+               fsm.act("SEND_LO",
+                       tx_data_r.eq(sink.d[0:4]),
+                       tx_en_r.eq(1),
+                       NextState("SEND_HI")
+               )
+               fsm.act("SEND_HI",
+                       tx_data_r.eq(sink.d[4:8]),
+                       tx_en_r.eq(1),
+                       sink.ack.eq(1),
+                       If(sink.stb & sink.eop,
+                               NextState("IDLE")
+                       ).Else(
+                               NextState("SEND_LO")
+                       )
+               )
+
+class MIIPHYRX(Module):
+       def __init__(self, pads):
+               self.source = source = Source(eth_description(8))
+
+               ###
+
+               sop = source.sop
+               set_sop = Signal()
+               clr_sop = Signal()
+               self.sync += \
+                       If(clr_sop,
+                               sop.eq(0)
+                       ).Elif(set_sop,
+                               sop.eq(1)
+                       )
+
+               lo = Signal(4)
+               hi = Signal(4)
+               load_nibble = Signal(2)
+               self.sync  += \
+                       If(load_nibble[0],
+                               lo.eq(pads.rx_data)
+                       ).Elif(load_nibble[1],
+                               hi.eq(pads.rx_data)
+                       )
+               self.comb += [
+                       source.d.eq(Cat(lo, hi))
+               ]
+
+               fsm = FSM(reset_state="IDLE")
+               self.submodules += fsm
+               fsm.act("IDLE",
+                       set_sop.eq(1),
+                       If(pads.dv,
+                               load_nibble.eq(0b01),
+                               NextState("LOAD_HI")
+                       )
+               )
+               fsm.act("LOAD_LO",
+                       source.stb.eq(1),
+                       If(pads.dv,
+                               clr_sop.eq(1),
+                               load_nibble.eq(0b01),
+                               NextState("LOAD_HI")
+                       ).Else(
+                               source.eop.eq(1),
+                               NextState("IDLE")
+                       )
+               )
+               fsm.act("LOAD_HI",
+                       load_nibble.eq(0b10),
+                       NextState("LOAD_LO")
+               )
+
+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()
+               self.clock_domains.cd_eth_tx = ClockDomain()
+               self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
+               self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx)
+
+               reset = self._reset.storage
+               self.comb += pads.rst_n.eq(~reset)
+               self.specials += [
+                       AsyncResetSynchronizer(self.cd_eth_tx, reset),
+                       AsyncResetSynchronizer(self.cd_eth_rx, reset),
+               ]
+
+class MIIPHY(Module, AutoCSR):
+       def __init__(self, clock_pads, pads):
+               self.dw = 8
+               self.submodules.crg = MIIPHYCRG(clock_pads, pads)
+               self.submodules.tx = RenameClockDomains(MIIPHYTX(pads), "eth_tx")
+               self.submodules.rx = RenameClockDomains(MIIPHYRX(pads), "eth_rx")
+               self.sink, self.source = self.tx.sink, self.rx.source
diff --git a/liteethernet/mac/test/Makefile b/liteethernet/mac/test/Makefile
new file mode 100644 (file)
index 0000000..55d28e1
--- /dev/null
@@ -0,0 +1,13 @@
+MSCDIR = ../../../
+PYTHON = python3
+
+CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
+
+crc_tb:
+       $(CMD) crc_tb.py
+
+preamble_tb:
+       $(CMD) preamble_tb.py
+
+ethmac_tb:
+       $(CMD) ethmac_tb.py
diff --git a/liteethernet/mac/test/__init__.py b/liteethernet/mac/test/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/liteethernet/mac/test/common.py b/liteethernet/mac/test/common.py
new file mode 100644 (file)
index 0000000..1312370
--- /dev/null
@@ -0,0 +1,120 @@
+import random, copy
+
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.genlib.record import *
+
+from misoclib.ethmac.common import *
+
+def seed_to_data(seed, random=True):
+       if random:
+               return (seed * 0x31415979 + 1) & 0xffffffff
+       else:
+               return seed
+
+def check(p1, p2):
+       p1 = copy.deepcopy(p1)
+       p2 = copy.deepcopy(p2)
+       if isinstance(p1, int):
+               return 0, 1, int(p1 != p2)
+       else:
+               if len(p1) >= len(p2):
+                       ref, res = p1, p2
+               else:
+                       ref, res = p2, p1
+               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
+
+def randn(max_n):
+       return random.randint(0, max_n-1)
+
+class Packet(list):
+       def __init__(self, init=[]):
+               self.ongoing = False
+               self.done = False
+               for data in init:
+                       self.append(data)
+
+class PacketStreamer(Module):
+       def __init__(self, description):
+               self.source = Source(description)
+               ###
+               self.packets = []
+               self.packet = Packet()
+               self.packet.done = 1
+
+       def send(self, packet):
+               packet = copy.deepcopy(packet)
+               self.packets.append(packet)
+
+       def do_simulation(self, selfp):
+               if len(self.packets) and self.packet.done:
+                       self.packet = self.packets.pop(0)
+               if not self.packet.ongoing and not self.packet.done:
+                       selfp.source.stb = 1
+                       selfp.source.sop = 1
+                       selfp.source.d = self.packet.pop(0)
+                       self.packet.ongoing = True
+               elif selfp.source.stb == 1 and selfp.source.ack == 1:
+                       selfp.source.sop = 0
+                       selfp.source.eop = (len(self.packet) == 1)
+                       if len(self.packet) > 0:
+                               selfp.source.stb = 1
+                               selfp.source.d = self.packet.pop(0)
+                       else:
+                               self.packet.done = 1
+                               selfp.source.stb = 0
+
+class PacketLogger(Module):
+       def __init__(self, description):
+               self.sink = Sink(description)
+               ###
+               self.packet = Packet()
+
+       def receive(self):
+               self.packet.done = 0
+               while self.packet.done == 0:
+                       yield
+
+       def do_simulation(self, selfp):
+               selfp.sink.ack = 1
+               if selfp.sink.stb == 1 and selfp.sink.sop == 1:
+                       self.packet = Packet()
+                       self.packet.append(selfp.sink.d)
+               elif selfp.sink.stb:
+                       self.packet.append(selfp.sink.d)
+               if selfp.sink.stb == 1 and selfp.sink.eop == 1:
+                       self.packet.done = True
+
+class AckRandomizer(Module):
+       def __init__(self, description, level=0):
+               self.level = level
+
+               self.sink = Sink(description)
+               self.source = Source(description)
+
+               self.run = Signal()
+
+               self.comb += \
+                       If(self.run,
+                               Record.connect(self.sink, self.source)
+                       ).Else(
+                               self.source.stb.eq(0),
+                               self.sink.ack.eq(0),
+                       )
+
+       def do_simulation(self, selfp):
+               n = randn(100)
+               if n < self.level:
+                       selfp.run = 0
+               else:
+                       selfp.run = 1
+
diff --git a/liteethernet/mac/test/crc_tb.py b/liteethernet/mac/test/crc_tb.py
new file mode 100644 (file)
index 0000000..5d185ac
--- /dev/null
@@ -0,0 +1,83 @@
+from migen.fhdl.std import *
+from migen.actorlib.crc import *
+
+from misoclib.ethmac.common import *
+from misoclib.ethmac.test.common import *
+
+payload = [
+       0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
+       0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
+       0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
+       0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
+       0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
+       0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+       0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+       0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
+]
+
+crc = [
+       0x7A, 0xD5, 0x6B, 0xB3
+]
+
+mux = {
+       "inserter": 0,
+       "checker": 1,
+       "both": 2
+}
+
+class TB(Module):
+       def __init__(self, random_level=50):
+               sm = self.submodules
+               sm.streamer = PacketStreamer(eth_description(8))
+               sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level)
+               sm.logger = PacketLogger(eth_description(8))
+               sm.logger_randomizer = AckRandomizer(eth_description(8), random_level)
+
+               self.comb += [
+                       self.streamer.source.connect(self.streamer_randomizer.sink),
+                       self.logger_randomizer.source.connect(self.logger.sink)
+               ]
+
+               sm.crc32_inserter = CRC32Inserter(eth_description(8))
+               sm.crc32_checker = CRC32Checker(eth_description(8))
+
+               self.mux = Signal(2)
+               self.comb += [
+                       If(self.mux == mux["inserter"],
+                               self.streamer_randomizer.source.connect(self.crc32_inserter.sink),
+                               self.crc32_inserter.source.connect(self.logger_randomizer.sink)
+                       ).Elif(self.mux == mux["checker"],
+                               self.streamer_randomizer.source.connect(self.crc32_checker.sink),
+                               self.crc32_checker.source.connect(self.logger_randomizer.sink)
+                       ).Elif(self.mux == mux["both"],
+                               self.streamer_randomizer.source.connect(self.crc32_inserter.sink),
+                               self.crc32_inserter.source.connect(self.crc32_checker.sink),
+                               self.crc32_checker.source.connect(self.logger_randomizer.sink)
+                       )
+               ]
+
+       def gen_simulation(self, selfp):
+               selfp.mux = mux["inserter"]
+               print("streamer --> crc32_inserter --> logger:")
+               self.streamer.send(Packet(payload))
+               yield from self.logger.receive()
+               s, l, e = check(payload+crc, self.logger.packet)
+               print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
+
+               selfp.mux = mux["checker"]
+               print("streamer --> crc32_checker --> logger:")
+               self.streamer.send(Packet(payload+crc))
+               yield from self.logger.receive()
+               s, l, e = check(payload, self.logger.packet)
+               print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
+
+               selfp.mux = mux["both"]
+               print("streamer --> crc32_inserter --> crc32_checker --> logger:")
+               self.streamer.send(Packet(payload))
+               yield from self.logger.receive()
+               s, l, e = check(payload, self.logger.packet)
+               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")
diff --git a/liteethernet/mac/test/ethmac_tb.gtkw b/liteethernet/mac/test/ethmac_tb.gtkw
new file mode 100644 (file)
index 0000000..350521a
--- /dev/null
@@ -0,0 +1,75 @@
+[*]
+[*] GTKWave Analyzer v3.3.46 (w)1999-2012 BSI
+[*] Fri Oct 31 11:20:55 2014
+[*]
+[dumpfile] "/home/florent/Dev/misoc/misoclib/ethmac/test/my.vcd"
+[dumpfile_mtime] "Fri Oct 31 11:20:07 2014"
+[dumpfile_size] 5152269
+[savefile] "/home/florent/Dev/misoc/misoclib/ethmac/test/ethmac_tb.gtkw"
+[timestart] 0
+[size] 1548 849
+[pos] 101 171
+*-25.000000 34300000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+[treeopen] top.
+[sst_width] 379
+[signals_width] 464
+[sst_expanded] 1
+[sst_vpaned_height] 485
+@200
+-WISHBONE
+@28
+top.dut.bus_ack
+@22
+top.dut.bus_adr[29:0]
+@28
+top.dut.bus_bte[1:0]
+top.dut.bus_cti[2:0]
+top.dut.bus_cyc
+@22
+top.dut.bus_dat_r[31:0]
+top.dut.bus_dat_w[31:0]
+@28
+top.dut.bus_err
+@22
+top.dut.bus_sel[3:0]
+@28
+top.dut.bus_stb
+top.dut.bus_we
+@200
+-SRAM_READER --> PHYTx
+@28
+top.dut.sram_reader_source_ack
+top.dut.sram_reader_source_eop
+@22
+top.dut.sram_reader_source_payload_d[31:0]
+top.dut.sram_reader_source_payload_error[3:0]
+top.dut.sram_reader_source_payload_last_be[3:0]
+@28
+top.dut.sram_reader_source_sop
+top.dut.sram_reader_source_stb
+@200
+-LOOPBACK
+@28
+top.dut.phy_source_ack
+top.dut.phy_source_eop
+@22
+top.dut.phy_source_payload_d[7:0]
+@28
+top.dut.phy_source_payload_error
+top.dut.phy_source_payload_last_be
+top.dut.phy_source_sop
+top.dut.phy_source_stb
+@200
+-PHYRx --> SRAM_WRITER
+@28
+top.dut.sram_writer_sink_ack
+top.dut.sram_writer_sink_eop
+@22
+top.dut.sram_writer_sink_payload_d[31:0]
+top.dut.sram_writer_sink_payload_error[3:0]
+top.dut.sram_writer_sink_payload_last_be[3:0]
+@28
+top.dut.sram_writer_sink_sop
+top.dut.sram_writer_sink_stb
+[pattern_trace] 1
+[pattern_trace] 0
diff --git a/liteethernet/mac/test/ethmac_tb.py b/liteethernet/mac/test/ethmac_tb.py
new file mode 100644 (file)
index 0000000..1e4dbf1
--- /dev/null
@@ -0,0 +1,123 @@
+from migen.fhdl.std import *
+from migen.bus import wishbone
+from migen.bus.transactions import *
+from migen.sim.generic import run_simulation
+
+from misoclib.ethmac import EthMAC
+from misoclib.ethmac.phy import loopback
+
+from misoclib.ethmac.test.common import *
+
+class WishboneMaster:
+       def __init__(self, obj):
+               self.obj = obj
+               self.dat = 0
+
+       def write(self, adr, dat):
+               self.obj.cyc = 1
+               self.obj.stb = 1
+               self.obj.adr = adr
+               self.obj.we = 1
+               self.obj.sel = 0xF
+               self.obj.dat_w = dat
+               while self.obj.ack == 0:
+                       yield
+               self.obj.cyc = 0
+               self.obj.stb = 0
+               yield
+
+       def read(self, adr):
+               self.obj.cyc = 1
+               self.obj.stb = 1
+               self.obj.adr = adr
+               self.obj.we = 0
+               self.obj.sel = 0xF
+               self.obj.dat_w = 0
+               while self.obj.ack == 0:
+                       yield
+               self.dat = self.obj.dat_r
+               self.obj.cyc = 0
+               self.obj.stb = 0
+               yield
+
+class SRAMReaderDriver:
+       def __init__(self, obj):
+               self.obj = obj
+
+       def start(self, slot, length):
+               self.obj._slot.storage = slot
+               self.obj._length.storage = length
+               self.obj._start.re = 1
+               yield
+               self.obj._start.re = 0
+               yield
+
+       def wait_done(self):
+               while self.obj.ev.done.pending == 0:
+                       yield
+
+       def clear_done(self):
+               self.obj.ev.done.clear = 1
+               yield
+               self.obj.ev.done.clear = 0
+               yield
+
+class TB(Module):
+       def __init__(self):
+               self.submodules.ethphy = loopback.LoopbackPHY()
+               self.submodules.ethmac = EthMAC(phy=self.ethphy, with_hw_preamble_crc=True)
+
+               # use sys_clk for each clock_domain
+               self.clock_domains.cd_eth_rx = ClockDomain()
+               self.clock_domains.cd_eth_tx = ClockDomain()
+               self.comb += [
+                       self.cd_eth_rx.clk.eq(ClockSignal()),
+                       self.cd_eth_rx.rst.eq(ResetSignal()),
+                       self.cd_eth_tx.clk.eq(ClockSignal()),
+                       self.cd_eth_tx.rst.eq(ResetSignal()),
+               ]
+
+       def gen_simulation(self, selfp):
+               selfp.cd_eth_rx.rst = 1
+               selfp.cd_eth_tx.rst = 1
+               yield
+               selfp.cd_eth_rx.rst = 0
+               selfp.cd_eth_tx.rst = 0
+
+               wishbone_master = WishboneMaster(selfp.ethmac.bus)
+               sram_reader_driver = SRAMReaderDriver(selfp.ethmac.sram_reader)
+
+               sram_writer_slots_offset = [0x000, 0x200]
+               sram_reader_slots_offset = [0x400, 0x600]
+
+               length = 1500+2
+
+               tx_payload = [seed_to_data(i, True) % 0xFF for i in range(length)] + [0, 0, 0, 0]
+
+               errors = 0
+
+               for slot in range(2):
+                       print("slot {}:".format(slot))
+                       # fill tx memory
+                       for i in range(length//4+1):
+                               dat = int.from_bytes(tx_payload[4*i:4*(i+1)], "big")
+                               yield from wishbone_master.write(sram_reader_slots_offset[slot]+i, dat)
+
+                       # send tx payload & wait
+                       yield from sram_reader_driver.start(slot, length)
+                       yield from sram_reader_driver.wait_done()
+                       yield from sram_reader_driver.clear_done()
+
+                       # get rx payload (loopback on PHY Model)
+                       rx_payload = []
+                       for i in range(length//4+1):
+                               yield from wishbone_master.read(sram_writer_slots_offset[slot]+i)
+                               dat = wishbone_master.dat
+                               rx_payload += list(dat.to_bytes(4, byteorder='big'))
+
+                       # check results
+                       s, l, e = check(tx_payload[:length], rx_payload[:min(length, len(rx_payload))])
+                       print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
+
+if __name__ == "__main__":
+       run_simulation(TB(), vcd_name="my.vcd")
diff --git a/liteethernet/mac/test/preamble_tb.py b/liteethernet/mac/test/preamble_tb.py
new file mode 100644 (file)
index 0000000..85bd7e6
--- /dev/null
@@ -0,0 +1,82 @@
+from migen.fhdl.std import *
+
+from misoclib.ethmac.common import *
+from misoclib.ethmac.preamble import *
+from misoclib.ethmac.test.common import *
+
+preamble = [
+       0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5
+]
+
+payload = [
+       0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
+       0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
+       0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
+       0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
+       0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
+       0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+       0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+       0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
+]
+
+mux = {
+       "inserter": 0,
+       "checker": 1,
+       "both": 2
+}
+
+class TB(Module):
+       def __init__(self, random_level=50):
+               sm = self.submodules
+               sm.streamer = PacketStreamer(eth_description(8))
+               sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level)
+               sm.logger = PacketLogger(eth_description(8))
+               sm.logger_randomizer = AckRandomizer(eth_description(8), random_level)
+
+               self.comb += [
+                       self.streamer.source.connect(self.streamer_randomizer.sink),
+                       self.logger_randomizer.source.connect(self.logger.sink)
+               ]
+
+               sm.preamble_inserter = PreambleInserter(8)
+               sm.preamble_checker = PreambleChecker(8)
+
+               self.mux = Signal(2)
+               self.comb += [
+                       If(self.mux == mux["inserter"],
+                               self.streamer_randomizer.source.connect(self.preamble_inserter.sink),
+                               self.preamble_inserter.source.connect(self.logger_randomizer.sink)
+                       ).Elif(self.mux == mux["checker"],
+                               self.streamer_randomizer.source.connect(self.preamble_checker.sink),
+                               self.preamble_checker.source.connect(self.logger_randomizer.sink)
+                       ).Elif(self.mux == mux["both"],
+                               self.streamer_randomizer.source.connect(self.preamble_inserter.sink),
+                               self.preamble_inserter.source.connect(self.preamble_checker.sink),
+                               self.preamble_checker.source.connect(self.logger_randomizer.sink)
+                       )
+               ]
+       def gen_simulation(self, selfp):
+               selfp.mux = mux["inserter"]
+               print("streamer --> preamble_inserter --> logger:")
+               self.streamer.send(Packet(payload))
+               yield from self.logger.receive()
+               s, l, e = check(preamble+payload, self.logger.packet)
+               print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
+
+               selfp.mux = mux["checker"]
+               print("streamer --> preamble_checker --> logger:")
+               self.streamer.send(Packet(preamble+payload))
+               yield from self.logger.receive()
+               s, l, e = check(payload, self.logger.packet)
+               print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
+
+               selfp.mux = mux["both"]
+               print("streamer --> preamble_inserter --> preamble_checker --> logger:")
+               self.streamer.send(Packet(payload))
+               yield from self.logger.receive()
+               s, l, e = check(payload, self.logger.packet)
+               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")