test: add mac simulation skeleton
This commit is contained in:
parent
8a42d74904
commit
33edf11ec9
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
LEDIR = ../../
|
||||
PYTHON = python3
|
||||
|
||||
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
|
||||
CMD = PYTHONPATH=$(LEDIR) $(PYTHON)
|
||||
|
||||
mac_core_tb:
|
||||
$(CMD) mac_core_tb.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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue