mac: import files from MiSoC

This commit is contained in:
Florent Kermarrec 2015-01-27 23:59:06 +01:00
parent a160b04d2f
commit 46c4841d68
15 changed files with 1293 additions and 0 deletions

View file

@ -0,0 +1,111 @@
# This file is Copyright (c) 2014 Florent Kermarrec <florent@enjoy-digital.fr>
# License: BSD
from migen.fhdl.std import *
from migen.bus import wishbone
from migen.actorlib.fifo import AsyncFIFO
from migen.actorlib.structuring import Converter, Pipeline
from migen.bank.eventmanager import SharedIRQ
from migen.bank.description import *
from migen.fhdl.simplify import *
from misoclib.ethmac.common import *
from misoclib.ethmac.preamble import PreambleInserter, PreambleChecker
from migen.actorlib.crc import CRC32Inserter, CRC32Checker
from misoclib.ethmac.last_be import TXLastBE, RXLastBE
from misoclib.ethmac.sram import SRAMWriter, SRAMReader
class EthMAC(Module, AutoCSR):
def __init__(self, phy, interface="wishbone", with_hw_preamble_crc=True, endianness="be"):
# Preamble / CRC (optional)
if with_hw_preamble_crc:
self._hw_preamble_crc = CSRStatus(reset=1)
# Preamble insert/check
preamble_inserter = PreambleInserter(phy.dw)
preamble_checker = PreambleChecker(phy.dw)
self.submodules += RenameClockDomains(preamble_inserter, "eth_tx")
self.submodules += RenameClockDomains(preamble_checker, "eth_rx")
# CRC insert/check
crc32_inserter = CRC32Inserter(eth_description(phy.dw))
crc32_checker = CRC32Checker(eth_description(phy.dw))
self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
self.submodules += RenameClockDomains(crc32_checker, "eth_rx")
# Delimiters
tx_last_be = TXLastBE(phy.dw)
rx_last_be = RXLastBE(phy.dw)
self.submodules += RenameClockDomains(tx_last_be, "eth_tx")
self.submodules += RenameClockDomains(rx_last_be, "eth_rx")
# Converters
reverse = endianness == "be"
tx_converter = Converter(eth_description(32), eth_description(phy.dw), reverse=reverse)
rx_converter = Converter(eth_description(phy.dw), eth_description(32), reverse=reverse)
self.submodules += RenameClockDomains(tx_converter, "eth_tx")
self.submodules += RenameClockDomains(rx_converter, "eth_rx")
# Cross Domain Crossing
tx_cdc = AsyncFIFO(eth_description(32), 4)
rx_cdc = AsyncFIFO(eth_description(32), 4)
self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"})
self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"})
# Graph
if with_hw_preamble_crc:
rx_pipeline = [phy, preamble_checker, crc32_checker, rx_last_be, rx_converter, rx_cdc]
tx_pipeline = [tx_cdc, tx_converter, tx_last_be, crc32_inserter, preamble_inserter, phy]
else:
rx_pipeline = [phy, rx_last_be, rx_converter, rx_cdc]
tx_pipeline = [tx_cdc, tx_converter, tx_last_be, phy]
self.submodules.rx_pipeline = Pipeline(*rx_pipeline)
self.submodules.tx_pipeline = Pipeline(*tx_pipeline)
if interface == "wishbone":
nrxslots = 2
ntxslots = 2
self.bus = wishbone.Interface()
# SRAM Memories
sram_depth = buffer_depth//(32//8)
self.submodules.sram_writer = SRAMWriter(sram_depth, nrxslots)
self.submodules.sram_reader = SRAMReader(sram_depth, ntxslots)
self.submodules.ev = SharedIRQ(self.sram_writer.ev, self.sram_reader.ev)
# Connect to pipelines
self.comb += [
self.rx_pipeline.source.connect(self.sram_writer.sink),
self.sram_reader.source.connect(self.tx_pipeline.sink)
]
# Interface
wb_rx_sram_ifs = [wishbone.SRAM(self.sram_writer.mems[n], read_only=True)
for n in range(nrxslots)]
# TODO: FullMemoryWE should move to Mibuild
wb_tx_sram_ifs = [FullMemoryWE(wishbone.SRAM(self.sram_reader.mems[n], read_only=False))
for n in range(ntxslots)]
wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs
wb_slaves = []
decoderoffset = log2_int(sram_depth)
decoderbits = log2_int(len(wb_sram_ifs))
for n, wb_sram_if in enumerate(wb_sram_ifs):
def slave_filter(a, v=n):
return a[decoderoffset:decoderoffset+decoderbits] == v
wb_slaves.append((slave_filter, wb_sram_if.bus))
self.submodules += wb_sram_if
wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True)
self.submodules += wb_con
elif interface == "lasmi":
raise NotImplementedError
elif interface == "expose":
# expose pipelines endpoints
self.sink = tx_pipeline.sink
self.source = rx_pipeline.source
else:
raise ValueError("EthMAC only supports Wishbone, LASMI or expose interfaces")

