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.resetsync import AsyncResetSynchronizer
|
||||||
from migen.genlib.record import *
|
from migen.genlib.record import *
|
||||||
from migen.genlib.fsm import FSM, NextState
|
from migen.genlib.fsm import FSM, NextState
|
||||||
|
from migen.genlib.misc import chooser
|
||||||
from migen.flow.actor import EndpointDescription
|
from migen.flow.actor import EndpointDescription
|
||||||
from migen.flow.actor import Sink, Source
|
from migen.flow.actor import Sink, Source
|
||||||
from migen.actorlib.structuring import Converter, Pipeline
|
from migen.actorlib.structuring import Converter, Pipeline
|
||||||
|
|
|
@ -16,8 +16,8 @@ class LiteEthMACCore(Module, AutoCSR):
|
||||||
self.submodules += RenameClockDomains(preamble_checker, "eth_rx")
|
self.submodules += RenameClockDomains(preamble_checker, "eth_rx")
|
||||||
|
|
||||||
# CRC insert/check
|
# CRC insert/check
|
||||||
crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_description(phy.dw))
|
crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw))
|
||||||
crc32_checker = crc.LiteEthMACCRC32Checker(eth_description(phy.dw))
|
crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw))
|
||||||
self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
|
self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
|
||||||
self.submodules += RenameClockDomains(crc32_checker, "eth_rx")
|
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)
|
reg = Signal(self.width, reset=self.init)
|
||||||
self.sync += reg.eq(self.engine.next)
|
self.sync += reg.eq(self.engine.next)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
|
@ -255,14 +255,14 @@ class LiteEthMACCRCChecker(Module):
|
||||||
NextState("IDLE"),
|
NextState("IDLE"),
|
||||||
)
|
)
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
crc.d.eq(sink.data),
|
crc.data.eq(sink.data),
|
||||||
If(sink.stb & sink.sop & sink.ack,
|
If(sink.stb & sink.sop & sink.ack,
|
||||||
crc.ce.eq(1),
|
crc.ce.eq(1),
|
||||||
NextState("COPY")
|
NextState("COPY")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("COPY",
|
fsm.act("COPY",
|
||||||
crc.d.eq(sink.data),
|
crc.data.eq(sink.data),
|
||||||
If(sink.stb & sink.ack,
|
If(sink.stb & sink.ack,
|
||||||
crc.ce.eq(1),
|
crc.ce.eq(1),
|
||||||
If(sink.eop,
|
If(sink.eop,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
LEDIR = ../../
|
LEDIR = ../../
|
||||||
PYTHON = python3
|
PYTHON = python3
|
||||||
|
|
||||||
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
|
CMD = PYTHONPATH=$(LEDIR) $(PYTHON)
|
||||||
|
|
||||||
mac_core_tb:
|
mac_core_tb:
|
||||||
$(CMD) mac_core_tb.py
|
$(CMD) mac_core_tb.py
|
||||||
|
|
|
@ -4,7 +4,7 @@ from migen.fhdl.std import *
|
||||||
from migen.flow.actor import Sink, Source
|
from migen.flow.actor import Sink, Source
|
||||||
from migen.genlib.record import *
|
from migen.genlib.record import *
|
||||||
|
|
||||||
from misoclib.ethmac.common import *
|
from liteeth.common import *
|
||||||
|
|
||||||
def print_with_prefix(s, prefix=""):
|
def print_with_prefix(s, prefix=""):
|
||||||
if not isinstance(s, str):
|
if not isinstance(s, str):
|
||||||
|
@ -19,6 +19,13 @@ def seed_to_data(seed, random=True):
|
||||||
else:
|
else:
|
||||||
return seed
|
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):
|
def check(p1, p2):
|
||||||
p1 = copy.deepcopy(p1)
|
p1 = copy.deepcopy(p1)
|
||||||
p2 = copy.deepcopy(p2)
|
p2 = copy.deepcopy(p2)
|
||||||
|
@ -51,16 +58,19 @@ class Packet(list):
|
||||||
self.append(data)
|
self.append(data)
|
||||||
|
|
||||||
class PacketStreamer(Module):
|
class PacketStreamer(Module):
|
||||||
def __init__(self, description):
|
def __init__(self, description, last_be=None):
|
||||||
self.source = Source(description)
|
self.source = Source(description)
|
||||||
|
self.last_be = last_be
|
||||||
###
|
###
|
||||||
self.packets = []
|
self.packets = []
|
||||||
self.packet = Packet()
|
self.packet = Packet()
|
||||||
self.packet.done = 1
|
self.packet.done = True
|
||||||
|
|
||||||
def send(self, packet):
|
def send(self, packet):
|
||||||
packet = copy.deepcopy(packet)
|
packet = copy.deepcopy(packet)
|
||||||
self.packets.append(packet)
|
self.packets.append(packet)
|
||||||
|
while not packet.done:
|
||||||
|
yield
|
||||||
|
|
||||||
def do_simulation(self, selfp):
|
def do_simulation(self, selfp):
|
||||||
if len(self.packets) and self.packet.done:
|
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:
|
if not self.packet.ongoing and not self.packet.done:
|
||||||
selfp.source.stb = 1
|
selfp.source.stb = 1
|
||||||
selfp.source.sop = 1
|
selfp.source.sop = 1
|
||||||
selfp.source.d = self.packet.pop(0)
|
selfp.source.data = self.packet.pop(0)
|
||||||
self.packet.ongoing = True
|
self.packet.ongoing = True
|
||||||
elif selfp.source.stb == 1 and selfp.source.ack == 1:
|
elif selfp.source.stb == 1 and selfp.source.ack == 1:
|
||||||
selfp.source.sop = 0
|
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:
|
if len(self.packet) > 0:
|
||||||
selfp.source.stb = 1
|
selfp.source.stb = 1
|
||||||
selfp.source.d = self.packet.pop(0)
|
selfp.source.data = self.packet.pop(0)
|
||||||
else:
|
else:
|
||||||
self.packet.done = 1
|
self.packet.done = True
|
||||||
selfp.source.stb = 0
|
selfp.source.stb = 0
|
||||||
|
|
||||||
class PacketLogger(Module):
|
class PacketLogger(Module):
|
||||||
|
@ -87,17 +103,17 @@ class PacketLogger(Module):
|
||||||
self.packet = Packet()
|
self.packet = Packet()
|
||||||
|
|
||||||
def receive(self):
|
def receive(self):
|
||||||
self.packet.done = 0
|
self.packet.done = False
|
||||||
while self.packet.done == 0:
|
while not self.packet.done:
|
||||||
yield
|
yield
|
||||||
|
|
||||||
def do_simulation(self, selfp):
|
def do_simulation(self, selfp):
|
||||||
selfp.sink.ack = 1
|
selfp.sink.ack = 1
|
||||||
if selfp.sink.stb == 1 and selfp.sink.sop == 1:
|
if selfp.sink.stb == 1 and selfp.sink.sop == 1:
|
||||||
self.packet = Packet()
|
self.packet = Packet()
|
||||||
self.packet.append(selfp.sink.d)
|
self.packet.append(selfp.sink.data)
|
||||||
elif selfp.sink.stb:
|
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:
|
if selfp.sink.stb == 1 and selfp.sink.eop == 1:
|
||||||
self.packet.done = True
|
self.packet.done = True
|
||||||
|
|
||||||
|
|
|
@ -3,69 +3,23 @@ from migen.bus import wishbone
|
||||||
from migen.bus.transactions import *
|
from migen.bus.transactions import *
|
||||||
from migen.sim.generic import run_simulation
|
from migen.sim.generic import run_simulation
|
||||||
|
|
||||||
from misoclib.ethmac import EthMAC
|
from liteeth.common import *
|
||||||
from misoclib.ethmac.phy import loopback
|
from liteeth.mac import LiteEthMAC
|
||||||
|
|
||||||
from misoclib.ethmac.test.common import *
|
from liteeth.test.common import *
|
||||||
|
from liteeth.test.model import phy, mac
|
||||||
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):
|
class TB(Module):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.submodules.ethphy = loopback.LoopbackPHY()
|
self.submodules.hostphy = phy.PHY(8, debug=True)
|
||||||
self.submodules.ethmac = EthMAC(phy=self.ethphy, with_hw_preamble_crc=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
|
# use sys_clk for each clock_domain
|
||||||
self.clock_domains.cd_eth_rx = ClockDomain()
|
self.clock_domains.cd_eth_rx = ClockDomain()
|
||||||
|
@ -77,6 +31,13 @@ class TB(Module):
|
||||||
self.cd_eth_tx.rst.eq(ResetSignal()),
|
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):
|
def gen_simulation(self, selfp):
|
||||||
selfp.cd_eth_rx.rst = 1
|
selfp.cd_eth_rx.rst = 1
|
||||||
selfp.cd_eth_tx.rst = 1
|
selfp.cd_eth_tx.rst = 1
|
||||||
|
@ -84,40 +45,10 @@ class TB(Module):
|
||||||
selfp.cd_eth_rx.rst = 0
|
selfp.cd_eth_rx.rst = 0
|
||||||
selfp.cd_eth_tx.rst = 0
|
selfp.cd_eth_tx.rst = 0
|
||||||
|
|
||||||
wishbone_master = WishboneMaster(selfp.ethmac.bus)
|
for i in range(8):
|
||||||
sram_reader_driver = SRAMReaderDriver(selfp.ethmac.sram_reader)
|
streamer_packet = Packet([i for i in range(64)])
|
||||||
|
print(streamer_packet)
|
||||||
sram_writer_slots_offset = [0x000, 0x200]
|
yield from self.streamer.send(streamer_packet)
|
||||||
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__":
|
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.dw = dw
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
|
||||||
self.phy_source = PHYSource(dw)
|
self.submodules.phy_source = PHYSource(dw)
|
||||||
self.phy_sink = PHYSink(dw)
|
self.submodules.phy_sink = PHYSink(dw)
|
||||||
|
|
||||||
self.source = self.phy_source.source
|
self.source = self.phy_source.source
|
||||||
self.sink = self.phy_sink.sink
|
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)
|
packet = Packet(datas)
|
||||||
yield from self.phy_source.send(packet, blocking)
|
yield from self.phy_source.send(packet, blocking)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue