test: add mac simulation skeleton
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 28 Jan 2015 18:07:59 +0000 (19:07 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 28 Jan 2015 18:07:59 +0000 (19:07 +0100)
liteeth/common.py
liteeth/mac/core/__init__.py
liteeth/mac/core/crc.py
liteeth/test/Makefile
liteeth/test/common.py
liteeth/test/mac_core_tb.py
liteeth/test/model/mac.py [new file with mode: 0644]
liteeth/test/model/phy.py

index 9cf764eb71d0ed4c542aa998619a35bbd26ff75d..272f946cf3d45666f13135099ac10c76c8d63644 100644 (file)
@@ -5,6 +5,7 @@ from migen.fhdl.std import *
 from migen.genlib.resetsync import AsyncResetSynchronizer
 from migen.genlib.record import *
 from migen.genlib.fsm import FSM, NextState
+from migen.genlib.misc import chooser
 from migen.flow.actor import EndpointDescription
 from migen.flow.actor import Sink, Source
 from migen.actorlib.structuring import Converter, Pipeline
index 5adaaf3fc93037e166358223a10de739d9459575..e4c378a8a2087755b3c82a886919b2a71fd4bb94 100644 (file)
@@ -16,8 +16,8 @@ class LiteEthMACCore(Module, AutoCSR):
                        self.submodules += RenameClockDomains(preamble_checker, "eth_rx")
 
                        # CRC insert/check
-                       crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_description(phy.dw))
-                       crc32_checker = crc.LiteEthMACCRC32Checker(eth_description(phy.dw))
+                       crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw))
+                       crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw))
                        self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
                        self.submodules += RenameClockDomains(crc32_checker, "eth_rx")
 
index 7e37fec1957407c987c17db12596fc7b932a8bd8..e04e303bfcab950cc520afbb8e51a52a10c595e1 100644 (file)
@@ -101,7 +101,7 @@ class LiteEthMACCRC32(Module):
 
                ###
 
-               self.submodules.engine = LiteEthCRCEngine(data_width, self.width, self.polynom)
+               self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom)
                reg = Signal(self.width, reset=self.init)
                self.sync += reg.eq(self.engine.next)
                self.comb += [
@@ -255,14 +255,14 @@ class LiteEthMACCRCChecker(Module):
                        NextState("IDLE"),
                )
                fsm.act("IDLE",
-                       crc.d.eq(sink.data),
+                       crc.data.eq(sink.data),
                        If(sink.stb & sink.sop & sink.ack,
                                crc.ce.eq(1),
                                NextState("COPY")
                        )
                )
                fsm.act("COPY",
-                       crc.d.eq(sink.data),
+                       crc.data.eq(sink.data),
                        If(sink.stb & sink.ack,
                                crc.ce.eq(1),
                                If(sink.eop,
index 31b69ebe4e518573e428ba5ea1d1a0913dfc3ba3..974f1e570032e96ce0f7fa690d62a901cbbfafa4 100644 (file)
@@ -1,7 +1,7 @@
 LEDIR = ../../
 PYTHON = python3
 
-CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
+CMD = PYTHONPATH=$(LEDIR) $(PYTHON)
 
 mac_core_tb:
        $(CMD) mac_core_tb.py
index 32e2aaeaa2660085a77944af696579962e591d0f..345e467b27132e065d6ba20cbe1d967c9fcb17d3 100644 (file)
@@ -4,7 +4,7 @@ from migen.fhdl.std import *
 from migen.flow.actor import Sink, Source
 from migen.genlib.record import *
 
-from misoclib.ethmac.common import *
+from liteeth.common import *
 
 def print_with_prefix(s, prefix=""):
        if not isinstance(s, str):
@@ -19,6 +19,13 @@ def seed_to_data(seed, random=True):
        else:
                return seed
 
+def comp(p1, p2):
+       r = True
+       for x, y in zip(p1, p2):
+               if x != y:
+                       r = False
+       return r
+
 def check(p1, p2):
        p1 = copy.deepcopy(p1)
        p2 = copy.deepcopy(p2)
@@ -51,16 +58,19 @@ class Packet(list):
                        self.append(data)
 
 class PacketStreamer(Module):
-       def __init__(self, description):
+       def __init__(self, description, last_be=None):
                self.source = Source(description)
+               self.last_be = last_be
                ###
                self.packets = []
                self.packet = Packet()
-               self.packet.done = 1
+               self.packet.done = True
 
        def send(self, packet):
                packet = copy.deepcopy(packet)
                self.packets.append(packet)
+               while not packet.done:
+                       yield
 
        def do_simulation(self, selfp):
                if len(self.packets) and self.packet.done:
@@ -68,16 +78,22 @@ class PacketStreamer(Module):
                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)
+                       selfp.source.data = 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) == 1:
+                               selfp.source.eop = 1
+                               if self.last_be is not None:
+                                       selfp.source.last_be = self.last_be
+                       else:
+                               selfp.source.eop = 0
+                               selfp.source.last_be = 0
                        if len(self.packet) > 0:
                                selfp.source.stb = 1
-                               selfp.source.d = self.packet.pop(0)
+                               selfp.source.data = self.packet.pop(0)
                        else:
-                               self.packet.done = 1
+                               self.packet.done = True
                                selfp.source.stb = 0
 
 class PacketLogger(Module):
@@ -87,17 +103,17 @@ class PacketLogger(Module):
                self.packet = Packet()
 
        def receive(self):
-               self.packet.done = 0
-               while self.packet.done == 0:
+               self.packet.done = False
+               while not self.packet.done:
                        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)