View file

@ -0,0 +1,14 @@
from migen.fhdl.std import *
from migen.flow.actor import EndpointDescription
eth_mtu = 1532
eth_preamble = 0xD555555555555555
buffer_depth = 2**log2_int(eth_mtu, need_pow2=False)
def eth_description(dw):
layout = [
("d", dw),
("last_be", dw//8),
("error", dw//8)
]
return EndpointDescription(layout, packetized=True)

View file

@ -0,0 +1,39 @@
from migen.fhdl.std import *
from migen.genlib.record import *
from migen.flow.actor import Sink, Source
from misoclib.ethmac.common import *
class TXLastBE(Module):
def __init__(self, d_w):
self.sink = sink = Sink(eth_description(d_w))
self.source = source = Source(eth_description(d_w))
###
ongoing = Signal()
self.sync += \
If(self.sink.stb & self.sink.ack,
If(sink.sop,
ongoing.eq(1)
).Elif(sink.last_be,
ongoing.eq(0)
)
)
self.comb += [
Record.connect(self.sink, self.source),
self.source.eop.eq(self.sink.last_be),
self.source.stb.eq(self.sink.stb & (self.sink.sop | ongoing))
]
class RXLastBE(Module):
def __init__(self, d_w):
self.sink = sink = Sink(eth_description(d_w))
self.source = source = Source(eth_description(d_w))
###
self.comb += [
Record.connect(self.sink, self.source),
self.source.last_be.eq(self.sink.eop)
]

View file

@ -0,0 +1,145 @@
from migen.fhdl.std import *
from migen.genlib.fsm import FSM, NextState
from migen.genlib.misc import chooser
from migen.genlib.record import *
from migen.flow.actor import Sink, Source
from misoclib.ethmac.common import *
class PreambleInserter(Module):
def __init__(self, d_w):
self.sink = Sink(eth_description(d_w))
self.source = Source(eth_description(d_w))
###
preamble = Signal(64, reset=eth_preamble)
cnt_max = (64//d_w)-1
cnt = Signal(max=cnt_max+1)
clr_cnt = Signal()
inc_cnt = Signal()
self.sync += \
If(clr_cnt,
cnt.eq(0)
).Elif(inc_cnt,
cnt.eq(cnt+1)
)
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
self.sink.ack.eq(1),
clr_cnt.eq(1),
If(self.sink.stb & self.sink.sop,
self.sink.ack.eq(0),
NextState("INSERT"),
)
)
fsm.act("INSERT",
self.source.stb.eq(1),
self.source.sop.eq(cnt==0),
chooser(preamble, cnt, self.source.d),
If(cnt == cnt_max,
If(self.source.ack, NextState("COPY"))
).Else(
inc_cnt.eq(self.source.ack)
)
)
fsm.act("COPY",
Record.connect(self.sink, self.source),
self.source.sop.eq(0),
If(self.sink.stb & self.sink.eop & self.source.ack,
NextState("IDLE"),
)
)
class PreambleChecker(Module):
def __init__(self, d_w):
self.sink = Sink(eth_description(d_w))
self.source = Source(eth_description(d_w))
###
preamble = Signal(64, reset=eth_preamble)
cnt_max = (64//d_w) - 1
cnt = Signal(max=cnt_max+1)
clr_cnt = Signal()
inc_cnt = Signal()
self.sync += \
If(clr_cnt,
cnt.eq(0)
).Elif(inc_cnt,
cnt.eq(cnt+1)
)
discard = Signal()
clr_discard = Signal()
set_discard = Signal()
self.sync += \
If(clr_discard,
discard.eq(0)
).Elif(set_discard,
discard.eq(1)
)
sop = Signal()
clr_sop = Signal()
set_sop = Signal()
self.sync += \
If(clr_sop,
sop.eq(0)
).Elif(set_sop,
sop.eq(1)
)
ref = Signal(d_w)
match = Signal()
self.comb += [
chooser(preamble, cnt, ref),
match.eq(self.sink.d == ref)
]
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
self.sink.ack.eq(1),
clr_cnt.eq(1),
clr_discard.eq(1),
If(self.sink.stb & self.sink.sop,
clr_cnt.eq(0),
inc_cnt.eq(1),
clr_discard.eq(0),
set_discard.eq(~match),
NextState("CHECK"),
)
)
fsm.act("CHECK",
self.sink.ack.eq(1),
If(self.sink.stb,
set_discard.eq(~match),
If(cnt == cnt_max,
If(discard | (~match),
NextState("IDLE")
).Else(
set_sop.eq(1),
NextState("COPY")
)
).Else(
inc_cnt.eq(1)
)
)
)
fsm.act("COPY",
Record.connect(self.sink, self.source),
self.source.sop.eq(sop),
clr_sop.eq(self.source.stb & self.source.ack),
If(self.source.stb & self.source.eop & self.source.ack,
NextState("IDLE"),
)
)

View file

@ -0,0 +1,251 @@
from migen.fhdl.std import *
from migen.genlib.fifo import SyncFIFO
from migen.genlib.fsm import FSM, NextState
from migen.genlib.misc import chooser
from migen.flow.actor import Sink, Source
from migen.bank.description import *
from migen.bank.eventmanager import *
from misoclib.ethmac.common import *
class SRAMWriter(Module, AutoCSR):
def __init__(self, depth, nslots=2):
self.sink = sink = Sink(eth_description(32))
self.crc_error = Signal()
slotbits = max(log2_int(nslots), 1)
lengthbits = log2_int(depth*4) # length in bytes
self._slot = CSRStatus(slotbits)
self._length = CSRStatus(lengthbits)
self.submodules.ev = EventManager()
self.ev.available = EventSourceLevel()
self.ev.finalize()
###
# packet dropped if no slot available
sink.ack.reset = 1
# length computation
cnt = Signal(lengthbits)
clr_cnt = Signal()
inc_cnt = Signal()
inc_val = Signal(3)
self.comb += \
If(sink.last_be[3],
inc_val.eq(1)
).Elif(sink.last_be[2],
inc_val.eq(2)
).Elif(sink.last_be[1],
inc_val.eq(3)
).Else(
inc_val.eq(4)
)
self.sync += \
If(clr_cnt,
cnt.eq(0)
).Elif(inc_cnt,
cnt.eq(cnt+inc_val)
)
# slot computation
slot = Signal(slotbits)
inc_slot = Signal()
self.sync += \
If(inc_slot,
If(slot == nslots-1,
slot.eq(0),
).Else(
slot.eq(slot+1)
)
)
ongoing = Signal()
discard = Signal()
# status fifo
fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots)
self.submodules += fifo
# fsm
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
inc_cnt.eq(sink.stb),
If(sink.stb & sink.sop,
ongoing.eq(1),
If(fifo.writable,
NextState("WRITE")
)
)
)
fsm.act("WRITE",
inc_cnt.eq(sink.stb),
ongoing.eq(1),
If(sink.stb & sink.eop,
If((sink.error & sink.last_be) != 0,
NextState("DISCARD")
).Else(
NextState("TERMINATE")
)
)
)
fsm.act("DISCARD",
clr_cnt.eq(1),
NextState("IDLE")
)
fsm.act("TERMINATE",
clr_cnt.eq(1),
inc_slot.eq(1),
fifo.we.eq(1),
fifo.din.slot.eq(slot),
fifo.din.length.eq(cnt),
NextState("IDLE")
)
self.comb += [
fifo.re.eq(self.ev.available.clear),
self.ev.available.trigger.eq(fifo.readable),
self._slot.status.eq(fifo.dout.slot),
self._length.status.eq(fifo.dout.length),
]
# memory
mems = [None]*nslots
ports = [None]*nslots
for n in range(nslots):
mems[n] = Memory(32, depth)
ports[n] = mems[n].get_port(write_capable=True)
self.specials += ports[n]
self.mems = mems
cases = {}
for n, port in enumerate(ports):
cases[n] = [
ports[n].adr.eq(cnt[2:]),
ports[n].dat_w.eq(sink.d),
If(sink.stb & ongoing,
ports[n].we.eq(0xf)
)
]
self.comb += Case(slot, cases)
class SRAMReader(Module, AutoCSR):
def __init__(self, depth, nslots=2):
self.source = source = Source(eth_description(32))
slotbits = max(log2_int(nslots), 1)
lengthbits = log2_int(depth*4) # length in bytes
self.lengthbits = lengthbits
self._start = CSR()
self._ready = CSRStatus()
self._slot = CSRStorage(slotbits)
self._length = CSRStorage(lengthbits)
self.submodules.ev = EventManager()
self.ev.done = EventSourcePulse()
self.ev.finalize()
###
# command fifo
fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots)
self.submodules += fifo
self.comb += [
fifo.we.eq(self._start.re),
fifo.din.slot.eq(self._slot.storage),
fifo.din.length.eq(self._length.storage),
self._ready.status.eq(fifo.writable)
]
# length computation
cnt = Signal(lengthbits)
clr_cnt = Signal()
inc_cnt = Signal()
self.sync += \
If(clr_cnt,
cnt.eq(0)
).Elif(inc_cnt,
cnt.eq(cnt+4)
)
# fsm
first = Signal()
last = Signal()
last_d = Signal()
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
clr_cnt.eq(1),
If(fifo.readable,
NextState("CHECK")
)
)
fsm.act("CHECK",
If(~last_d,
NextState("SEND"),
).Else(
NextState("END"),
)
)
length_lsb = fifo.dout.length[0:2]
fsm.act("SEND",
source.stb.eq(1),
source.sop.eq(first),
source.eop.eq(last),
If(last,
If(length_lsb == 3,
source.last_be.eq(0b0010)
).Elif(length_lsb == 2,
source.last_be.eq(0b0100)
).Elif(length_lsb == 1,
source.last_be.eq(0b1000)
).Else(
source.last_be.eq(0b0001)
)
),
If(source.ack,
inc_cnt.eq(~last),
NextState("CHECK")
)
)
fsm.act("END",
fifo.re.eq(1),
self.ev.done.trigger.eq(1),
NextState("IDLE")
)
# first/last computation
self.sync += [
If(fsm.ongoing("IDLE"),
first.eq(1)
).Elif(source.stb & source.ack,
first.eq(0)
)
]
self.comb += last.eq(cnt + 4 >= fifo.dout.length)
self.sync += last_d.eq(last)
# memory
rd_slot = fifo.dout.slot
mems = [None]*nslots
ports = [None]*nslots
for n in range(nslots):
mems[n] = Memory(32, depth)
ports[n] = mems[n].get_port()
self.specials += ports[n]
self.mems = mems
cases = {}
for n, port in enumerate(ports):
self.comb += ports[n].adr.eq(cnt[2:])
cases[n] = [source.d.eq(port.dat_r)]
self.comb += Case(rd_slot, cases)

View file

@ -0,0 +1,76 @@
from migen.fhdl.std import *
from migen.flow.actor import Sink, Source
from migen.bank.description import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoclib.ethmac.common import *
class GMIIPHYTX(Module):
def __init__(self, pads):
self.sink = sink = Sink(eth_description(8))
###
self.sync += [
pads.tx_er.eq(0),
pads.tx_en.eq(sink.stb),
pads.tx_data.eq(sink.d)
]
self.comb += sink.ack.eq(1)
class GMIIPHYRX(Module):
def __init__(self, pads):
self.source = source = Source(eth_description(8))
###
dv_d = Signal()
self.sync += dv_d.eq(pads.dv)
sop = Signal()
eop = Signal()
self.comb += [
sop.eq(pads.dv & ~dv_d),
eop.eq(~pads.dv & dv_d)
]
self.sync += [
source.stb.eq(pads.dv),
source.sop.eq(sop),
source.d.eq(pads.rx_data)
]
self.comb += source.eop.eq(eop)
# CRG is the only Xilinx specific module.
# TODO: use generic code or add support for others vendors
class GMIIPHYCRG(Module, AutoCSR):
def __init__(self, clock_pads, pads):
self._reset = CSRStorage()
###
self.clock_domains.cd_eth_rx = ClockDomain()
self.clock_domains.cd_eth_tx = ClockDomain()
self.specials += [
Instance("ODDR",
p_DDR_CLK_EDGE="SAME_EDGE",
i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0,
i_D1=1, i_D2=0, o_Q=clock_pads.gtx,
),
Instance("BUFG", i_I=clock_pads.rx, o_O=self.cd_eth_rx.clk),
]
self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk)
reset = self._reset.storage
self.comb += pads.rst_n.eq(~reset)
self.specials += [
AsyncResetSynchronizer(self.cd_eth_tx, reset),
AsyncResetSynchronizer(self.cd_eth_rx, reset),
]
class GMIIPHY(Module, AutoCSR):
def __init__(self, clock_pads, pads):
self.dw = 8
self.submodules.crg = GMIIPHYCRG(clock_pads, pads)
self.submodules.tx = RenameClockDomains(GMIIPHYTX(pads), "eth_tx")
self.submodules.rx = RenameClockDomains(GMIIPHYRX(pads), "eth_rx")
self.sink, self.source = self.tx.sink, self.rx.source

View file

@ -0,0 +1,33 @@
from migen.fhdl.std import *
from migen.flow.actor import Sink, Source
from migen.bank.description import *
from migen.genlib.record import *
from misoclib.ethmac.common import *
class LoopbackPHYCRG(Module, AutoCSR):
def __init__(self):
self._reset = CSRStorage()
###
self.clock_domains.cd_eth_rx = ClockDomain()
self.clock_domains.cd_eth_tx = ClockDomain()
self.comb += [
self.cd_eth_rx.clk.eq(ClockSignal()),
self.cd_eth_tx.clk.eq(ClockSignal())
]
reset = self._reset.storage
self.comb += [
self.cd_eth_rx.rst.eq(reset),
self.cd_eth_tx.rst.eq(reset)
]
class LoopbackPHY(Module, AutoCSR):
def __init__(self):
self.dw = 8
self.submodules.crg = LoopbackPHYCRG()
self.sink = sink = Sink(eth_description(8))
self.source = source = Source(eth_description(8))
self.comb += Record.connect(self.sink, self.source)

128
liteethernet/mac/phy/mii.py Normal file
View file

@ -0,0 +1,128 @@
from migen.fhdl.std import *
from migen.genlib.fsm import FSM, NextState
from migen.flow.actor import Sink, Source
from migen.bank.description import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoclib.ethmac.common import *
class MIIPHYTX(Module):
def __init__(self, pads):
self.sink = sink = Sink(eth_description(8))
###
tx_en_r = Signal()
tx_data_r = Signal(4)
self.sync += [
pads.tx_er.eq(0),
pads.tx_en.eq(tx_en_r),
pads.tx_data.eq(tx_data_r),
]
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
sink.ack.eq(1),
If(sink.stb & sink.sop,
sink.ack.eq(0),
NextState("SEND_LO")
)
)
fsm.act("SEND_LO",
tx_data_r.eq(sink.d[0:4]),
tx_en_r.eq(1),
NextState("SEND_HI")
)
fsm.act("SEND_HI",
tx_data_r.eq(sink.d[4:8]),
tx_en_r.eq(1),
sink.ack.eq(1),
If(sink.stb & sink.eop,
NextState("IDLE")
).Else(
NextState("SEND_LO")
)
)
class MIIPHYRX(Module):
def __init__(self, pads):
self.source = source = Source(eth_description(8))
###
sop = source.sop
set_sop = Signal()
clr_sop = Signal()
self.sync += \
If(clr_sop,
sop.eq(0)
).Elif(set_sop,
sop.eq(1)
)
lo = Signal(4)
hi = Signal(4)
load_nibble = Signal(2)
self.sync += \
If(load_nibble[0],
lo.eq(pads.rx_data)
).Elif(load_nibble[1],
hi.eq(pads.rx_data)
)
self.comb += [
source.d.eq(Cat(lo, hi))
]
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
set_sop.eq(1),
If(pads.dv,
load_nibble.eq(0b01),
NextState("LOAD_HI")
)
)
fsm.act("LOAD_LO",
source.stb.eq(1),
If(pads.dv,
clr_sop.eq(1),
load_nibble.eq(0b01),
NextState("LOAD_HI")
).Else(
source.eop.eq(1),
NextState("IDLE")
)
)
fsm.act("LOAD_HI",
load_nibble.eq(0b10),
NextState("LOAD_LO")
)
class MIIPHYCRG(Module, AutoCSR):
def __init__(self, clock_pads, pads):
self._reset = CSRStorage()
###
self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy)
self.clock_domains.cd_eth_rx = ClockDomain()
self.clock_domains.cd_eth_tx = ClockDomain()
self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx)
reset = self._reset.storage
self.comb += pads.rst_n.eq(~reset)
self.specials += [
AsyncResetSynchronizer(self.cd_eth_tx, reset),
AsyncResetSynchronizer(self.cd_eth_rx, reset),
]
class MIIPHY(Module, AutoCSR):
def __init__(self, clock_pads, pads):
self.dw = 8
self.submodules.crg = MIIPHYCRG(clock_pads, pads)
self.submodules.tx = RenameClockDomains(MIIPHYTX(pads), "eth_tx")
self.submodules.rx = RenameClockDomains(MIIPHYRX(pads), "eth_rx")
self.sink, self.source = self.tx.sink, self.rx.source

