diff --git a/liteeth/common.py b/liteeth/common.py index 9cf764eb7..272f946cf 100644 --- a/liteeth/common.py +++ b/liteeth/common.py @@ -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 diff --git a/liteeth/mac/core/__init__.py b/liteeth/mac/core/__init__.py index 5adaaf3fc..e4c378a8a 100644 --- a/liteeth/mac/core/__init__.py +++ b/liteeth/mac/core/__init__.py @@ -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") diff --git a/liteeth/mac/core/crc.py b/liteeth/mac/core/crc.py index 7e37fec19..e04e303bf 100644 --- a/liteeth/mac/core/crc.py +++ b/liteeth/mac/core/crc.py @@ -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, diff --git a/liteeth/test/Makefile b/liteeth/test/Makefile index 31b69ebe4..974f1e570 100644 --- a/liteeth/test/Makefile +++ b/liteeth/test/Makefile @@ -1,7 +1,7 @@ LEDIR = ../../ PYTHON = python3 -CMD = PYTHONPATH=$(MSCDIR) $(PYTHON) +CMD = PYTHONPATH=$(LEDIR) $(PYTHON) mac_core_tb: $(CMD) mac_core_tb.py diff --git a/liteeth/test/common.py b/liteeth/test/common.py index 32e2aaeaa..345e467b2 100644 --- a/liteeth/test/common.py +++ b/liteeth/test/common.py @@ -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 diff --git a/liteeth/test/mac_core_tb.py b/liteeth/test/mac_core_tb.py index 1e4dbf1fe..0f766b547 100644 --- a/liteeth/test/mac_core_tb.py +++ b/liteeth/test/mac_core_tb.py @@ -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 index 000000000..7795ed388 --- /dev/null +++ b/liteeth/test/model/mac.py @@ -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) diff --git a/liteeth/test/model/phy.py b/liteeth/test/model/phy.py index 2708ed4c7..40689537a 100644 --- a/liteeth/test/model/phy.py +++ b/liteeth/test/model/phy.py @@ -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)