+                       self.packet.append(selfp.sink.data)
                elif selfp.sink.stb:
-                       self.packet.append(selfp.sink.d)
+                       self.packet.append(selfp.sink.data)
                if selfp.sink.stb == 1 and selfp.sink.eop == 1:
                        self.packet.done = True
 
index 1e4dbf1fe3708903f6f3fe5f73c5fe22d199889a..0f766b5476be158b1d95dcbc5dd19a9213a65e8f 100644 (file)
@@ -3,69 +3,23 @@ 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 liteeth.common import *
+from liteeth.mac import LiteEthMAC
 
-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
+from liteeth.test.common import *
+from liteeth.test.model import phy, mac
 
 class TB(Module):
        def __init__(self):
-               self.submodules.ethphy = loopback.LoopbackPHY()
-               self.submodules.ethmac = EthMAC(phy=self.ethphy, with_hw_preamble_crc=True)
+               self.submodules.hostphy = phy.PHY(8, debug=True)
+               self.submodules.hostmac = mac.MAC(self.hostphy, debug=True, random_level=0)
+               self.submodules.ethmac = LiteEthMAC(phy=self.hostphy, dw=32, interface="core", with_hw_preamble_crc=True)
+
+               self.submodules.streamer = PacketStreamer(eth_mac_description(32), last_be=1)
+               self.submodules.streamer_randomizer = AckRandomizer(eth_mac_description(32), level=0)
+
+               self.submodules.logger_randomizer = AckRandomizer(eth_mac_description(32), level=0)
+               self.submodules.logger = PacketLogger(eth_mac_description(32))
 
                # use sys_clk for each clock_domain
                self.clock_domains.cd_eth_rx = ClockDomain()
@@ -77,6 +31,13 @@ class TB(Module):
                        self.cd_eth_tx.rst.eq(ResetSignal()),
                ]
 
+               self.comb += [
+                       Record.connect(self.streamer.source, self.streamer_randomizer.sink),
+                       Record.connect(self.streamer_randomizer.source, self.ethmac.sink),
+                       Record.connect(self.ethmac.source, self.logger_randomizer.sink),
+                       Record.connect(self.logger_randomizer.source, self.logger.sink)
+               ]
+
        def gen_simulation(self, selfp):
                selfp.cd_eth_rx.rst = 1
                selfp.cd_eth_tx.rst = 1