View file

@ -0,0 +1,13 @@
MSCDIR = ../../../
PYTHON = python3
CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
crc_tb:
$(CMD) crc_tb.py
preamble_tb:
$(CMD) preamble_tb.py
ethmac_tb:
$(CMD) ethmac_tb.py

View file

View file

@ -0,0 +1,120 @@
import random, copy
from migen.fhdl.std import *
from migen.flow.actor import Sink, Source
from migen.genlib.record import *
from misoclib.ethmac.common import *
def seed_to_data(seed, random=True):
if random:
return (seed * 0x31415979 + 1) & 0xffffffff
else:
return seed
def check(p1, p2):
p1 = copy.deepcopy(p1)
p2 = copy.deepcopy(p2)
if isinstance(p1, int):
return 0, 1, int(p1 != p2)
else:
if len(p1) >= len(p2):
ref, res = p1, p2
else:
ref, res = p2, p1
shift = 0
while((ref[0] != res[0]) and (len(res)>1)):
res.pop(0)
shift += 1
length = min(len(ref), len(res))
errors = 0
for i in range(length):
if ref.pop(0) != res.pop(0):
errors += 1
return shift, length, errors
def randn(max_n):
return random.randint(0, max_n-1)
class Packet(list):
def __init__(self, init=[]):
self.ongoing = False
self.done = False
for data in init:
self.append(data)
class PacketStreamer(Module):
def __init__(self, description):
self.source = Source(description)
###
self.packets = []
self.packet = Packet()
self.packet.done = 1
def send(self, packet):
packet = copy.deepcopy(packet)
self.packets.append(packet)
def do_simulation(self, selfp):
if len(self.packets) and self.packet.done:
self.packet = self.packets.pop(0)
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)
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) > 0:
selfp.source.stb = 1
selfp.source.d = self.packet.pop(0)
else:
self.packet.done = 1
selfp.source.stb = 0
class PacketLogger(Module):
def __init__(self, description):
self.sink = Sink(description)
###
self.packet = Packet()
def receive(self):
self.packet.done = 0
while self.packet.done == 0:
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)
elif selfp.sink.stb:
self.packet.append(selfp.sink.d)
if selfp.sink.stb == 1 and selfp.sink.eop == 1:
self.packet.done = True
class AckRandomizer(Module):
def __init__(self, description, level=0):
self.level = level
self.sink = Sink(description)
self.source = Source(description)
self.run = Signal()
self.comb += \
If(self.run,
Record.connect(self.sink, self.source)
).Else(
self.source.stb.eq(0),
self.sink.ack.eq(0),
)
def do_simulation(self, selfp):
n = randn(100)
if n < self.level:
selfp.run = 0
else:
selfp.run = 1

