-Unless otherwise noted, LiteScope is copyright (C) 2015 Florent Kermarrec.
+Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- __ _ __ ______ __ __
- / / (_) /____ / __/ /_/ / ___ _______ ___ / /_
- / /__/ / __/ -_) _// __/ _ \/ -_) __/ _ \/ -_) __/
- /____/_/\__/\__/___/\__/_//_/\__/_/ /_//_/\__/\__/
+ __ _ __ ______ __
+ / / (_) /____ / __/ /_/ /
+ / /__/ / __/ -_) _// __/ _ \
+ /____/_/\__/\__/___/\__/_//_/
Copyright 2012-2015 / EnjoyDigital
florent@enjoy-digital.fr
[> Intro
---------
-LiteEthernet provides a small footprint and configurable Ethernet core.
+LiteEth provides a small footprint and configurable Ethernet core.
-LiteEthernet is part of LiteX libraries whose aims are to lower entry level of
+LiteEth is part of LiteX libraries whose aims are to lower entry level of
complex FPGA IP cores by providing simple, elegant and efficient implementations
ofcomponents used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller...
Since Python is used to describe the HDL, the core is highly and easily
configurable.
-LiteEthernet uses technologies developed in partnership with M-Labs Ltd:
+LiteEth uses technologies developed in partnership with M-Labs Ltd:
- Migen enables generating HDL with Python in an efficient way.
- MiSoC provides the basic blocks to build a powerful and small footprint SoC.
-LiteEthernet can be used as a Migen/MiSoC library (by simply installing it
+LiteEth can be used as a Migen/MiSoC library (by simply installing it
with the provided setup.py) or can be integrated with your standard design flow
by generating the verilog rtl that you will use as a standard core.
[> License
-----------
-LiteEthernet is released under the very permissive two-clause BSD license. Under
-the terms of this license, you are authorized to use LiteEthernet for closed-source
+LiteEth is released under the very permissive two-clause BSD license. Under
+the terms of this license, you are authorized to use LiteEth for closed-source
proprietary designs.
Even though we do not require you to do so, those things are awesome, so please
do them if possible:
- - tell us that you are using LiteEthernet
- - cite LiteEthernet in publications related to research it has helped
+ - tell us that you are using LiteEth
+ - cite LiteEth in publications related to research it has helped
- send us feedback and suggestions for improvements
- send us bug reports when something goes wrong
- - send us the modifications and improvements you have done to LiteEthernet.
+ - send us the modifications and improvements you have done to LiteEth.
[> Support and Consulting
--------------------------
We love open-source hardware and like sharing our designs with others.
-LiteEthernet is developed and maintained by EnjoyDigital.
+LiteEth is developed and maintained by EnjoyDigital.
-If you would like to know more about LiteEthernet or if you are already a happy
+If you would like to know more about LiteEth or if you are already a happy
user and would like to extend it for your needs, EnjoyDigital can provide standard
commercial support as well as consulting services.
--- /dev/null
+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)
--- /dev/null
+# This file is Copyright (c) 2014 Robert Jordens <jordens@gmail.com>
+# License: BSD
+
+import struct
+from collections import namedtuple
+
+from migen.fhdl.std import *
+from migen.bus import wishbone
+from migen.genlib.record import Record
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.fifo import SyncFIFO
+from migen.genlib.misc import optree
+from migen.actorlib.structuring import Cast, Pack, Unpack, pack_layout
+from migen.actorlib.sim import SimActor
+from migen.bus.transactions import TRead, TWrite
+from migen.flow.transactions import Token
+from migen.flow.actor import Source, Sink
+from migen.flow.network import DataFlowGraph, CompositeActor
+
+
+_eb_width = 32 # addr and data
+_eb_queue_len = 32
+_eb_magic = 0x4e6f
+_eb_ver = 1
+_eb_hdr = [
+ ("magic", 16),
+ ("ver", 4),
+ ("res1", 1),
+ ("no_response", 1),
+ ("probe_res", 1),
+ ("probe", 1),
+ ("addr_size", 4),
+ ("data_size", 4),
+ ][::-1] # big-endian
+
+_eb_rec_hdr = [
+ ("bca_cfg", 1),
+ ("rca_cfg", 1),
+ ("rd_fifo", 1),
+ ("res1", 1),
+ ("drop_cyc", 1),
+ ("wca_cfg", 1),
+ ("wr_fifo", 1),
+ ("res2", 1),
+ ("sel", 8),
+ ("wr_cnt", 8),
+ ("rd_cnt", 8),
+ ][::-1] # big-endian
+
+_eb_layout = [("data", _eb_width)]
+
+
+class Config(Module):
+ def __init__(self, sdb_addr):
+ self.errreg = Signal(8*8)
+ mach = Signal(4*8, reset=0xd15e)
+ macl = Signal(4*8, reset=0xa5edbeef)
+ self.mac = Signal(6*8)
+ self.comb += self.mac.eq(Cat(macl, mach))
+ self.ip = Signal(4*8, reset=0xc0a80064)
+ self.port = Signal(4*8, reset=0xebd0)
+
+ self.bus = bus = wishbone.Interface()
+ self.submodules.fifo = SyncFIFO(3, _eb_queue_len)
+
+ read_mux = Array([self.errreg[32:], self.errreg[:32], 0,
+ sdb_addr, mach, macl, self.ip, self.port])
+ write_mux = Array([mach, macl, self.ip, self.port])[bus.adr - 4]
+
+ self.dout = read_mux[self.fifo.dout]
+ self.comb += [
+ bus.ack.eq(bus.cyc & bus.stb),
+ bus.dat_r.eq(read_mux[bus.adr[:3]]),
+ ]
+ self.sync += [
+ If(bus.cyc & bus.stb & bus.we & optree("|",
+ [bus.adr[:3] == i for i in (4, 5, 6, 7)]),
+ write_mux.eq(bus.dat_w),
+ )]
+
+class WishboneMaster(Module):
+ def __init__(self, timeout):
+ self.bus = bus = wishbone.Interface()
+ self.submodules.fifo = SyncFIFO(_eb_width + 1, _eb_queue_len)
+
+ self.active = Signal()
+ inflight = Signal(max=_eb_queue_len)
+ queued = Signal(max=_eb_queue_len)
+ self.sync += [
+ inflight.eq(inflight + self.active - self.fifo.we),
+ queued.eq(queued + self.active - self.fifo.re),
+ ]
+
+ self.busy = Signal()
+ self.full = Signal()
+ self.comb += [
+ self.busy.eq(inflight != 0),
+ self.full.eq(queued == _eb_queue_len - 1),
+ ]
+
+ kill_ack = Signal()
+ time = Signal(max=timeout)
+ self.comb += kill_ack.eq(time == timeout - 1)
+ self.sync += [
+ If(self.fifo.we | ~self.busy,
+ time.eq(0),
+ ).Else(
+ time.eq(time + 1),
+ )]
+
+ self.comb += [
+ self.fifo.we.eq(bus.ack | bus.err | kill_ack),
+ self.fifo.din.eq(Cat(bus.dat_r, ~bus.ack)),
+ ]
+
+ self.errreg = Signal(64)
+ self.sync += [
+ If(self.fifo.re,
+ self.errreg.eq(Cat(self.fifo.dout[-1], self.errreg)),
+ )]
+
+class Transmit(Module):
+ def __init__(self, pas, cfg, wbm, tag, tags):
+ self.tx = Source(_eb_layout)
+
+ data = Signal(_eb_width)
+ re = Signal(4)
+ self.tx_cyc = Signal()
+ self.tx_skip = Signal()
+ last_tx_cyc = Signal()
+ last_tx_skip = Signal()
+ readable = Signal()
+
+ self.sync += [
+ last_tx_cyc.eq(self.tx_cyc),
+ last_tx_skip.eq(self.tx_skip),
+ ]
+
+ self.comb += [
+ readable.eq(Cat(tag.readable, pas.readable,
+ cfg.fifo.readable, wbm.readable) & re == re),
+ self.tx.stb.eq(readable & (re[1:] != 0)),
+ self.tx.payload.data.eq(data),
+ Case(tag.dout, {
+ tags["PASS_TX"]: [re.eq(0b0011), data.eq(pas.dout)],
+ tags["PASS_ON"]: [re.eq(0b0011), data.eq(pas.dout)],
+ tags["CFG_REQ"]: [re.eq(0b0101), data.eq(cfg.dout)],
+ tags["CFG_IGN"]: [re.eq(0b0111), data.eq(pas.dout)],
+ tags["WBM_REQ"]: [re.eq(0b1001), data.eq(wbm.dout)],
+ tags["WBM_IGN"]: [re.eq(0b1011), data.eq(pas.dout)],
+ "default": [re.eq(0b0001)],
+ }),
+ If(readable & (self.tx.ack | (re[1:] == 0)),
+ Cat(tag.re, pas.re, cfg.fifo.re, wbm.re).eq(re),
+ ),
+ If(tag.readable,
+ If(tag.dout == tags["PASS_TX"],
+ self.tx_cyc.eq(1),
+ self.tx_skip.eq(0),
+ ).Elif(tag.dout == tags["SKIP_TX"],
+ self.tx_cyc.eq(0),
+ self.tx_skip.eq(1),
+ ).Elif(tag.dout == tags["DROP_TX"],
+ self.tx_cyc.eq(0),
+ self.tx_skip.eq(0),
+ ).Else(
+ self.tx_cyc.eq(last_tx_cyc),
+ self.tx_skip.eq(last_tx_skip),
+ ),
+ ).Else(
+ self.tx_cyc.eq(last_tx_cyc),
+ self.tx_skip.eq(last_tx_skip),
+ ),
+ ]
+
+class Receive(Module):
+ def __init__(self, pas, cfg, wbm, tag, tags):
+ self.rx = Sink(_eb_layout)
+
+ rx_rec_hdr = Record(_eb_rec_hdr)
+ tx_rec_hdr = Record(_eb_rec_hdr)
+ rx_eb_hdr = Record(_eb_hdr)
+ tx_eb_hdr = Record(_eb_hdr)
+ self.comb += [
+ rx_eb_hdr.raw_bits().eq(self.rx.payload.data),
+ tx_eb_hdr.magic.eq(rx_eb_hdr.magic),
+ tx_eb_hdr.ver.eq(_eb_ver),
+ tx_eb_hdr.no_response.eq(1),
+ tx_eb_hdr.addr_size.eq(4),
+ tx_eb_hdr.data_size.eq(4),
+ tx_eb_hdr.probe_res.eq(rx_eb_hdr.probe),
+
+ rx_rec_hdr.raw_bits().eq(self.rx.payload.data),
+ tx_rec_hdr.wca_cfg.eq(rx_rec_hdr.bca_cfg),
+ tx_rec_hdr.wr_fifo.eq(rx_rec_hdr.rd_fifo),
+ tx_rec_hdr.wr_cnt.eq(rx_rec_hdr.rd_cnt),
+ tx_rec_hdr.sel.eq(rx_rec_hdr.sel),
+ tx_rec_hdr.drop_cyc.eq(rx_rec_hdr.drop_cyc),
+ ]
+
+ do_rx = Signal()
+ self.rx_cyc = Signal()
+ self.comb += [
+ wbm.bus.sel.eq(rx_rec_hdr.sel),
+ do_rx.eq(tag.writable & # tag is always written/read
+ self.rx_cyc & self.rx.stb & # have data
+ (wbm.fifo.we | ~wbm.bus.stb) & # stb finished or idle
+ (wbm.bus.cyc | ~wbm.busy)), # in-cycle or idle
+ self.rx.ack.eq(do_rx),
+ cfg.fifo.din.eq(wbm.bus.adr),
+ # no eb-cfg write support yet
+ #cfg.dat_w.eq(wbm.bus.dat_w),
+ #cfg.we.eq(wbm.bus.we),
+ cfg.errreg.eq(wbm.errreg),
+ ]
+
+ cur_rx_rec_hdr = Record(_eb_rec_hdr)
+ cur_tx_rec_hdr = Record(_eb_rec_hdr)
+ do_rec = Signal()
+ do_adr = Signal()
+ do_write = Signal()
+ do_read = Signal()
+ wr_adr = Signal(flen(wbm.bus.adr))
+ old_rx_cyc = Signal()
+ self.sync += [
+ wbm.bus.stb.eq(wbm.bus.stb & ~wbm.fifo.we),
+ wbm.bus.cyc.eq(wbm.bus.cyc & (
+ ~cur_rx_rec_hdr.drop_cyc |
+ (cur_rx_rec_hdr.wr_cnt > 0) |
+ (cur_rx_rec_hdr.rd_cnt > 0))),
+ If(do_rec,
+ cur_rx_rec_hdr.eq(rx_rec_hdr),
+ cur_tx_rec_hdr.eq(tx_rec_hdr),
+ ),
+ If(do_adr,
+ wr_adr.eq(self.rx.payload.data[2:]),
+ ),
+ If(do_write,
+ If(cur_rx_rec_hdr.wca_cfg,
+ cfg.fifo.we.eq(1),
+ ).Else(
+ wbm.bus.cyc.eq(1),
+ wbm.bus.stb.eq(1),
+ ),
+ wbm.bus.we.eq(1),
+ wbm.bus.adr.eq(wr_adr),
+ wbm.bus.dat_w.eq(self.rx.payload.data),
+ If(~cur_rx_rec_hdr.wr_fifo,
+ wr_adr.eq(wr_adr + 1),
+ ),
+ cur_rx_rec_hdr.wr_cnt.eq(cur_rx_rec_hdr.wr_cnt - 1),
+ ),
+ If(do_read,
+ If(cur_rx_rec_hdr.rca_cfg,
+ cfg.fifo.we.eq(1),
+ ).Else(
+ wbm.bus.cyc.eq(1),
+ wbm.bus.stb.eq(1),
+ ),
+ wbm.bus.we.eq(0),
+ wbm.bus.adr.eq(self.rx.payload.data[2:]),
+ cur_rx_rec_hdr.rd_cnt.eq(cur_rx_rec_hdr.rd_cnt - 1),
+ ),
+ If(~self.rx_cyc,
+ wbm.bus.cyc.eq(0),
+ ),
+ old_rx_cyc.eq(self.rx_cyc),
+ ]
+
+ fsm = self.submodules.fsm = FSM()
+ fsm.reset_state = "EB_HDR"
+ fsm.act("EB_HDR",
+ If(do_rx,
+ tag.we.eq(1),
+ If((rx_eb_hdr.magic != _eb_magic) |
+ (rx_eb_hdr.ver !=_eb_ver),
+ tag.din.eq(tags["SKIP_TX"]),
+ NextState("DROP"),
+ ).Else(
+ If(rx_eb_hdr.no_response,
+ tag.din.eq(tags["SKIP_TX"]),
+ ).Else(
+ tag.din.eq(tags["PASS_TX"]),
+ pas.we.eq(1),
+ pas.din.eq(tx_eb_hdr.raw_bits()),
+ ),
+ If(rx_eb_hdr.probe,
+ If(rx_eb_hdr.addr_size[2] &
+ rx_eb_hdr.data_size[2],
+ NextState("PROBE_ID"),
+ ).Else(
+ NextState("PROBE_DROP"),
+ ),
+ ).Else(
+ If((rx_eb_hdr.addr_size == 4) &
+ (rx_eb_hdr.data_size == 4),
+ NextState("CYC_HDR"),
+ ).Else(
+ NextState("DROP"),
+ ),
+ ),
+ ),
+ ))
+ fsm.act("PROBE_DROP",
+ If(do_rx,
+ tag.we.eq(1),
+ tag.din.eq(tags["PASS_ON"]),
+ pas.we.eq(1),
+ pas.din.eq(self.rx.payload.data),
+ NextState("DROP"),
+ ))
+ fsm.act("PROBE_ID",
+ If(do_rx,
+ tag.we.eq(1),
+ tag.din.eq(tags["PASS_ON"]),
+ pas.we.eq(1),
+ pas.din.eq(self.rx.payload.data),
+ NextState("CYC_HDR"),
+ ))
+ fsm.act("CYC_HDR",
+ If(do_rx,
+ do_rec.eq(1),
+ tag.we.eq(1),
+ tag.din.eq(tags["PASS_ON"]),
+ pas.we.eq(1),
+ If(rx_rec_hdr.wr_cnt != 0,
+ NextState("WR_ADR"),
+ ).Else(
+ pas.din.eq(tx_rec_hdr.raw_bits()),
+ If(rx_rec_hdr.rd_cnt != 0,
+ NextState("RD_ADR"),
+ ).Else(
+ NextState("CYC_HDR"),
+ ),
+ ),
+ ))
+ fsm.act("WR_ADR",
+ If(do_rx,
+ do_adr.eq(1),
+ tag.we.eq(1),
+ tag.din.eq(tags["PASS_ON"]),
+ pas.we.eq(1),
+ NextState("WRITE"),
+ ))
+ fsm.act("WRITE",
+ If(do_rx,
+ do_write.eq(1),
+ tag.we.eq(1),
+ If(cur_rx_rec_hdr.wca_cfg,
+ tag.din.eq(tags["CFG_IGN"]),
+ ).Else(
+ wbm.active.eq(1),
+ tag.din.eq(tags["WBM_IGN"]),
+ ),
+ pas.we.eq(1),
+ If(cur_rx_rec_hdr.wr_cnt == 1,
+ pas.din.eq(cur_tx_rec_hdr.raw_bits()),
+ If(cur_rx_rec_hdr.rd_cnt != 0,
+ NextState("RD_ADR"),
+ ).Else(
+ NextState("CYC_HDR"),
+ ),
+ ),
+ ))
+ fsm.act("RD_ADR",
+ If(do_rx,
+ tag.we.eq(1),
+ tag.din.eq(tags["PASS_ON"]),
+ pas.we.eq(1),
+ pas.din.eq(self.rx.payload.data),
+ NextState("READ"),
+ ))
+ fsm.act("READ",
+ If(do_rx,
+ do_read.eq(1),
+ tag.we.eq(1),
+ If(cur_rx_rec_hdr.rca_cfg,
+ tag.din.eq(tags["CFG_REQ"]),
+ ).Else(
+ wbm.active.eq(1),
+ tag.din.eq(tags["WBM_REQ"]),
+ ),
+ If(cur_rx_rec_hdr.rd_cnt == 1,
+ NextState("CYC_HDR"),
+ ),
+ ))
+ fsm.act("DROP",
+ #If(do_rx,
+ # tag.we.eq(1),
+ # tag.din.eq(tags["PASS_ON"]),
+ # pas.we.eq(1),
+ #)
+ )
+ for state in fsm.actions:
+ fsm.act(state, If(~self.rx_cyc, NextState("EB_HDR")))
+ self.comb += [
+ If(~self.rx_cyc,
+ Cat(do_rec, do_adr, do_write, do_read).eq(0),
+ Cat(wbm.active, pas.we).eq(0),
+ If(old_rx_cyc,
+ tag.we.eq(1),
+ tag.din.eq(tags["DROP_TX"]),
+ ),
+ )]
+
+class Slave(Module):
+ def __init__(self, sdb_addr, timeout):
+ tags = dict((k, i) for i, k in enumerate(
+ "DROP_TX SKIP_TX PASS_TX PASS_ON CFG_REQ "
+ "CFG_IGN WBM_REQ WBM_IGN".split()))
+ tag_width = flen(Signal(max=len(tags)))
+
+ self.submodules.pas = SyncFIFO(_eb_width, _eb_queue_len)
+ self.submodules.cfg = Config(sdb_addr)
+ self.submodules.wbm = WishboneMaster(timeout)
+ self.submodules.tag = SyncFIFO(tag_width, _eb_queue_len)
+
+ self.submodules.rxfsm = Receive(self.pas, self.cfg,
+ self.wbm, self.tag, tags)
+ self.rx = self.rxfsm.rx
+ self.rx_cyc = self.rxfsm.rx_cyc
+ self.submodules.txmux = Transmit(self.pas, self.cfg,
+ self.wbm.fifo, self.tag, tags)
+ self.tx = self.txmux.tx
+ self.tx_skip = self.txmux.tx_skip
+ self.tx_cyc = self.txmux.tx_cyc
+ self.busy = self.wbm.busy
+
+class Converter(Module):
+ def __init__(self, raw_width, graph, **slave_kwargs):
+ raw_layout = [("data", raw_width)]
+ pack_factor = _eb_width//raw_width
+
+ self.rx = Sink(raw_layout)
+ rx_pack = Pack(raw_layout, pack_factor)
+ rx_cast = Cast(pack_layout(raw_layout, pack_factor), _eb_layout)
+ self.submodules.slave = Slave(**slave_kwargs)
+ tx_cast = Cast(_eb_layout, pack_layout(raw_layout, pack_factor))
+ tx_unpack = Unpack(pack_factor, raw_layout)
+ self.tx = Source(raw_layout)
+
+ graph.add_connection(self.rx, rx_pack)
+ graph.add_connection(rx_pack, rx_cast)
+ graph.add_connection(rx_cast, self.slave.rx)
+ graph.add_connection(self.slave.tx, tx_cast)
+ graph.add_connection(tx_cast, tx_unpack)
+ graph.add_connection(tx_unpack, self.tx)
+
+class SimTx(SimActor):
+ def __init__(self, data):
+ self.tx = Source(_eb_layout)
+ SimActor.__init__(self, self.gen(data))
+
+ def gen(self, data):
+ for i in data:
+ yield Token("tx", {"data": i})
+ print("eb tx", hex(i))
+
+class SimRx(SimActor):
+ def __init__(self):
+ self.rx = Sink(_eb_layout)
+ self.recv = []
+ SimActor.__init__(self, self.gen())
+
+ def gen(self):
+ while True:
+ t = Token("rx")
+ yield t
+ print("eb rx", hex(t.value["data"]))
+ self.recv.append(t.value["data"])
+
+class TB(Module):
+ def __init__(self, data):
+ ebm_tx = SimTx(data)
+ ebm_rx = SimRx()
+ self.slave = Slave(0x200, 10)
+ g = DataFlowGraph()
+ g.add_connection(ebm_tx, self.slave)
+ g.add_connection(self.slave, ebm_rx)
+ self.submodules.graph = CompositeActor(g)
+ self.submodules.cfg_master = wishbone.Initiator(self.gen_cfg_reads())
+ self.submodules.cfg_tap = wishbone.Tap(self.slave.cfg.bus,
+ lambda l: print("cfg", l))
+ self.submodules.wbm_tap = wishbone.Tap(self.slave.wbm.bus,
+ lambda l: print("wbm", l))
+ self.submodules.xbar = wishbone.Crossbar(
+ [self.cfg_master.bus, self.slave.wbm.bus],
+ [
+ (lambda a: a[6:] == 0x0, wishbone.Target(
+ wishbone.TargetModel()).bus),
+ (lambda a: a[6:] == 0x1, self.slave.cfg.bus),
+ ])
+
+ def gen_cfg_reads(self):
+ for a in range(0x40, 0x40+4):
+ t = TRead(a)
+ yield t
+
+ def do_simulation(self, s):
+ #s.interrupt = self.cfg_master.done
+ s.wr(self.slave.rx_cyc, int(s.cycle_counter < 200))
+
+class MyStruct(object):
+ _data = None
+ _fmt = "!"
+
+ def __init__(self, **kwargs):
+ self.data = self._data(**kwargs)
+
+ def __bytes__(self):
+ return struct.pack(self._fmt, *self.data)
+
+class EbHeader(MyStruct):
+ _data = namedtuple("eb_hdr", "magic ver size")
+ _fmt = "!HBB"
+
+ def __init__(self, probe_id=None, addr_size=4, data_size=4, records=[]):
+ no_response = not any(r.read for r in records)
+ probe = probe_id is not None
+ probe_res = False
+ MyStruct.__init__(self, magic=_eb_magic, ver=(_eb_ver<<4) |
+ (no_response<<2) | (probe_res<<1) | (probe<<0),
+ size=(addr_size<<4) | (data_size<<0))
+ self.probe = struct.pack("!I", probe_id) if probe else b""
+ self.records = records
+
+ def __bytes__(self):
+ return (MyStruct.__bytes__(self) + self.probe +
+ b"".join(map(bytes, self.records)))
+
+class EbRecord(MyStruct):
+ _data = namedtuple("eb_rec", "flags sel wr_cnt rd_cnt")
+ _fmt = "!BBBB"
+
+ def __init__(self, sel=0xf, wr_adr=0, rd_adr=0, write=[], read=[],
+ bca_cfg=False, rca_cfg=False, rd_fifo=False, drop_cyc=False,
+ wca_cfg=False, wr_fifo=False):
+ MyStruct.__init__(self, sel=sel, wr_cnt=len(write),
+ rd_cnt=len(read), flags=(bca_cfg<<7) | (rca_cfg<<6) |
+ (rd_fifo<<5) | (drop_cyc<<3) | (wca_cfg<<2) |
+ (wr_fifo>>1))
+ self.wr_adr = wr_adr
+ self.write = write
+ self.rd_adr = rd_adr
+ self.read = read
+
+ def __bytes__(self):
+ b = MyStruct.__bytes__(self)
+ if self.write:
+ b += struct.pack("!I" + "I"*len(self.write), self.wr_adr,
+ *self.write)
+ if self.read:
+ b += struct.pack("!I" + "I"*len(self.read), self.rd_adr,
+ *self.read)
+ return b
+
+def main():
+ from migen.sim.generic import Simulator, TopLevel
+
+ #from migen.fhdl import verilog
+ #s = Slave(0, 10)
+ #print(verilog.convert(s, ios={s.rx.payload.data, s.tx.payload.data,
+ # s.rx.stb, s.rx.ack, s.tx.stb, s.tx.ack}))
+
+ eb_pkt = EbHeader(records=[
+ EbRecord(wr_adr=0x10, write=[0x20, 0x21],
+ rd_adr=0x30, read=range(0, 8, 4)),
+ EbRecord(rd_adr=0x40, read=range(0x100, 0x100+32, 4),
+ drop_cyc=True),
+ EbRecord(rca_cfg=True, bca_cfg=True, rd_adr=0x50,
+ read=range(0, 0+8, 4), drop_cyc=True),
+ ])
+ eb_pkt = bytes(eb_pkt)
+ eb_pkt = struct.unpack("!" + "I"*(len(eb_pkt)//4), eb_pkt)
+ tb = TB(eb_pkt)
+ sim = Simulator(tb, TopLevel("etherbone.vcd"))
+ sim.run(500)
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
--- /dev/null
+from liteethernet.common import *
+from liteethernet.mac import LiteEthernetMAC
+
+class LiteEthernetMAC(Module, AutoCSR):
+ def __init__(self, phy, frontend="wishbone", with_hw_preamble_crc=True, endianness="be"):
+ self.submodules.core = LiteEthernetMAC(phy, with_hw_preamble, endianness)
+
+ 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")
--- /dev/null
+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)
--- /dev/null
+
+from liteethernet.common import *
+from liteethernet.mac.common import *
+from liteethernet.mac.preamble import PreambleInserter, PreambleChecker
+from liteethernet.mac.crc import CRC32Inserter, CRC32Checker
+from liteethernet.mac.last_be import TXLastBE, RXLastBE
+
+class LiteEthernetMACCore(Module, AutoCSR):
+ def __init__(self, phy, 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)
+
+ self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.record import *
+from migen.genlib.misc import optree, chooser
+from migen.genlib.crc import *
+from migen.flow.actor import Sink, Source
+from migen.actorlib.fifo import SyncFIFO
+
+from collections import OrderedDict
+
+class CRCEngine(Module):
+ """Cyclic Redundancy Check Engine
+
+ Compute next CRC value from last CRC value and data input using
+ an optimized asynchronous LFSR.
+
+ Parameters
+ ----------
+ dat_width : int
+ Width of the data bus.
+ width : int
+ Width of the CRC.
+ polynom : int
+ Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC)
+
+ Attributes
+ ----------
+ d : in
+ Data input.
+ last : in
+ last CRC value.
+ next :
+ next CRC value.
+ """
+ def __init__(self, dat_width, width, polynom):
+ self.d = Signal(dat_width)
+ self.last = Signal(width)
+ self.next = Signal(width)
+
+ ###
+
+ def _optimize_eq(l):
+ """
+ Replace even numbers of XORs in the equation
+ with an equivalent XOR
+ """
+ d = OrderedDict()
+ for e in l:
+ if e in d:
+ d[e] += 1
+ else:
+ d[e] = 1
+ r = []
+ for key, value in d.items():
+ if value%2 != 0:
+ r.append(key)
+ return r
+
+ # compute and optimize CRC's LFSR
+ curval = [[("state", i)] for i in range(width)]
+ for i in range(dat_width):
+ feedback = curval.pop() + [("din", i)]
+ for j in range(width-1):
+ if (polynom & (1<<(j+1))):
+ curval[j] += feedback
+ curval[j] = _optimize_eq(curval[j])
+ curval.insert(0, feedback)
+
+ # implement logic
+ for i in range(width):
+ xors = []
+ for t, n in curval[i]:
+ if t == "state":
+ xors += [self.last[n]]
+ elif t == "din":
+ xors += [self.d[n]]
+ self.comb += self.next[i].eq(optree("^", xors))
+
+@DecorateModule(InsertReset)
+@DecorateModule(InsertCE)
+class CRC32(Module):
+ """IEEE 802.3 CRC
+
+ Implement an IEEE 802.3 CRC generator/checker.
+
+ Parameters
+ ----------
+ dat_width : int
+ Width of the data bus.
+
+ Attributes
+ ----------
+ d : in
+ Data input.
+ value : out
+ CRC value (used for generator).
+ error : out
+ CRC error (used for checker).
+ """
+ width = 32
+ polynom = 0x04C11DB7
+ init = 2**width-1
+ check = 0xC704DD7B
+ def __init__(self, dat_width):
+ self.d = Signal(dat_width)
+ self.value = Signal(self.width)
+ self.error = Signal()
+
+ ###
+
+ self.submodules.engine = CRCEngine(dat_width, self.width, self.polynom)
+ reg = Signal(self.width, reset=self.init)
+ self.sync += reg.eq(self.engine.next)
+ self.comb += [
+ self.engine.d.eq(self.d),
+ self.engine.last.eq(reg),
+
+ self.value.eq(~reg[::-1]),
+ self.error.eq(self.engine.next != self.check)
+ ]
+
+class CRCInserter(Module):
+ """CRC Inserter
+
+ Append a CRC at the end of each packet.
+
+ Parameters
+ ----------
+ layout : layout
+ Layout of the dataflow.
+
+ Attributes
+ ----------
+ sink : in
+ Packets input without CRC.
+ source : out
+ Packets output with CRC.
+ """
+ def __init__(self, crc_class, layout):
+ self.sink = sink = Sink(layout)
+ self.source = source = Source(layout)
+ self.busy = Signal()
+
+ ###
+
+ dw = flen(sink.d)
+ crc = crc_class(dw)
+ fsm = FSM(reset_state="IDLE")
+ self.submodules += crc, fsm
+
+ fsm.act("IDLE",
+ crc.reset.eq(1),
+ sink.ack.eq(1),
+ If(sink.stb & sink.sop,
+ sink.ack.eq(0),
+ NextState("COPY"),
+ )
+ )
+ fsm.act("COPY",
+ crc.ce.eq(sink.stb & source.ack),
+ crc.d.eq(sink.d),
+ Record.connect(sink, source),
+ source.eop.eq(0),
+ If(sink.stb & sink.eop & source.ack,
+ NextState("INSERT"),
+ )
+ )
+ ratio = crc.width//dw
+ if ratio > 1:
+ cnt = Signal(max=ratio, reset=ratio-1)
+ cnt_done = Signal()
+ fsm.act("INSERT",
+ source.stb.eq(1),
+ chooser(crc.value, cnt, source.d, reverse=True),
+ If(cnt_done,
+ source.eop.eq(1),
+ If(source.ack, NextState("IDLE"))
+ )
+ )
+ self.comb += cnt_done.eq(cnt == 0)
+ self.sync += \
+ If(fsm.ongoing("IDLE"),
+ cnt.eq(cnt.reset)
+ ).Elif(fsm.ongoing("INSERT") & ~cnt_done,
+ cnt.eq(cnt - source.ack)
+ )
+ else:
+ fsm.act("INSERT",
+ source.stb.eq(1),
+ source.eop.eq(1),
+ source.d.eq(crc.value),
+ If(source.ack, NextState("IDLE"))
+ )
+ self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
+
+class CRC32Inserter(CRCInserter):
+ def __init__(self, layout):
+ CRCInserter.__init__(self, CRC32, layout)
+
+class CRCChecker(Module):
+ """CRC Checker
+
+ Check CRC at the end of each packet.
+
+ Parameters
+ ----------
+ layout : layout
+ Layout of the dataflow.
+
+ Attributes
+ ----------
+ sink : in
+ Packets input with CRC.
+ source : out
+ Packets output without CRC and "error" set to 0
+ on eop when CRC OK / set to 1 when CRC KO.
+ """
+ def __init__(self, crc_class, layout):
+ self.sink = sink = Sink(layout)
+ self.source = source = Source(layout)
+ self.busy = Signal()
+
+ ###
+
+ dw = flen(sink.d)
+ crc = crc_class(dw)
+ self.submodules += crc
+ ratio = crc.width//dw
+
+ error = Signal()
+ fifo = InsertReset(SyncFIFO(layout, ratio + 1))
+ self.submodules += fifo
+
+ fsm = FSM(reset_state="RESET")
+ self.submodules += fsm
+
+ fifo_in = Signal()
+ fifo_out = Signal()
+ fifo_full = Signal()
+
+ self.comb += [
+ fifo_full.eq(fifo.fifo.level == ratio),
+ fifo_in.eq(sink.stb & (~fifo_full | fifo_out)),
+ fifo_out.eq(source.stb & source.ack),
+
+ Record.connect(sink, fifo.sink),
+ fifo.sink.stb.eq(fifo_in),
+ self.sink.ack.eq(fifo_in),
+
+ source.stb.eq(sink.stb & fifo_full),
+ source.sop.eq(fifo.source.sop),
+ source.eop.eq(sink.eop),
+ fifo.source.ack.eq(fifo_out),
+ source.payload.eq(fifo.source.payload),
+
+ source.error.eq(sink.error | crc.error),
+ ]
+
+ fsm.act("RESET",
+ crc.reset.eq(1),
+ fifo.reset.eq(1),
+ NextState("IDLE"),
+ )
+ fsm.act("IDLE",
+ crc.d.eq(sink.d),
+ If(sink.stb & sink.sop & sink.ack,
+ crc.ce.eq(1),
+ NextState("COPY")
+ )
+ )
+ fsm.act("COPY",
+ crc.d.eq(sink.d),
+ If(sink.stb & sink.ack,
+ crc.ce.eq(1),
+ If(sink.eop,
+ NextState("RESET")
+ )
+ )
+ )
+ self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
+
+class CRC32Checker(CRCChecker):
+ def __init__(self, layout):
+ CRCChecker.__init__(self, CRC32, layout)
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.record import *
+from migen.flow.actor import Sink, Source
+
+from liteethernet.common import *
+from liteethernet.mac.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)
+ ]
--- /dev/null
+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 liteethernet.common import *
+from liteethernet.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"),
+ )
+ )
--- /dev/null
+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 liteethernet.common import *
+from liteethernet.mac.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)
--- /dev/null
+from liteethernet.common import *
+from liteethernet.mac import LiteEthernetMAC
+
+class LiteEthernetMACWishboneInterface(Module, AutoCSR):
+ def __init__(self, nrxslots=2, ntxslots=2):
+ self.sink = Sink(mac_description(dw))
+ self.source = Source(max_description(dw))
+ self.bus = wishbone.Interface()
+
+ ###
+
+ # SRAM Storage
+ 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)
+ self.comb += [
+ Record.connect(self.sink, self.sram_writer.sink),
+ Record.connect(self.sram_reader.source, self.source)
+ ]
+
+ # 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
--- /dev/null
+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
--- /dev/null
+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
+
--- /dev/null
+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")
--- /dev/null
+[*]
+[*] 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
--- /dev/null
+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")
--- /dev/null
+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")
--- /dev/null
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+from liteethernet.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
--- /dev/null
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.record import *
+
+from liteethernet.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)
--- /dev/null
+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 liteethernet.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
+++ /dev/null
-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)
+++ /dev/null
-# This file is Copyright (c) 2014 Robert Jordens <jordens@gmail.com>
-# License: BSD
-
-import struct
-from collections import namedtuple
-
-from migen.fhdl.std import *
-from migen.bus import wishbone
-from migen.genlib.record import Record
-from migen.genlib.fsm import FSM, NextState
-from migen.genlib.fifo import SyncFIFO
-from migen.genlib.misc import optree
-from migen.actorlib.structuring import Cast, Pack, Unpack, pack_layout
-from migen.actorlib.sim import SimActor
-from migen.bus.transactions import TRead, TWrite
-from migen.flow.transactions import Token
-from migen.flow.actor import Source, Sink
-from migen.flow.network import DataFlowGraph, CompositeActor
-
-
-_eb_width = 32 # addr and data
-_eb_queue_len = 32
-_eb_magic = 0x4e6f
-_eb_ver = 1
-_eb_hdr = [
- ("magic", 16),
- ("ver", 4),
- ("res1", 1),
- ("no_response", 1),
- ("probe_res", 1),
- ("probe", 1),
- ("addr_size", 4),
- ("data_size", 4),
- ][::-1] # big-endian
-
-_eb_rec_hdr = [
- ("bca_cfg", 1),
- ("rca_cfg", 1),
- ("rd_fifo", 1),
- ("res1", 1),
- ("drop_cyc", 1),
- ("wca_cfg", 1),
- ("wr_fifo", 1),
- ("res2", 1),
- ("sel", 8),
- ("wr_cnt", 8),
- ("rd_cnt", 8),
- ][::-1] # big-endian
-
-_eb_layout = [("data", _eb_width)]
-
-
-class Config(Module):
- def __init__(self, sdb_addr):
- self.errreg = Signal(8*8)
- mach = Signal(4*8, reset=0xd15e)
- macl = Signal(4*8, reset=0xa5edbeef)
- self.mac = Signal(6*8)
- self.comb += self.mac.eq(Cat(macl, mach))
- self.ip = Signal(4*8, reset=0xc0a80064)
- self.port = Signal(4*8, reset=0xebd0)
-
- self.bus = bus = wishbone.Interface()
- self.submodules.fifo = SyncFIFO(3, _eb_queue_len)
-
- read_mux = Array([self.errreg[32:], self.errreg[:32], 0,
- sdb_addr, mach, macl, self.ip, self.port])
- write_mux = Array([mach, macl, self.ip, self.port])[bus.adr - 4]
-
- self.dout = read_mux[self.fifo.dout]
- self.comb += [
- bus.ack.eq(bus.cyc & bus.stb),
- bus.dat_r.eq(read_mux[bus.adr[:3]]),
- ]
- self.sync += [
- If(bus.cyc & bus.stb & bus.we & optree("|",
- [bus.adr[:3] == i for i in (4, 5, 6, 7)]),
- write_mux.eq(bus.dat_w),
- )]
-
-class WishboneMaster(Module):
- def __init__(self, timeout):
- self.bus = bus = wishbone.Interface()
- self.submodules.fifo = SyncFIFO(_eb_width + 1, _eb_queue_len)
-
- self.active = Signal()
- inflight = Signal(max=_eb_queue_len)
- queued = Signal(max=_eb_queue_len)
- self.sync += [
- inflight.eq(inflight + self.active - self.fifo.we),
- queued.eq(queued + self.active - self.fifo.re),
- ]
-
- self.busy = Signal()
- self.full = Signal()
- self.comb += [
- self.busy.eq(inflight != 0),
- self.full.eq(queued == _eb_queue_len - 1),
- ]
-
- kill_ack = Signal()
- time = Signal(max=timeout)
- self.comb += kill_ack.eq(time == timeout - 1)
- self.sync += [
- If(self.fifo.we | ~self.busy,
- time.eq(0),
- ).Else(
- time.eq(time + 1),
- )]
-
- self.comb += [
- self.fifo.we.eq(bus.ack | bus.err | kill_ack),
- self.fifo.din.eq(Cat(bus.dat_r, ~bus.ack)),
- ]
-
- self.errreg = Signal(64)
- self.sync += [
- If(self.fifo.re,
- self.errreg.eq(Cat(self.fifo.dout[-1], self.errreg)),
- )]
-
-class Transmit(Module):
- def __init__(self, pas, cfg, wbm, tag, tags):
- self.tx = Source(_eb_layout)
-
- data = Signal(_eb_width)
- re = Signal(4)
- self.tx_cyc = Signal()
- self.tx_skip = Signal()
- last_tx_cyc = Signal()
- last_tx_skip = Signal()
- readable = Signal()
-
- self.sync += [
- last_tx_cyc.eq(self.tx_cyc),
- last_tx_skip.eq(self.tx_skip),
- ]
-
- self.comb += [
- readable.eq(Cat(tag.readable, pas.readable,
- cfg.fifo.readable, wbm.readable) & re == re),
- self.tx.stb.eq(readable & (re[1:] != 0)),
- self.tx.payload.data.eq(data),
- Case(tag.dout, {
- tags["PASS_TX"]: [re.eq(0b0011), data.eq(pas.dout)],
- tags["PASS_ON"]: [re.eq(0b0011), data.eq(pas.dout)],
- tags["CFG_REQ"]: [re.eq(0b0101), data.eq(cfg.dout)],
- tags["CFG_IGN"]: [re.eq(0b0111), data.eq(pas.dout)],
- tags["WBM_REQ"]: [re.eq(0b1001), data.eq(wbm.dout)],
- tags["WBM_IGN"]: [re.eq(0b1011), data.eq(pas.dout)],
- "default": [re.eq(0b0001)],
- }),
- If(readable & (self.tx.ack | (re[1:] == 0)),
- Cat(tag.re, pas.re, cfg.fifo.re, wbm.re).eq(re),
- ),
- If(tag.readable,
- If(tag.dout == tags["PASS_TX"],
- self.tx_cyc.eq(1),
- self.tx_skip.eq(0),
- ).Elif(tag.dout == tags["SKIP_TX"],
- self.tx_cyc.eq(0),
- self.tx_skip.eq(1),
- ).Elif(tag.dout == tags["DROP_TX"],
- self.tx_cyc.eq(0),
- self.tx_skip.eq(0),
- ).Else(
- self.tx_cyc.eq(last_tx_cyc),
- self.tx_skip.eq(last_tx_skip),
- ),
- ).Else(
- self.tx_cyc.eq(last_tx_cyc),
- self.tx_skip.eq(last_tx_skip),
- ),
- ]
-
-class Receive(Module):
- def __init__(self, pas, cfg, wbm, tag, tags):
- self.rx = Sink(_eb_layout)
-
- rx_rec_hdr = Record(_eb_rec_hdr)
- tx_rec_hdr = Record(_eb_rec_hdr)
- rx_eb_hdr = Record(_eb_hdr)
- tx_eb_hdr = Record(_eb_hdr)
- self.comb += [
- rx_eb_hdr.raw_bits().eq(self.rx.payload.data),
- tx_eb_hdr.magic.eq(rx_eb_hdr.magic),
- tx_eb_hdr.ver.eq(_eb_ver),
- tx_eb_hdr.no_response.eq(1),
- tx_eb_hdr.addr_size.eq(4),
- tx_eb_hdr.data_size.eq(4),
- tx_eb_hdr.probe_res.eq(rx_eb_hdr.probe),
-
- rx_rec_hdr.raw_bits().eq(self.rx.payload.data),
- tx_rec_hdr.wca_cfg.eq(rx_rec_hdr.bca_cfg),
- tx_rec_hdr.wr_fifo.eq(rx_rec_hdr.rd_fifo),
- tx_rec_hdr.wr_cnt.eq(rx_rec_hdr.rd_cnt),
- tx_rec_hdr.sel.eq(rx_rec_hdr.sel),
- tx_rec_hdr.drop_cyc.eq(rx_rec_hdr.drop_cyc),
- ]
-
- do_rx = Signal()
- self.rx_cyc = Signal()
- self.comb += [
- wbm.bus.sel.eq(rx_rec_hdr.sel),
- do_rx.eq(tag.writable & # tag is always written/read
- self.rx_cyc & self.rx.stb & # have data
- (wbm.fifo.we | ~wbm.bus.stb) & # stb finished or idle
- (wbm.bus.cyc | ~wbm.busy)), # in-cycle or idle
- self.rx.ack.eq(do_rx),
- cfg.fifo.din.eq(wbm.bus.adr),
- # no eb-cfg write support yet
- #cfg.dat_w.eq(wbm.bus.dat_w),
- #cfg.we.eq(wbm.bus.we),
- cfg.errreg.eq(wbm.errreg),
- ]
-
- cur_rx_rec_hdr = Record(_eb_rec_hdr)
- cur_tx_rec_hdr = Record(_eb_rec_hdr)
- do_rec = Signal()
- do_adr = Signal()
- do_write = Signal()
- do_read = Signal()
- wr_adr = Signal(flen(wbm.bus.adr))
- old_rx_cyc = Signal()
- self.sync += [
- wbm.bus.stb.eq(wbm.bus.stb & ~wbm.fifo.we),
- wbm.bus.cyc.eq(wbm.bus.cyc & (
- ~cur_rx_rec_hdr.drop_cyc |
- (cur_rx_rec_hdr.wr_cnt > 0) |
- (cur_rx_rec_hdr.rd_cnt > 0))),
- If(do_rec,
- cur_rx_rec_hdr.eq(rx_rec_hdr),
- cur_tx_rec_hdr.eq(tx_rec_hdr),
- ),
- If(do_adr,
- wr_adr.eq(self.rx.payload.data[2:]),
- ),
- If(do_write,
- If(cur_rx_rec_hdr.wca_cfg,
- cfg.fifo.we.eq(1),
- ).Else(
- wbm.bus.cyc.eq(1),
- wbm.bus.stb.eq(1),
- ),
- wbm.bus.we.eq(1),
- wbm.bus.adr.eq(wr_adr),
- wbm.bus.dat_w.eq(self.rx.payload.data),
- If(~cur_rx_rec_hdr.wr_fifo,
- wr_adr.eq(wr_adr + 1),
- ),
- cur_rx_rec_hdr.wr_cnt.eq(cur_rx_rec_hdr.wr_cnt - 1),
- ),
- If(do_read,
- If(cur_rx_rec_hdr.rca_cfg,
- cfg.fifo.we.eq(1),
- ).Else(
- wbm.bus.cyc.eq(1),
- wbm.bus.stb.eq(1),
- ),
- wbm.bus.we.eq(0),
- wbm.bus.adr.eq(self.rx.payload.data[2:]),
- cur_rx_rec_hdr.rd_cnt.eq(cur_rx_rec_hdr.rd_cnt - 1),
- ),
- If(~self.rx_cyc,
- wbm.bus.cyc.eq(0),
- ),
- old_rx_cyc.eq(self.rx_cyc),
- ]
-
- fsm = self.submodules.fsm = FSM()
- fsm.reset_state = "EB_HDR"
- fsm.act("EB_HDR",
- If(do_rx,
- tag.we.eq(1),
- If((rx_eb_hdr.magic != _eb_magic) |
- (rx_eb_hdr.ver !=_eb_ver),
- tag.din.eq(tags["SKIP_TX"]),
- NextState("DROP"),
- ).Else(
- If(rx_eb_hdr.no_response,
- tag.din.eq(tags["SKIP_TX"]),
- ).Else(
- tag.din.eq(tags["PASS_TX"]),
- pas.we.eq(1),
- pas.din.eq(tx_eb_hdr.raw_bits()),
- ),
- If(rx_eb_hdr.probe,
- If(rx_eb_hdr.addr_size[2] &
- rx_eb_hdr.data_size[2],
- NextState("PROBE_ID"),
- ).Else(
- NextState("PROBE_DROP"),
- ),
- ).Else(
- If((rx_eb_hdr.addr_size == 4) &
- (rx_eb_hdr.data_size == 4),
- NextState("CYC_HDR"),
- ).Else(
- NextState("DROP"),
- ),
- ),
- ),
- ))
- fsm.act("PROBE_DROP",
- If(do_rx,
- tag.we.eq(1),
- tag.din.eq(tags["PASS_ON"]),
- pas.we.eq(1),
- pas.din.eq(self.rx.payload.data),
- NextState("DROP"),
- ))
- fsm.act("PROBE_ID",
- If(do_rx,
- tag.we.eq(1),
- tag.din.eq(tags["PASS_ON"]),
- pas.we.eq(1),
- pas.din.eq(self.rx.payload.data),
- NextState("CYC_HDR"),
- ))
- fsm.act("CYC_HDR",
- If(do_rx,
- do_rec.eq(1),
- tag.we.eq(1),
- tag.din.eq(tags["PASS_ON"]),
- pas.we.eq(1),
- If(rx_rec_hdr.wr_cnt != 0,
- NextState("WR_ADR"),
- ).Else(
- pas.din.eq(tx_rec_hdr.raw_bits()),
- If(rx_rec_hdr.rd_cnt != 0,
- NextState("RD_ADR"),
- ).Else(
- NextState("CYC_HDR"),
- ),
- ),
- ))
- fsm.act("WR_ADR",
- If(do_rx,
- do_adr.eq(1),
- tag.we.eq(1),
- tag.din.eq(tags["PASS_ON"]),
- pas.we.eq(1),
- NextState("WRITE"),
- ))
- fsm.act("WRITE",
- If(do_rx,
- do_write.eq(1),
- tag.we.eq(1),
- If(cur_rx_rec_hdr.wca_cfg,
- tag.din.eq(tags["CFG_IGN"]),
- ).Else(
- wbm.active.eq(1),
- tag.din.eq(tags["WBM_IGN"]),
- ),
- pas.we.eq(1),
- If(cur_rx_rec_hdr.wr_cnt == 1,
- pas.din.eq(cur_tx_rec_hdr.raw_bits()),
- If(cur_rx_rec_hdr.rd_cnt != 0,
- NextState("RD_ADR"),
- ).Else(
- NextState("CYC_HDR"),
- ),
- ),
- ))
- fsm.act("RD_ADR",
- If(do_rx,
- tag.we.eq(1),
- tag.din.eq(tags["PASS_ON"]),
- pas.we.eq(1),
- pas.din.eq(self.rx.payload.data),
- NextState("READ"),
- ))
- fsm.act("READ",
- If(do_rx,
- do_read.eq(1),
- tag.we.eq(1),
- If(cur_rx_rec_hdr.rca_cfg,
- tag.din.eq(tags["CFG_REQ"]),
- ).Else(
- wbm.active.eq(1),
- tag.din.eq(tags["WBM_REQ"]),
- ),
- If(cur_rx_rec_hdr.rd_cnt == 1,
- NextState("CYC_HDR"),
- ),
- ))
- fsm.act("DROP",
- #If(do_rx,
- # tag.we.eq(1),
- # tag.din.eq(tags["PASS_ON"]),
- # pas.we.eq(1),
- #)
- )
- for state in fsm.actions:
- fsm.act(state, If(~self.rx_cyc, NextState("EB_HDR")))
- self.comb += [
- If(~self.rx_cyc,
- Cat(do_rec, do_adr, do_write, do_read).eq(0),
- Cat(wbm.active, pas.we).eq(0),
- If(old_rx_cyc,
- tag.we.eq(1),
- tag.din.eq(tags["DROP_TX"]),
- ),
- )]
-
-class Slave(Module):
- def __init__(self, sdb_addr, timeout):
- tags = dict((k, i) for i, k in enumerate(
- "DROP_TX SKIP_TX PASS_TX PASS_ON CFG_REQ "
- "CFG_IGN WBM_REQ WBM_IGN".split()))
- tag_width = flen(Signal(max=len(tags)))
-
- self.submodules.pas = SyncFIFO(_eb_width, _eb_queue_len)
- self.submodules.cfg = Config(sdb_addr)
- self.submodules.wbm = WishboneMaster(timeout)
- self.submodules.tag = SyncFIFO(tag_width, _eb_queue_len)
-
- self.submodules.rxfsm = Receive(self.pas, self.cfg,
- self.wbm, self.tag, tags)
- self.rx = self.rxfsm.rx
- self.rx_cyc = self.rxfsm.rx_cyc
- self.submodules.txmux = Transmit(self.pas, self.cfg,
- self.wbm.fifo, self.tag, tags)
- self.tx = self.txmux.tx
- self.tx_skip = self.txmux.tx_skip
- self.tx_cyc = self.txmux.tx_cyc
- self.busy = self.wbm.busy
-
-class Converter(Module):
- def __init__(self, raw_width, graph, **slave_kwargs):
- raw_layout = [("data", raw_width)]
- pack_factor = _eb_width//raw_width
-
- self.rx = Sink(raw_layout)
- rx_pack = Pack(raw_layout, pack_factor)
- rx_cast = Cast(pack_layout(raw_layout, pack_factor), _eb_layout)
- self.submodules.slave = Slave(**slave_kwargs)
- tx_cast = Cast(_eb_layout, pack_layout(raw_layout, pack_factor))
- tx_unpack = Unpack(pack_factor, raw_layout)
- self.tx = Source(raw_layout)
-
- graph.add_connection(self.rx, rx_pack)
- graph.add_connection(rx_pack, rx_cast)
- graph.add_connection(rx_cast, self.slave.rx)
- graph.add_connection(self.slave.tx, tx_cast)
- graph.add_connection(tx_cast, tx_unpack)
- graph.add_connection(tx_unpack, self.tx)
-
-class SimTx(SimActor):
- def __init__(self, data):
- self.tx = Source(_eb_layout)
- SimActor.__init__(self, self.gen(data))
-
- def gen(self, data):
- for i in data:
- yield Token("tx", {"data": i})
- print("eb tx", hex(i))
-
-class SimRx(SimActor):
- def __init__(self):
- self.rx = Sink(_eb_layout)
- self.recv = []
- SimActor.__init__(self, self.gen())
-
- def gen(self):
- while True:
- t = Token("rx")
- yield t
- print("eb rx", hex(t.value["data"]))
- self.recv.append(t.value["data"])
-
-class TB(Module):
- def __init__(self, data):
- ebm_tx = SimTx(data)
- ebm_rx = SimRx()
- self.slave = Slave(0x200, 10)
- g = DataFlowGraph()
- g.add_connection(ebm_tx, self.slave)
- g.add_connection(self.slave, ebm_rx)
- self.submodules.graph = CompositeActor(g)
- self.submodules.cfg_master = wishbone.Initiator(self.gen_cfg_reads())
- self.submodules.cfg_tap = wishbone.Tap(self.slave.cfg.bus,
- lambda l: print("cfg", l))
- self.submodules.wbm_tap = wishbone.Tap(self.slave.wbm.bus,
- lambda l: print("wbm", l))
- self.submodules.xbar = wishbone.Crossbar(
- [self.cfg_master.bus, self.slave.wbm.bus],
- [
- (lambda a: a[6:] == 0x0, wishbone.Target(
- wishbone.TargetModel()).bus),
- (lambda a: a[6:] == 0x1, self.slave.cfg.bus),
- ])
-
- def gen_cfg_reads(self):
- for a in range(0x40, 0x40+4):
- t = TRead(a)
- yield t
-
- def do_simulation(self, s):
- #s.interrupt = self.cfg_master.done
- s.wr(self.slave.rx_cyc, int(s.cycle_counter < 200))
-
-class MyStruct(object):
- _data = None
- _fmt = "!"
-
- def __init__(self, **kwargs):
- self.data = self._data(**kwargs)
-
- def __bytes__(self):
- return struct.pack(self._fmt, *self.data)
-
-class EbHeader(MyStruct):
- _data = namedtuple("eb_hdr", "magic ver size")
- _fmt = "!HBB"
-
- def __init__(self, probe_id=None, addr_size=4, data_size=4, records=[]):
- no_response = not any(r.read for r in records)
- probe = probe_id is not None
- probe_res = False
- MyStruct.__init__(self, magic=_eb_magic, ver=(_eb_ver<<4) |
- (no_response<<2) | (probe_res<<1) | (probe<<0),
- size=(addr_size<<4) | (data_size<<0))
- self.probe = struct.pack("!I", probe_id) if probe else b""
- self.records = records
-
- def __bytes__(self):
- return (MyStruct.__bytes__(self) + self.probe +
- b"".join(map(bytes, self.records)))
-
-class EbRecord(MyStruct):
- _data = namedtuple("eb_rec", "flags sel wr_cnt rd_cnt")
- _fmt = "!BBBB"
-
- def __init__(self, sel=0xf, wr_adr=0, rd_adr=0, write=[], read=[],
- bca_cfg=False, rca_cfg=False, rd_fifo=False, drop_cyc=False,
- wca_cfg=False, wr_fifo=False):
- MyStruct.__init__(self, sel=sel, wr_cnt=len(write),
- rd_cnt=len(read), flags=(bca_cfg<<7) | (rca_cfg<<6) |
- (rd_fifo<<5) | (drop_cyc<<3) | (wca_cfg<<2) |
- (wr_fifo>>1))
- self.wr_adr = wr_adr
- self.write = write
- self.rd_adr = rd_adr
- self.read = read
-
- def __bytes__(self):
- b = MyStruct.__bytes__(self)
- if self.write:
- b += struct.pack("!I" + "I"*len(self.write), self.wr_adr,
- *self.write)
- if self.read:
- b += struct.pack("!I" + "I"*len(self.read), self.rd_adr,
- *self.read)
- return b
-
-def main():
- from migen.sim.generic import Simulator, TopLevel
-
- #from migen.fhdl import verilog
- #s = Slave(0, 10)
- #print(verilog.convert(s, ios={s.rx.payload.data, s.tx.payload.data,
- # s.rx.stb, s.rx.ack, s.tx.stb, s.tx.ack}))
-
- eb_pkt = EbHeader(records=[
- EbRecord(wr_adr=0x10, write=[0x20, 0x21],
- rd_adr=0x30, read=range(0, 8, 4)),
- EbRecord(rd_adr=0x40, read=range(0x100, 0x100+32, 4),
- drop_cyc=True),
- EbRecord(rca_cfg=True, bca_cfg=True, rd_adr=0x50,
- read=range(0, 0+8, 4), drop_cyc=True),
- ])
- eb_pkt = bytes(eb_pkt)
- eb_pkt = struct.unpack("!" + "I"*(len(eb_pkt)//4), eb_pkt)
- tb = TB(eb_pkt)
- sim = Simulator(tb, TopLevel("etherbone.vcd"))
- sim.run(500)
-
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
+++ /dev/null
-from liteethernet.common import *
-from liteethernet.mac import LiteEthernetMAC
-
-class LiteEthernetMAC(Module, AutoCSR):
- def __init__(self, phy, frontend="wishbone", with_hw_preamble_crc=True, endianness="be"):
- self.submodules.core = LiteEthernetMAC(phy, with_hw_preamble, endianness)
-
- 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")
+++ /dev/null
-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)
+++ /dev/null
-
-from liteethernet.common import *
-from liteethernet.mac.common import *
-from liteethernet.mac.preamble import PreambleInserter, PreambleChecker
-from liteethernet.mac.crc import CRC32Inserter, CRC32Checker
-from liteethernet.mac.last_be import TXLastBE, RXLastBE
-
-class LiteEthernetMACCore(Module, AutoCSR):
- def __init__(self, phy, 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)
-
- self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source
+++ /dev/null
-from migen.fhdl.std import *
-from migen.genlib.fsm import FSM, NextState
-from migen.genlib.record import *
-from migen.genlib.misc import optree, chooser
-from migen.genlib.crc import *
-from migen.flow.actor import Sink, Source
-from migen.actorlib.fifo import SyncFIFO
-
-from collections import OrderedDict
-
-class CRCEngine(Module):
- """Cyclic Redundancy Check Engine
-
- Compute next CRC value from last CRC value and data input using
- an optimized asynchronous LFSR.
-
- Parameters
- ----------
- dat_width : int
- Width of the data bus.
- width : int
- Width of the CRC.
- polynom : int
- Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC)
-
- Attributes
- ----------
- d : in
- Data input.
- last : in
- last CRC value.
- next :
- next CRC value.
- """
- def __init__(self, dat_width, width, polynom):
- self.d = Signal(dat_width)
- self.last = Signal(width)
- self.next = Signal(width)
-
- ###
-
- def _optimize_eq(l):
- """
- Replace even numbers of XORs in the equation
- with an equivalent XOR
- """
- d = OrderedDict()
- for e in l:
- if e in d:
- d[e] += 1
- else:
- d[e] = 1
- r = []
- for key, value in d.items():
- if value%2 != 0:
- r.append(key)
- return r
-
- # compute and optimize CRC's LFSR
- curval = [[("state", i)] for i in range(width)]
- for i in range(dat_width):
- feedback = curval.pop() + [("din", i)]
- for j in range(width-1):
- if (polynom & (1<<(j+1))):
- curval[j] += feedback
- curval[j] = _optimize_eq(curval[j])
- curval.insert(0, feedback)
-
- # implement logic
- for i in range(width):
- xors = []
- for t, n in curval[i]:
- if t == "state":
- xors += [self.last[n]]
- elif t == "din":
- xors += [self.d[n]]
- self.comb += self.next[i].eq(optree("^", xors))
-
-@DecorateModule(InsertReset)
-@DecorateModule(InsertCE)
-class CRC32(Module):
- """IEEE 802.3 CRC
-
- Implement an IEEE 802.3 CRC generator/checker.
-
- Parameters
- ----------
- dat_width : int
- Width of the data bus.
-
- Attributes
- ----------
- d : in
- Data input.
- value : out
- CRC value (used for generator).
- error : out
- CRC error (used for checker).
- """
- width = 32
- polynom = 0x04C11DB7
- init = 2**width-1
- check = 0xC704DD7B
- def __init__(self, dat_width):
- self.d = Signal(dat_width)
- self.value = Signal(self.width)
- self.error = Signal()
-
- ###
-
- self.submodules.engine = CRCEngine(dat_width, self.width, self.polynom)
- reg = Signal(self.width, reset=self.init)
- self.sync += reg.eq(self.engine.next)
- self.comb += [
- self.engine.d.eq(self.d),
- self.engine.last.eq(reg),
-
- self.value.eq(~reg[::-1]),
- self.error.eq(self.engine.next != self.check)
- ]
-
-class CRCInserter(Module):
- """CRC Inserter
-
- Append a CRC at the end of each packet.
-
- Parameters
- ----------
- layout : layout
- Layout of the dataflow.
-
- Attributes
- ----------
- sink : in
- Packets input without CRC.
- source : out
- Packets output with CRC.
- """
- def __init__(self, crc_class, layout):
- self.sink = sink = Sink(layout)
- self.source = source = Source(layout)
- self.busy = Signal()
-
- ###
-
- dw = flen(sink.d)
- crc = crc_class(dw)
- fsm = FSM(reset_state="IDLE")
- self.submodules += crc, fsm
-
- fsm.act("IDLE",
- crc.reset.eq(1),
- sink.ack.eq(1),
- If(sink.stb & sink.sop,
- sink.ack.eq(0),
- NextState("COPY"),
- )
- )
- fsm.act("COPY",
- crc.ce.eq(sink.stb & source.ack),
- crc.d.eq(sink.d),
- Record.connect(sink, source),
- source.eop.eq(0),
- If(sink.stb & sink.eop & source.ack,
- NextState("INSERT"),
- )
- )
- ratio = crc.width//dw
- if ratio > 1:
- cnt = Signal(max=ratio, reset=ratio-1)
- cnt_done = Signal()
- fsm.act("INSERT",
- source.stb.eq(1),
- chooser(crc.value, cnt, source.d, reverse=True),
- If(cnt_done,
- source.eop.eq(1),
- If(source.ack, NextState("IDLE"))
- )
- )
- self.comb += cnt_done.eq(cnt == 0)
- self.sync += \
- If(fsm.ongoing("IDLE"),
- cnt.eq(cnt.reset)
- ).Elif(fsm.ongoing("INSERT") & ~cnt_done,
- cnt.eq(cnt - source.ack)
- )
- else:
- fsm.act("INSERT",
- source.stb.eq(1),
- source.eop.eq(1),
- source.d.eq(crc.value),
- If(source.ack, NextState("IDLE"))
- )
- self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
-
-class CRC32Inserter(CRCInserter):
- def __init__(self, layout):
- CRCInserter.__init__(self, CRC32, layout)
-
-class CRCChecker(Module):
- """CRC Checker
-
- Check CRC at the end of each packet.
-
- Parameters
- ----------
- layout : layout
- Layout of the dataflow.
-
- Attributes
- ----------
- sink : in
- Packets input with CRC.
- source : out
- Packets output without CRC and "error" set to 0
- on eop when CRC OK / set to 1 when CRC KO.
- """
- def __init__(self, crc_class, layout):
- self.sink = sink = Sink(layout)
- self.source = source = Source(layout)
- self.busy = Signal()
-
- ###
-
- dw = flen(sink.d)
- crc = crc_class(dw)
- self.submodules += crc
- ratio = crc.width//dw
-
- error = Signal()
- fifo = InsertReset(SyncFIFO(layout, ratio + 1))
- self.submodules += fifo
-
- fsm = FSM(reset_state="RESET")
- self.submodules += fsm
-
- fifo_in = Signal()
- fifo_out = Signal()
- fifo_full = Signal()
-
- self.comb += [
- fifo_full.eq(fifo.fifo.level == ratio),
- fifo_in.eq(sink.stb & (~fifo_full | fifo_out)),
- fifo_out.eq(source.stb & source.ack),
-
- Record.connect(sink, fifo.sink),
- fifo.sink.stb.eq(fifo_in),
- self.sink.ack.eq(fifo_in),
-
- source.stb.eq(sink.stb & fifo_full),
- source.sop.eq(fifo.source.sop),
- source.eop.eq(sink.eop),
- fifo.source.ack.eq(fifo_out),
- source.payload.eq(fifo.source.payload),
-
- source.error.eq(sink.error | crc.error),
- ]
-
- fsm.act("RESET",
- crc.reset.eq(1),
- fifo.reset.eq(1),
- NextState("IDLE"),
- )
- fsm.act("IDLE",
- crc.d.eq(sink.d),
- If(sink.stb & sink.sop & sink.ack,
- crc.ce.eq(1),
- NextState("COPY")
- )
- )
- fsm.act("COPY",
- crc.d.eq(sink.d),
- If(sink.stb & sink.ack,
- crc.ce.eq(1),
- If(sink.eop,
- NextState("RESET")
- )
- )
- )
- self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
-
-class CRC32Checker(CRCChecker):
- def __init__(self, layout):
- CRCChecker.__init__(self, CRC32, layout)
+++ /dev/null
-from migen.fhdl.std import *
-from migen.genlib.record import *
-from migen.flow.actor import Sink, Source
-
-from liteethernet.common import *
-from liteethernet.mac.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)
- ]
+++ /dev/null
-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 liteethernet.common import *
-from liteethernet.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"),
- )
- )
+++ /dev/null
-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 liteethernet.common import *
-from liteethernet.mac.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)
+++ /dev/null
-from liteethernet.common import *
-from liteethernet.mac import LiteEthernetMAC
-
-class LiteEthernetMACWishboneInterface(Module, AutoCSR):
- def __init__(self, nrxslots=2, ntxslots=2):
- self.sink = Sink(mac_description(dw))
- self.source = Source(max_description(dw))
- self.bus = wishbone.Interface()
-
- ###
-
- # SRAM Storage
- 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)
- self.comb += [
- Record.connect(self.sink, self.sram_writer.sink),
- Record.connect(self.sram_reader.source, self.source)
- ]
-
- # 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
+++ /dev/null
-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
+++ /dev/null
-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
-
+++ /dev/null
-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")
+++ /dev/null
-[*]
-[*] 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
+++ /dev/null
-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")
+++ /dev/null
-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")
+++ /dev/null
-from migen.fhdl.std import *
-from migen.flow.actor import Sink, Source
-from migen.bank.description import *
-from migen.genlib.resetsync import AsyncResetSynchronizer
-
-from liteethernet.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
+++ /dev/null
-from migen.fhdl.std import *
-from migen.flow.actor import Sink, Source
-from migen.bank.description import *
-from migen.genlib.record import *
-
-from liteethernet.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)
+++ /dev/null
-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 liteethernet.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
required_version = (3, 3)
if sys.version_info < required_version:
- raise SystemExit("LiteEthernet requires python {0} or greater".format(
+ raise SystemExit("LiteEth requires python {0} or greater".format(
".".join(map(str, required_version))))
setup(
name="liteethernet",
version="unknown",
- description="small footprint and configurable embedded FPGA logic analyzer",
+ description="small footprint and configurable Ethernet core",
long_description=README,
author="Florent Kermarrec",
author_email="florent@enjoy-digital.fr",
url="http://enjoy-digital.fr",
- download_url="https://github.com/enjoy-digital/liteethernet",
+ download_url="https://github.com/enjoy-digital/liteeth",
packages=find_packages(here),
license="GPL",
platforms=["Any"],