@@ -84,40 +45,10 @@ class TB(Module):
                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))
+               for i in range(8):
+                       streamer_packet = Packet([i for i in range(64)])
+                       print(streamer_packet)
+                       yield from self.streamer.send(streamer_packet)
 
 if __name__ == "__main__":
-       run_simulation(TB(), vcd_name="my.vcd")
+       run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True)
diff --git a/liteeth/test/model/mac.py b/liteeth/test/model/mac.py
new file mode 100644 (file)
index 0000000..7795ed3
--- /dev/null
@@ -0,0 +1,82 @@
+import binascii
+
+from liteeth.common import *
+from liteeth.mac.common import *
+from liteeth.test.common import *
+
+def crc32(l):
+       crc = []
+       crc_bytes = binascii.crc32(bytes(l)).to_bytes(4, byteorder="little")
+       for byte in crc_bytes:
+               crc.append(int(byte))
+       return crc
+
+# MAC model
+class MACPacket(list):
+       def __init__(self, init=[]):
+               self.ongoing = False
+               self.done = False
+               for byte in init:
+                       self.append(byte)
+
+class MACRXPacket(MACPacket):
+       def check_remove_preamble(self):
+               if comp(self[0:8], [0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5]):
+                       for i in range(8):
+                               self.pop(0)
+                       return False
+               else:
+                       return True
+
+       def check_remove_crc(self):
+               if comp(self[-4:], crc32(self[:-4])):
+                       for i in range(4):
+                               self.pop()
+                       return False
+               else:
+                       return True
+
+class MACTXPacket(MACPacket):
+       def insert_crc(self):
+               return self
+
+       def insert_preamble(self):
+               return self
+
+class MAC(Module):
+       def  __init__(self, phy, debug=False, random_level=0):
+               self.phy = phy
+               self.debug = debug
+               self.random_level = random_level
+               self.tx_packets = []
+               self.tx_packet = MACTXPacket()
+               self.rx_packet = MACRXPacket()
+
+               self.ip_callback = None
+
+       def set_ip_callback(self, callback):
+               self.ip_callback = callback
+
+       def send(self, datas):
+               tx_packet = MACTXPacket(datas)
+               tx_packet.insert_crc()
+               tx_packet.insert_preamble()
+               self.tx_packets.append(tx_packet)
+
+       def callback(self, datas):
+               rx_packet = MACRXPacket(datas)
+               preamble_error = rx_packet.check_remove_preamble()
+               crc_error = rx_packet.check_remove_crc()
+               if (not preamble_error) and (not crc_error):
+                       if self.ip_callback is not None:
+                               self.ip_callback(rx_packet)
+
+       def gen_simulation(self, selfp):
+               self.tx_packet.done = True
+               while True:
+                       yield from self.phy.receive()
+                       self.callback(self.phy.packet)
+                       # XXX add full duplex
+                       if len(self.tx_packets) != 0:
+                               tx_packet = self.tx_packets.pop(0)
+                               yield from self.phy.send(tx_packet)
index 2708ed4c777ab050b3d7ac8820d9fcf9be01c1b0..40689537a754d35e3625642413924374744124b7 100644 (file)
@@ -16,13 +16,18 @@ class PHY(Module):
                self.dw = dw
                self.debug = debug
 
-               self.phy_source = PHYSource(dw)
-               self.phy_sink = PHYSink(dw)
+               self.submodules.phy_source = PHYSource(dw)
+               self.submodules.phy_sink = PHYSink(dw)
 
                self.source = self.phy_source.source
                self.sink = self.phy_sink.sink
 
-       def send(self, datas, blocking=True):
+               self.mac_callback = None
+
+       def set_mac_callback(self, callback):
+               self.mac_callback = callback
+
+       def send(self, datas):
                packet = Packet(datas)
                yield from self.phy_source.send(packet, blocking)