View file

@ -0,0 +1,83 @@
from migen.fhdl.std import *
from migen.actorlib.crc import *
from misoclib.ethmac.common import *
from misoclib.ethmac.test.common import *
payload = [
0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
]
crc = [
0x7A, 0xD5, 0x6B, 0xB3
]
mux = {
"inserter": 0,
"checker": 1,
"both": 2
}
class TB(Module):
def __init__(self, random_level=50):
sm = self.submodules
sm.streamer = PacketStreamer(eth_description(8))
sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level)
sm.logger = PacketLogger(eth_description(8))
sm.logger_randomizer = AckRandomizer(eth_description(8), random_level)
self.comb += [
self.streamer.source.connect(self.streamer_randomizer.sink),
self.logger_randomizer.source.connect(self.logger.sink)
]
sm.crc32_inserter = CRC32Inserter(eth_description(8))
sm.crc32_checker = CRC32Checker(eth_description(8))
self.mux = Signal(2)
self.comb += [
If(self.mux == mux["inserter"],
self.streamer_randomizer.source.connect(self.crc32_inserter.sink),
self.crc32_inserter.source.connect(self.logger_randomizer.sink)
).Elif(self.mux == mux["checker"],
self.streamer_randomizer.source.connect(self.crc32_checker.sink),
self.crc32_checker.source.connect(self.logger_randomizer.sink)
).Elif(self.mux == mux["both"],
self.streamer_randomizer.source.connect(self.crc32_inserter.sink),
self.crc32_inserter.source.connect(self.crc32_checker.sink),
self.crc32_checker.source.connect(self.logger_randomizer.sink)
)
]
def gen_simulation(self, selfp):
selfp.mux = mux["inserter"]
print("streamer --> crc32_inserter --> logger:")
self.streamer.send(Packet(payload))
yield from self.logger.receive()
s, l, e = check(payload+crc, self.logger.packet)
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
selfp.mux = mux["checker"]
print("streamer --> crc32_checker --> logger:")
self.streamer.send(Packet(payload+crc))
yield from self.logger.receive()
s, l, e = check(payload, self.logger.packet)
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
selfp.mux = mux["both"]
print("streamer --> crc32_inserter --> crc32_checker --> logger:")
self.streamer.send(Packet(payload))
yield from self.logger.receive()
s, l, e = check(payload, self.logger.packet)
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
if __name__ == "__main__":
from migen.sim.generic import run_simulation
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd")

