From: Florent Kermarrec Date: Tue, 27 Jan 2015 23:36:09 +0000 (+0100) Subject: change name to LiteEth (LiteEthernet is too long...) X-Git-Tag: 24jan2021_ls180~2604^2~125 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4cd73fc9dab2659465ef4c8dea801e843c3a3bdf;p=litex.git change name to LiteEth (LiteEthernet is too long...) --- diff --git a/LICENSE b/LICENSE index 95ac4a6a..cbbfe8be 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -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: diff --git a/README b/README index 194275ce..b11ff35e 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ - __ _ __ ______ __ __ - / / (_) /____ / __/ /_/ / ___ _______ ___ / /_ - / /__/ / __/ -_) _// __/ _ \/ -_) __/ _ \/ -_) __/ - /____/_/\__/\__/___/\__/_//_/\__/_/ /_//_/\__/\__/ + __ _ __ ______ __ + / / (_) /____ / __/ /_/ / + / /__/ / __/ -_) _// __/ _ \ + /____/_/\__/\__/___/\__/_//_/ Copyright 2012-2015 / EnjoyDigital florent@enjoy-digital.fr @@ -11,9 +11,9 @@ [> 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... @@ -23,11 +23,11 @@ adapters to use standardized AXI or Avalon-ST streaming buses. 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. @@ -62,24 +62,24 @@ devel [AT] lists.m-labs.hk. [> 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. diff --git a/liteeth/__init__.py b/liteeth/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteeth/arp/__init__.py b/liteeth/arp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteeth/common.py b/liteeth/common.py new file mode 100644 index 00000000..177943ee --- /dev/null +++ b/liteeth/common.py @@ -0,0 +1,14 @@ +from migen.fhdl.std import * +from migen.flow.actor import EndpointDescription + +eth_mtu = 1532 +eth_preamble = 0xD555555555555555 +buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) + +def eth_description(dw): + layout = [ + ("d", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(layout, packetized=True) diff --git a/liteeth/etherbone/__init__.py b/liteeth/etherbone/__init__.py new file mode 100644 index 00000000..f84348a1 --- /dev/null +++ b/liteeth/etherbone/__init__.py @@ -0,0 +1,581 @@ +# This file is Copyright (c) 2014 Robert Jordens +# 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 diff --git a/liteeth/ip/__init__.py b/liteeth/ip/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteeth/mac/__init__.py b/liteeth/mac/__init__.py new file mode 100644 index 00000000..e9b2ccfa --- /dev/null +++ b/liteeth/mac/__init__.py @@ -0,0 +1,54 @@ +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") diff --git a/liteeth/mac/common.py b/liteeth/mac/common.py new file mode 100644 index 00000000..177943ee --- /dev/null +++ b/liteeth/mac/common.py @@ -0,0 +1,14 @@ +from migen.fhdl.std import * +from migen.flow.actor import EndpointDescription + +eth_mtu = 1532 +eth_preamble = 0xD555555555555555 +buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) + +def eth_description(dw): + layout = [ + ("d", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(layout, packetized=True) diff --git a/liteeth/mac/core/__init__.py b/liteeth/mac/core/__init__.py new file mode 100644 index 00000000..a361d5dc --- /dev/null +++ b/liteeth/mac/core/__init__.py @@ -0,0 +1,54 @@ + +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 diff --git a/liteeth/mac/core/crc.py b/liteeth/mac/core/crc.py new file mode 100644 index 00000000..8a058fae --- /dev/null +++ b/liteeth/mac/core/crc.py @@ -0,0 +1,284 @@ +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) diff --git a/liteeth/mac/core/last_be.py b/liteeth/mac/core/last_be.py new file mode 100644 index 00000000..555e523f --- /dev/null +++ b/liteeth/mac/core/last_be.py @@ -0,0 +1,40 @@ +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) + ] diff --git a/liteeth/mac/core/preamble.py b/liteeth/mac/core/preamble.py new file mode 100644 index 00000000..f1c76034 --- /dev/null +++ b/liteeth/mac/core/preamble.py @@ -0,0 +1,146 @@ +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"), + ) + ) diff --git a/liteeth/mac/frontend/__init__.py b/liteeth/mac/frontend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteeth/mac/frontend/sram.py b/liteeth/mac/frontend/sram.py new file mode 100644 index 00000000..c51c0ecb --- /dev/null +++ b/liteeth/mac/frontend/sram.py @@ -0,0 +1,252 @@ +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) diff --git a/liteeth/mac/frontend/wishbone.py b/liteeth/mac/frontend/wishbone.py new file mode 100644 index 00000000..56601e12 --- /dev/null +++ b/liteeth/mac/frontend/wishbone.py @@ -0,0 +1,39 @@ +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 diff --git a/liteeth/mac/test/Makefile b/liteeth/mac/test/Makefile new file mode 100644 index 00000000..55d28e10 --- /dev/null +++ b/liteeth/mac/test/Makefile @@ -0,0 +1,13 @@ +MSCDIR = ../../../ +PYTHON = python3 + +CMD = PYTHONPATH=$(MSCDIR) $(PYTHON) + +crc_tb: + $(CMD) crc_tb.py + +preamble_tb: + $(CMD) preamble_tb.py + +ethmac_tb: + $(CMD) ethmac_tb.py diff --git a/liteeth/mac/test/__init__.py b/liteeth/mac/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteeth/mac/test/common.py b/liteeth/mac/test/common.py new file mode 100644 index 00000000..13123700 --- /dev/null +++ b/liteeth/mac/test/common.py @@ -0,0 +1,120 @@ +import random, copy + +from migen.fhdl.std import * +from migen.flow.actor import Sink, Source +from migen.genlib.record import * + +from misoclib.ethmac.common import * + +def seed_to_data(seed, random=True): + if random: + return (seed * 0x31415979 + 1) & 0xffffffff + else: + return seed + +def check(p1, p2): + p1 = copy.deepcopy(p1) + p2 = copy.deepcopy(p2) + if isinstance(p1, int): + return 0, 1, int(p1 != p2) + else: + if len(p1) >= len(p2): + ref, res = p1, p2 + else: + ref, res = p2, p1 + shift = 0 + while((ref[0] != res[0]) and (len(res)>1)): + res.pop(0) + shift += 1 + length = min(len(ref), len(res)) + errors = 0 + for i in range(length): + if ref.pop(0) != res.pop(0): + errors += 1 + return shift, length, errors + +def randn(max_n): + return random.randint(0, max_n-1) + +class Packet(list): + def __init__(self, init=[]): + self.ongoing = False + self.done = False + for data in init: + self.append(data) + +class PacketStreamer(Module): + def __init__(self, description): + self.source = Source(description) + ### + self.packets = [] + self.packet = Packet() + self.packet.done = 1 + + def send(self, packet): + packet = copy.deepcopy(packet) + self.packets.append(packet) + + def do_simulation(self, selfp): + if len(self.packets) and self.packet.done: + self.packet = self.packets.pop(0) + if not self.packet.ongoing and not self.packet.done: + selfp.source.stb = 1 + selfp.source.sop = 1 + selfp.source.d = self.packet.pop(0) + self.packet.ongoing = True + elif selfp.source.stb == 1 and selfp.source.ack == 1: + selfp.source.sop = 0 + selfp.source.eop = (len(self.packet) == 1) + if len(self.packet) > 0: + selfp.source.stb = 1 + selfp.source.d = self.packet.pop(0) + else: + self.packet.done = 1 + selfp.source.stb = 0 + +class PacketLogger(Module): + def __init__(self, description): + self.sink = Sink(description) + ### + self.packet = Packet() + + def receive(self): + self.packet.done = 0 + while self.packet.done == 0: + yield + + def do_simulation(self, selfp): + selfp.sink.ack = 1 + if selfp.sink.stb == 1 and selfp.sink.sop == 1: + self.packet = Packet() + self.packet.append(selfp.sink.d) + elif selfp.sink.stb: + self.packet.append(selfp.sink.d) + if selfp.sink.stb == 1 and selfp.sink.eop == 1: + self.packet.done = True + +class AckRandomizer(Module): + def __init__(self, description, level=0): + self.level = level + + self.sink = Sink(description) + self.source = Source(description) + + self.run = Signal() + + self.comb += \ + If(self.run, + Record.connect(self.sink, self.source) + ).Else( + self.source.stb.eq(0), + self.sink.ack.eq(0), + ) + + def do_simulation(self, selfp): + n = randn(100) + if n < self.level: + selfp.run = 0 + else: + selfp.run = 1 + diff --git a/liteeth/mac/test/crc_tb.py b/liteeth/mac/test/crc_tb.py new file mode 100644 index 00000000..5d185ac1 --- /dev/null +++ b/liteeth/mac/test/crc_tb.py @@ -0,0 +1,83 @@ +from migen.fhdl.std import * +from migen.actorlib.crc import * + +from misoclib.ethmac.common import * +from misoclib.ethmac.test.common import * + +payload = [ + 0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12, + 0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11, + 0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00, + 0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C, + 0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13 +] + +crc = [ + 0x7A, 0xD5, 0x6B, 0xB3 +] + +mux = { + "inserter": 0, + "checker": 1, + "both": 2 +} + +class TB(Module): + def __init__(self, random_level=50): + sm = self.submodules + sm.streamer = PacketStreamer(eth_description(8)) + sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level) + sm.logger = PacketLogger(eth_description(8)) + sm.logger_randomizer = AckRandomizer(eth_description(8), random_level) + + self.comb += [ + self.streamer.source.connect(self.streamer_randomizer.sink), + self.logger_randomizer.source.connect(self.logger.sink) + ] + + sm.crc32_inserter = CRC32Inserter(eth_description(8)) + sm.crc32_checker = CRC32Checker(eth_description(8)) + + self.mux = Signal(2) + self.comb += [ + If(self.mux == mux["inserter"], + self.streamer_randomizer.source.connect(self.crc32_inserter.sink), + self.crc32_inserter.source.connect(self.logger_randomizer.sink) + ).Elif(self.mux == mux["checker"], + self.streamer_randomizer.source.connect(self.crc32_checker.sink), + self.crc32_checker.source.connect(self.logger_randomizer.sink) + ).Elif(self.mux == mux["both"], + self.streamer_randomizer.source.connect(self.crc32_inserter.sink), + self.crc32_inserter.source.connect(self.crc32_checker.sink), + self.crc32_checker.source.connect(self.logger_randomizer.sink) + ) + ] + + def gen_simulation(self, selfp): + selfp.mux = mux["inserter"] + print("streamer --> crc32_inserter --> logger:") + self.streamer.send(Packet(payload)) + yield from self.logger.receive() + s, l, e = check(payload+crc, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + + selfp.mux = mux["checker"] + print("streamer --> crc32_checker --> logger:") + self.streamer.send(Packet(payload+crc)) + yield from self.logger.receive() + s, l, e = check(payload, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + + selfp.mux = mux["both"] + print("streamer --> crc32_inserter --> crc32_checker --> logger:") + self.streamer.send(Packet(payload)) + yield from self.logger.receive() + s, l, e = check(payload, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + run_simulation(TB(), ncycles=1000, vcd_name="my.vcd") diff --git a/liteeth/mac/test/ethmac_tb.gtkw b/liteeth/mac/test/ethmac_tb.gtkw new file mode 100644 index 00000000..350521a2 --- /dev/null +++ b/liteeth/mac/test/ethmac_tb.gtkw @@ -0,0 +1,75 @@ +[*] +[*] GTKWave Analyzer v3.3.46 (w)1999-2012 BSI +[*] Fri Oct 31 11:20:55 2014 +[*] +[dumpfile] "/home/florent/Dev/misoc/misoclib/ethmac/test/my.vcd" +[dumpfile_mtime] "Fri Oct 31 11:20:07 2014" +[dumpfile_size] 5152269 +[savefile] "/home/florent/Dev/misoc/misoclib/ethmac/test/ethmac_tb.gtkw" +[timestart] 0 +[size] 1548 849 +[pos] 101 171 +*-25.000000 34300000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] top. +[sst_width] 379 +[signals_width] 464 +[sst_expanded] 1 +[sst_vpaned_height] 485 +@200 +-WISHBONE +@28 +top.dut.bus_ack +@22 +top.dut.bus_adr[29:0] +@28 +top.dut.bus_bte[1:0] +top.dut.bus_cti[2:0] +top.dut.bus_cyc +@22 +top.dut.bus_dat_r[31:0] +top.dut.bus_dat_w[31:0] +@28 +top.dut.bus_err +@22 +top.dut.bus_sel[3:0] +@28 +top.dut.bus_stb +top.dut.bus_we +@200 +-SRAM_READER --> PHYTx +@28 +top.dut.sram_reader_source_ack +top.dut.sram_reader_source_eop +@22 +top.dut.sram_reader_source_payload_d[31:0] +top.dut.sram_reader_source_payload_error[3:0] +top.dut.sram_reader_source_payload_last_be[3:0] +@28 +top.dut.sram_reader_source_sop +top.dut.sram_reader_source_stb +@200 +-LOOPBACK +@28 +top.dut.phy_source_ack +top.dut.phy_source_eop +@22 +top.dut.phy_source_payload_d[7:0] +@28 +top.dut.phy_source_payload_error +top.dut.phy_source_payload_last_be +top.dut.phy_source_sop +top.dut.phy_source_stb +@200 +-PHYRx --> SRAM_WRITER +@28 +top.dut.sram_writer_sink_ack +top.dut.sram_writer_sink_eop +@22 +top.dut.sram_writer_sink_payload_d[31:0] +top.dut.sram_writer_sink_payload_error[3:0] +top.dut.sram_writer_sink_payload_last_be[3:0] +@28 +top.dut.sram_writer_sink_sop +top.dut.sram_writer_sink_stb +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/liteeth/mac/test/ethmac_tb.py b/liteeth/mac/test/ethmac_tb.py new file mode 100644 index 00000000..1e4dbf1f --- /dev/null +++ b/liteeth/mac/test/ethmac_tb.py @@ -0,0 +1,123 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from misoclib.ethmac import EthMAC +from misoclib.ethmac.phy import loopback + +from misoclib.ethmac.test.common import * + +class WishboneMaster: + def __init__(self, obj): + self.obj = obj + self.dat = 0 + + def write(self, adr, dat): + self.obj.cyc = 1 + self.obj.stb = 1 + self.obj.adr = adr + self.obj.we = 1 + self.obj.sel = 0xF + self.obj.dat_w = dat + while self.obj.ack == 0: + yield + self.obj.cyc = 0 + self.obj.stb = 0 + yield + + def read(self, adr): + self.obj.cyc = 1 + self.obj.stb = 1 + self.obj.adr = adr + self.obj.we = 0 + self.obj.sel = 0xF + self.obj.dat_w = 0 + while self.obj.ack == 0: + yield + self.dat = self.obj.dat_r + self.obj.cyc = 0 + self.obj.stb = 0 + yield + +class SRAMReaderDriver: + def __init__(self, obj): + self.obj = obj + + def start(self, slot, length): + self.obj._slot.storage = slot + self.obj._length.storage = length + self.obj._start.re = 1 + yield + self.obj._start.re = 0 + yield + + def wait_done(self): + while self.obj.ev.done.pending == 0: + yield + + def clear_done(self): + self.obj.ev.done.clear = 1 + yield + self.obj.ev.done.clear = 0 + yield + +class TB(Module): + def __init__(self): + self.submodules.ethphy = loopback.LoopbackPHY() + self.submodules.ethmac = EthMAC(phy=self.ethphy, with_hw_preamble_crc=True) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + wishbone_master = WishboneMaster(selfp.ethmac.bus) + sram_reader_driver = SRAMReaderDriver(selfp.ethmac.sram_reader) + + sram_writer_slots_offset = [0x000, 0x200] + sram_reader_slots_offset = [0x400, 0x600] + + length = 1500+2 + + tx_payload = [seed_to_data(i, True) % 0xFF for i in range(length)] + [0, 0, 0, 0] + + errors = 0 + + for slot in range(2): + print("slot {}:".format(slot)) + # fill tx memory + for i in range(length//4+1): + dat = int.from_bytes(tx_payload[4*i:4*(i+1)], "big") + yield from wishbone_master.write(sram_reader_slots_offset[slot]+i, dat) + + # send tx payload & wait + yield from sram_reader_driver.start(slot, length) + yield from sram_reader_driver.wait_done() + yield from sram_reader_driver.clear_done() + + # get rx payload (loopback on PHY Model) + rx_payload = [] + for i in range(length//4+1): + yield from wishbone_master.read(sram_writer_slots_offset[slot]+i) + dat = wishbone_master.dat + rx_payload += list(dat.to_bytes(4, byteorder='big')) + + # check results + s, l, e = check(tx_payload[:length], rx_payload[:min(length, len(rx_payload))]) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + run_simulation(TB(), vcd_name="my.vcd") diff --git a/liteeth/mac/test/preamble_tb.py b/liteeth/mac/test/preamble_tb.py new file mode 100644 index 00000000..85bd7e64 --- /dev/null +++ b/liteeth/mac/test/preamble_tb.py @@ -0,0 +1,82 @@ +from migen.fhdl.std import * + +from misoclib.ethmac.common import * +from misoclib.ethmac.preamble import * +from misoclib.ethmac.test.common import * + +preamble = [ + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5 +] + +payload = [ + 0x00, 0x0A, 0xE6, 0xF0, 0x05, 0xA3, 0x00, 0x12, + 0x34, 0x56, 0x78, 0x90, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x30, 0xB3, 0xFE, 0x00, 0x00, 0x80, 0x11, + 0x72, 0xBA, 0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00, + 0x00, 0x02, 0x04, 0x00, 0x04, 0x00, 0x00, 0x1C, + 0x89, 0x4D, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13 +] + +mux = { + "inserter": 0, + "checker": 1, + "both": 2 +} + +class TB(Module): + def __init__(self, random_level=50): + sm = self.submodules + sm.streamer = PacketStreamer(eth_description(8)) + sm.streamer_randomizer = AckRandomizer(eth_description(8), random_level) + sm.logger = PacketLogger(eth_description(8)) + sm.logger_randomizer = AckRandomizer(eth_description(8), random_level) + + self.comb += [ + self.streamer.source.connect(self.streamer_randomizer.sink), + self.logger_randomizer.source.connect(self.logger.sink) + ] + + sm.preamble_inserter = PreambleInserter(8) + sm.preamble_checker = PreambleChecker(8) + + self.mux = Signal(2) + self.comb += [ + If(self.mux == mux["inserter"], + self.streamer_randomizer.source.connect(self.preamble_inserter.sink), + self.preamble_inserter.source.connect(self.logger_randomizer.sink) + ).Elif(self.mux == mux["checker"], + self.streamer_randomizer.source.connect(self.preamble_checker.sink), + self.preamble_checker.source.connect(self.logger_randomizer.sink) + ).Elif(self.mux == mux["both"], + self.streamer_randomizer.source.connect(self.preamble_inserter.sink), + self.preamble_inserter.source.connect(self.preamble_checker.sink), + self.preamble_checker.source.connect(self.logger_randomizer.sink) + ) + ] + def gen_simulation(self, selfp): + selfp.mux = mux["inserter"] + print("streamer --> preamble_inserter --> logger:") + self.streamer.send(Packet(payload)) + yield from self.logger.receive() + s, l, e = check(preamble+payload, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + + selfp.mux = mux["checker"] + print("streamer --> preamble_checker --> logger:") + self.streamer.send(Packet(preamble+payload)) + yield from self.logger.receive() + s, l, e = check(payload, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + + selfp.mux = mux["both"] + print("streamer --> preamble_inserter --> preamble_checker --> logger:") + self.streamer.send(Packet(payload)) + yield from self.logger.receive() + s, l, e = check(payload, self.logger.packet) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + run_simulation(TB(), ncycles=1000, vcd_name="my.vcd") diff --git a/liteeth/phy/__init__.py b/liteeth/phy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteeth/phy/gmii.py b/liteeth/phy/gmii.py new file mode 100644 index 00000000..c1b8243d --- /dev/null +++ b/liteeth/phy/gmii.py @@ -0,0 +1,70 @@ +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 diff --git a/liteeth/phy/loopback.py b/liteeth/phy/loopback.py new file mode 100644 index 00000000..04a51d07 --- /dev/null +++ b/liteeth/phy/loopback.py @@ -0,0 +1,31 @@ +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) diff --git a/liteeth/phy/mii.py b/liteeth/phy/mii.py new file mode 100644 index 00000000..a58b52b5 --- /dev/null +++ b/liteeth/phy/mii.py @@ -0,0 +1,122 @@ +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 diff --git a/liteeth/udp/__init__.py b/liteeth/udp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteethernet/__init__.py b/liteethernet/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/arp/__init__.py b/liteethernet/arp/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/common.py b/liteethernet/common.py deleted file mode 100644 index 177943ee..00000000 --- a/liteethernet/common.py +++ /dev/null @@ -1,14 +0,0 @@ -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) diff --git a/liteethernet/etherbone/__init__.py b/liteethernet/etherbone/__init__.py deleted file mode 100644 index f84348a1..00000000 --- a/liteethernet/etherbone/__init__.py +++ /dev/null @@ -1,581 +0,0 @@ -# This file is Copyright (c) 2014 Robert Jordens -# 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 diff --git a/liteethernet/ip/__init__.py b/liteethernet/ip/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/mac/__init__.py b/liteethernet/mac/__init__.py deleted file mode 100644 index e9b2ccfa..00000000 --- a/liteethernet/mac/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -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") diff --git a/liteethernet/mac/common.py b/liteethernet/mac/common.py deleted file mode 100644 index 177943ee..00000000 --- a/liteethernet/mac/common.py +++ /dev/null @@ -1,14 +0,0 @@ -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) diff --git a/liteethernet/mac/core/__init__.py b/liteethernet/mac/core/__init__.py deleted file mode 100644 index a361d5dc..00000000 --- a/liteethernet/mac/core/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ - -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 diff --git a/liteethernet/mac/core/crc.py b/liteethernet/mac/core/crc.py deleted file mode 100644 index 8a058fae..00000000 --- a/liteethernet/mac/core/crc.py +++ /dev/null @@ -1,284 +0,0 @@ -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) diff --git a/liteethernet/mac/core/last_be.py b/liteethernet/mac/core/last_be.py deleted file mode 100644 index 555e523f..00000000 --- a/liteethernet/mac/core/last_be.py +++ /dev/null @@ -1,40 +0,0 @@ -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) - ] diff --git a/liteethernet/mac/core/preamble.py b/liteethernet/mac/core/preamble.py deleted file mode 100644 index f1c76034..00000000 --- a/liteethernet/mac/core/preamble.py +++ /dev/null @@ -1,146 +0,0 @@ -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"), - ) - ) diff --git a/liteethernet/mac/frontend/__init__.py b/liteethernet/mac/frontend/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/mac/frontend/sram.py b/liteethernet/mac/frontend/sram.py deleted file mode 100644 index c51c0ecb..00000000 --- a/liteethernet/mac/frontend/sram.py +++ /dev/null @@ -1,252 +0,0 @@ -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) diff --git a/liteethernet/mac/frontend/wishbone.py b/liteethernet/mac/frontend/wishbone.py deleted file mode 100644 index 56601e12..00000000 --- a/liteethernet/mac/frontend/wishbone.py +++ /dev/null @@ -1,39 +0,0 @@ -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 diff --git a/liteethernet/mac/test/Makefile b/liteethernet/mac/test/Makefile deleted file mode 100644 index 55d28e10..00000000 --- a/liteethernet/mac/test/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -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 diff --git a/liteethernet/mac/test/__init__.py b/liteethernet/mac/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/mac/test/common.py b/liteethernet/mac/test/common.py deleted file mode 100644 index 13123700..00000000 --- a/liteethernet/mac/test/common.py +++ /dev/null @@ -1,120 +0,0 @@ -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 - diff --git a/liteethernet/mac/test/crc_tb.py b/liteethernet/mac/test/crc_tb.py deleted file mode 100644 index 5d185ac1..00000000 --- a/liteethernet/mac/test/crc_tb.py +++ /dev/null @@ -1,83 +0,0 @@ -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") diff --git a/liteethernet/mac/test/ethmac_tb.gtkw b/liteethernet/mac/test/ethmac_tb.gtkw deleted file mode 100644 index 350521a2..00000000 --- a/liteethernet/mac/test/ethmac_tb.gtkw +++ /dev/null @@ -1,75 +0,0 @@ -[*] -[*] 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 diff --git a/liteethernet/mac/test/ethmac_tb.py b/liteethernet/mac/test/ethmac_tb.py deleted file mode 100644 index 1e4dbf1f..00000000 --- a/liteethernet/mac/test/ethmac_tb.py +++ /dev/null @@ -1,123 +0,0 @@ -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") diff --git a/liteethernet/mac/test/preamble_tb.py b/liteethernet/mac/test/preamble_tb.py deleted file mode 100644 index 85bd7e64..00000000 --- a/liteethernet/mac/test/preamble_tb.py +++ /dev/null @@ -1,82 +0,0 @@ -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") diff --git a/liteethernet/phy/__init__.py b/liteethernet/phy/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/phy/gmii.py b/liteethernet/phy/gmii.py deleted file mode 100644 index c1b8243d..00000000 --- a/liteethernet/phy/gmii.py +++ /dev/null @@ -1,70 +0,0 @@ -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 diff --git a/liteethernet/phy/loopback.py b/liteethernet/phy/loopback.py deleted file mode 100644 index 04a51d07..00000000 --- a/liteethernet/phy/loopback.py +++ /dev/null @@ -1,31 +0,0 @@ -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) diff --git a/liteethernet/phy/mii.py b/liteethernet/phy/mii.py deleted file mode 100644 index a58b52b5..00000000 --- a/liteethernet/phy/mii.py +++ /dev/null @@ -1,122 +0,0 @@ -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 diff --git a/liteethernet/udp/__init__.py b/liteethernet/udp/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/setup.py b/setup.py index ca0e57d8..93351841 100644 --- a/setup.py +++ b/setup.py @@ -9,18 +9,18 @@ README = open(os.path.join(here, "README")).read() 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"],