test: add mac simulation skeleton

This commit is contained in:
Florent Kermarrec 2015-01-28 19:07:59 +01:00
parent 8a42d74904
commit 33edf11ec9
8 changed files with 149 additions and 114 deletions

View 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

View 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")

View 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,

View File

@ -1,7 +1,7 @@
LEDIR = ../../
PYTHON = python3
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
CMD = PYTHONPATH=$(LEDIR) $(PYTHON)
mac_core_tb:
$(CMD) mac_core_tb.py

View 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

View 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)

82
liteeth/test/model/mac.py Normal file
View File

@ -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)

View 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)