View file

@ -0,0 +1,75 @@
[*]
[*] GTKWave Analyzer v3.3.46 (w)1999-2012 BSI
[*] Fri Oct 31 11:20:55 2014
[*]
[dumpfile] "/home/florent/Dev/misoc/misoclib/ethmac/test/my.vcd"
[dumpfile_mtime] "Fri Oct 31 11:20:07 2014"
[dumpfile_size] 5152269
[savefile] "/home/florent/Dev/misoc/misoclib/ethmac/test/ethmac_tb.gtkw"
[timestart] 0
[size] 1548 849
[pos] 101 171
*-25.000000 34300000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
[treeopen] top.
[sst_width] 379
[signals_width] 464
[sst_expanded] 1
[sst_vpaned_height] 485
@200
-WISHBONE
@28
top.dut.bus_ack
@22
top.dut.bus_adr[29:0]
@28
top.dut.bus_bte[1:0]
top.dut.bus_cti[2:0]
top.dut.bus_cyc
@22
top.dut.bus_dat_r[31:0]
top.dut.bus_dat_w[31:0]
@28
top.dut.bus_err
@22
top.dut.bus_sel[3:0]
@28
top.dut.bus_stb
top.dut.bus_we
@200
-SRAM_READER --> PHYTx
@28
top.dut.sram_reader_source_ack
top.dut.sram_reader_source_eop
@22
top.dut.sram_reader_source_payload_d[31:0]
top.dut.sram_reader_source_payload_error[3:0]
top.dut.sram_reader_source_payload_last_be[3:0]
@28
top.dut.sram_reader_source_sop
top.dut.sram_reader_source_stb
@200
-LOOPBACK
@28
top.dut.phy_source_ack
top.dut.phy_source_eop
@22
top.dut.phy_source_payload_d[7:0]
@28
top.dut.phy_source_payload_error
top.dut.phy_source_payload_last_be
top.dut.phy_source_sop
top.dut.phy_source_stb
@200
-PHYRx --> SRAM_WRITER
@28
top.dut.sram_writer_sink_ack
top.dut.sram_writer_sink_eop
@22
top.dut.sram_writer_sink_payload_d[31:0]
top.dut.sram_writer_sink_payload_error[3:0]
top.dut.sram_writer_sink_payload_last_be[3:0]
@28
top.dut.sram_writer_sink_sop
top.dut.sram_writer_sink_stb
[pattern_trace] 1
[pattern_trace] 0

View file

@ -0,0 +1,123 @@
from migen.fhdl.std import *
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 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
class TB(Module):
def __init__(self):
self.submodules.ethphy = loopback.LoopbackPHY()
self.submodules.ethmac = EthMAC(phy=self.ethphy, with_hw_preamble_crc=True)
# use sys_clk for each clock_domain
self.clock_domains.cd_eth_rx = ClockDomain()
self.clock_domains.cd_eth_tx = ClockDomain()
self.comb += [
self.cd_eth_rx.clk.eq(ClockSignal()),
self.cd_eth_rx.rst.eq(ResetSignal()),
self.cd_eth_tx.clk.eq(ClockSignal()),
self.cd_eth_tx.rst.eq(ResetSignal()),
]
def gen_simulation(self, selfp):
selfp.cd_eth_rx.rst = 1
selfp.cd_eth_tx.rst = 1
yield
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))
if __name__ == "__main__":
run_simulation(TB(), vcd_name="my.vcd")

View file

@ -0,0 +1,82 @@
from migen.fhdl.std import *
from misoclib.ethmac.common import *
from misoclib.ethmac.preamble import *
from misoclib.ethmac.test.common import *
preamble = [
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5
]
payload = [
0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12,
0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00,
0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11,
0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00,
0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C,
0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
]
mux = {
"inserter": 0,
"checker": 1,
"both": 2
}
class TB(Module):
def __init__(self, random_level=50):
sm = self.submodules
sm.streamer = PacketStreamer(eth_description(8))
sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level)
sm.logger = PacketLogger(eth_description(8))
sm.logger_randomizer = AckRandomizer(eth_description(8), random_level)
self.comb += [
self.streamer.source.connect(self.streamer_randomizer.sink),
self.logger_randomizer.source.connect(self.logger.sink)
]
sm.preamble_inserter = PreambleInserter(8)
sm.preamble_checker = PreambleChecker(8)
self.mux = Signal(2)
self.comb += [
If(self.mux == mux["inserter"],
self.streamer_randomizer.source.connect(self.preamble_inserter.sink),
self.preamble_inserter.source.connect(self.logger_randomizer.sink)
).Elif(self.mux == mux["checker"],
self.streamer_randomizer.source.connect(self.preamble_checker.sink),
self.preamble_checker.source.connect(self.logger_randomizer.sink)
).Elif(self.mux == mux["both"],
self.streamer_randomizer.source.connect(self.preamble_inserter.sink),
self.preamble_inserter.source.connect(self.preamble_checker.sink),
self.preamble_checker.source.connect(self.logger_randomizer.sink)
)
]
def gen_simulation(self, selfp):
selfp.mux = mux["inserter"]
print("streamer --> preamble_inserter --> logger:")
self.streamer.send(Packet(payload))
yield from self.logger.receive()
s, l, e = check(preamble+payload, self.logger.packet)
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
selfp.mux = mux["checker"]
print("streamer --> preamble_checker --> logger:")
self.streamer.send(Packet(preamble+payload))
yield from self.logger.receive()
s, l, e = check(payload, self.logger.packet)
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
selfp.mux = mux["both"]
print("streamer --> preamble_inserter --> preamble_checker --> logger:")
self.streamer.send(Packet(payload))
yield from self.logger.receive()
s, l, e = check(payload, self.logger.packet)
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
if __name__ == "__main__":
from migen.sim.generic import run_simulation
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd")