--- /dev/null
+from migen.fhdl.std import *
+from migen.flow.actor import EndpointDescription
+
+eth_mtu = 1532
+eth_preamble = 0xD555555555555555
+buffer_depth = 2**log2_int(eth_mtu, need_pow2=False)
+
+def eth_description(dw):
+ layout = [
+ ("d", dw),
+ ("last_be", dw//8),
+ ("error", dw//8)
+ ]
+ return EndpointDescription(layout, packetized=True)
-# This file is Copyright (c) 2014 Florent Kermarrec <florent@enjoy-digital.fr>
-# License: BSD
+from liteethernet.common import *
+from liteethernet.mac import LiteEthernetMAC
-from migen.fhdl.std import *
-
-from migen.bus import wishbone
-from migen.actorlib.fifo import AsyncFIFO
-from migen.actorlib.structuring import Converter, Pipeline
-from migen.bank.eventmanager import SharedIRQ
-from migen.bank.description import *
-from migen.fhdl.simplify import *
-
-from misoclib.ethmac.common import *
-from misoclib.ethmac.preamble import PreambleInserter, PreambleChecker
-from migen.actorlib.crc import CRC32Inserter, CRC32Checker
-from misoclib.ethmac.last_be import TXLastBE, RXLastBE
-from misoclib.ethmac.sram import SRAMWriter, SRAMReader
-
-class EthMAC(Module, AutoCSR):
- def __init__(self, phy, interface="wishbone", with_hw_preamble_crc=True, endianness="be"):
- # Preamble / CRC (optional)
- if with_hw_preamble_crc:
- self._hw_preamble_crc = CSRStatus(reset=1)
- # Preamble insert/check
- preamble_inserter = PreambleInserter(phy.dw)
- preamble_checker = PreambleChecker(phy.dw)
- self.submodules += RenameClockDomains(preamble_inserter, "eth_tx")
- self.submodules += RenameClockDomains(preamble_checker, "eth_rx")
-
- # CRC insert/check
- crc32_inserter = CRC32Inserter(eth_description(phy.dw))
- crc32_checker = CRC32Checker(eth_description(phy.dw))
- self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
- self.submodules += RenameClockDomains(crc32_checker, "eth_rx")
-
- # Delimiters
- tx_last_be = TXLastBE(phy.dw)
- rx_last_be = RXLastBE(phy.dw)
- self.submodules += RenameClockDomains(tx_last_be, "eth_tx")
- self.submodules += RenameClockDomains(rx_last_be, "eth_rx")
-
- # Converters
- reverse = endianness == "be"
- tx_converter = Converter(eth_description(32), eth_description(phy.dw), reverse=reverse)
- rx_converter = Converter(eth_description(phy.dw), eth_description(32), reverse=reverse)
- self.submodules += RenameClockDomains(tx_converter, "eth_tx")
- self.submodules += RenameClockDomains(rx_converter, "eth_rx")
-
- # Cross Domain Crossing
- tx_cdc = AsyncFIFO(eth_description(32), 4)
- rx_cdc = AsyncFIFO(eth_description(32), 4)
- self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"})
- self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"})
-
- # Graph
- if with_hw_preamble_crc:
- rx_pipeline = [phy, preamble_checker, crc32_checker, rx_last_be, rx_converter, rx_cdc]
- tx_pipeline = [tx_cdc, tx_converter, tx_last_be, crc32_inserter, preamble_inserter, phy]
- else:
- rx_pipeline = [phy, rx_last_be, rx_converter, rx_cdc]
- tx_pipeline = [tx_cdc, tx_converter, tx_last_be, phy]
- self.submodules.rx_pipeline = Pipeline(*rx_pipeline)
- self.submodules.tx_pipeline = Pipeline(*tx_pipeline)
+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
+
+from liteethernet.common import *
+from liteethernet.mac.common import *
+from liteethernet.mac.preamble import PreambleInserter, PreambleChecker
+from liteethernet.mac.crc import CRC32Inserter, CRC32Checker
+from liteethernet.mac.last_be import TXLastBE, RXLastBE
+
+class LiteEthernetMACCore(Module, AutoCSR):
+ def __init__(self, phy, with_hw_preamble_crc=True, endianness="be"):
+ # Preamble / CRC (optional)
+ if with_hw_preamble_crc:
+ self._hw_preamble_crc = CSRStatus(reset=1)
+ # Preamble insert/check
+ preamble_inserter = PreambleInserter(phy.dw)
+ preamble_checker = PreambleChecker(phy.dw)
+ self.submodules += RenameClockDomains(preamble_inserter, "eth_tx")
+ self.submodules += RenameClockDomains(preamble_checker, "eth_rx")
+
+ # CRC insert/check
+ crc32_inserter = CRC32Inserter(eth_description(phy.dw))
+ crc32_checker = CRC32Checker(eth_description(phy.dw))
+ self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
+ self.submodules += RenameClockDomains(crc32_checker, "eth_rx")
+
+ # Delimiters
+ tx_last_be = TXLastBE(phy.dw)
+ rx_last_be = RXLastBE(phy.dw)
+ self.submodules += RenameClockDomains(tx_last_be, "eth_tx")
+ self.submodules += RenameClockDomains(rx_last_be, "eth_rx")
+
+ # Converters
+ reverse = endianness == "be"
+ tx_converter = Converter(eth_description(32), eth_description(phy.dw), reverse=reverse)
+ rx_converter = Converter(eth_description(phy.dw), eth_description(32), reverse=reverse)
+ self.submodules += RenameClockDomains(tx_converter, "eth_tx")
+ self.submodules += RenameClockDomains(rx_converter, "eth_rx")
+
+ # Cross Domain Crossing
+ tx_cdc = AsyncFIFO(eth_description(32), 4)
+ rx_cdc = AsyncFIFO(eth_description(32), 4)
+ self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"})
+ self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"})
+
+ # Graph
+ if with_hw_preamble_crc:
+ rx_pipeline = [phy, preamble_checker, crc32_checker, rx_last_be, rx_converter, rx_cdc]
+ tx_pipeline = [tx_cdc, tx_converter, tx_last_be, crc32_inserter, preamble_inserter, phy]
+ else:
+ rx_pipeline = [phy, rx_last_be, rx_converter, rx_cdc]
+ tx_pipeline = [tx_cdc, tx_converter, tx_last_be, phy]
+ self.submodules.rx_pipeline = Pipeline(*rx_pipeline)
+ self.submodules.tx_pipeline = Pipeline(*tx_pipeline)
+
+ self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.record import *
+from migen.genlib.misc import optree, chooser
+from migen.genlib.crc import *
+from migen.flow.actor import Sink, Source
+from migen.actorlib.fifo import SyncFIFO
+
+from collections import OrderedDict
+
+class CRCEngine(Module):
+ """Cyclic Redundancy Check Engine
+
+ Compute next CRC value from last CRC value and data input using
+ an optimized asynchronous LFSR.
+
+ Parameters
+ ----------
+ dat_width : int
+ Width of the data bus.
+ width : int
+ Width of the CRC.
+ polynom : int
+ Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC)
+
+ Attributes
+ ----------
+ d : in
+ Data input.
+ last : in
+ last CRC value.
+ next :
+ next CRC value.
+ """
+ def __init__(self, dat_width, width, polynom):
+ self.d = Signal(dat_width)
+ self.last = Signal(width)
+ self.next = Signal(width)
+
+ ###
+
+ def _optimize_eq(l):
+ """
+ Replace even numbers of XORs in the equation
+ with an equivalent XOR
+ """
+ d = OrderedDict()
+ for e in l:
+ if e in d:
+ d[e] += 1
+ else:
+ d[e] = 1
+ r = []
+ for key, value in d.items():
+ if value%2 != 0:
+ r.append(key)
+ return r
+
+ # compute and optimize CRC's LFSR
+ curval = [[("state", i)] for i in range(width)]
+ for i in range(dat_width):
+ feedback = curval.pop() + [("din", i)]
+ for j in range(width-1):
+ if (polynom & (1<<(j+1))):
+ curval[j] += feedback
+ curval[j] = _optimize_eq(curval[j])
+ curval.insert(0, feedback)
+
+ # implement logic
+ for i in range(width):
+ xors = []
+ for t, n in curval[i]:
+ if t == "state":
+ xors += [self.last[n]]
+ elif t == "din":
+ xors += [self.d[n]]
+ self.comb += self.next[i].eq(optree("^", xors))
+
+@DecorateModule(InsertReset)
+@DecorateModule(InsertCE)
+class CRC32(Module):
+ """IEEE 802.3 CRC
+
+ Implement an IEEE 802.3 CRC generator/checker.
+
+ Parameters
+ ----------
+ dat_width : int
+ Width of the data bus.
+
+ Attributes
+ ----------
+ d : in
+ Data input.
+ value : out
+ CRC value (used for generator).
+ error : out
+ CRC error (used for checker).
+ """
+ width = 32
+ polynom = 0x04C11DB7
+ init = 2**width-1
+ check = 0xC704DD7B
+ def __init__(self, dat_width):
+ self.d = Signal(dat_width)
+ self.value = Signal(self.width)
+ self.error = Signal()
+
+ ###
+
+ self.submodules.engine = CRCEngine(dat_width, self.width, self.polynom)
+ reg = Signal(self.width, reset=self.init)
+ self.sync += reg.eq(self.engine.next)
+ self.comb += [
+ self.engine.d.eq(self.d),
+ self.engine.last.eq(reg),
+
+ self.value.eq(~reg[::-1]),
+ self.error.eq(self.engine.next != self.check)
+ ]
+
+class CRCInserter(Module):
+ """CRC Inserter
+
+ Append a CRC at the end of each packet.
+
+ Parameters
+ ----------
+ layout : layout
+ Layout of the dataflow.
+
+ Attributes
+ ----------
+ sink : in
+ Packets input without CRC.
+ source : out
+ Packets output with CRC.
+ """
+ def __init__(self, crc_class, layout):
+ self.sink = sink = Sink(layout)
+ self.source = source = Source(layout)
+ self.busy = Signal()
+
+ ###
+
+ dw = flen(sink.d)
+ crc = crc_class(dw)
+ fsm = FSM(reset_state="IDLE")
+ self.submodules += crc, fsm
+
+ fsm.act("IDLE",
+ crc.reset.eq(1),
+ sink.ack.eq(1),
+ If(sink.stb & sink.sop,
+ sink.ack.eq(0),
+ NextState("COPY"),
+ )
+ )
+ fsm.act("COPY",
+ crc.ce.eq(sink.stb & source.ack),
+ crc.d.eq(sink.d),
+ Record.connect(sink, source),
+ source.eop.eq(0),
+ If(sink.stb & sink.eop & source.ack,
+ NextState("INSERT"),
+ )
+ )
+ ratio = crc.width//dw
+ if ratio > 1:
+ cnt = Signal(max=ratio, reset=ratio-1)
+ cnt_done = Signal()
+ fsm.act("INSERT",
+ source.stb.eq(1),
+ chooser(crc.value, cnt, source.d, reverse=True),
+ If(cnt_done,
+ source.eop.eq(1),
+ If(source.ack, NextState("IDLE"))
+ )
+ )
+ self.comb += cnt_done.eq(cnt == 0)
+ self.sync += \
+ If(fsm.ongoing("IDLE"),
+ cnt.eq(cnt.reset)
+ ).Elif(fsm.ongoing("INSERT") & ~cnt_done,
+ cnt.eq(cnt - source.ack)
+ )
+ else:
+ fsm.act("INSERT",
+ source.stb.eq(1),
+ source.eop.eq(1),
+ source.d.eq(crc.value),
+ If(source.ack, NextState("IDLE"))
+ )
+ self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
+
+class CRC32Inserter(CRCInserter):
+ def __init__(self, layout):
+ CRCInserter.__init__(self, CRC32, layout)
+
+class CRCChecker(Module):
+ """CRC Checker
+
+ Check CRC at the end of each packet.
+
+ Parameters
+ ----------
+ layout : layout
+ Layout of the dataflow.
+
+ Attributes
+ ----------
+ sink : in
+ Packets input with CRC.
+ source : out
+ Packets output without CRC and "error" set to 0
+ on eop when CRC OK / set to 1 when CRC KO.
+ """
+ def __init__(self, crc_class, layout):
+ self.sink = sink = Sink(layout)
+ self.source = source = Source(layout)
+ self.busy = Signal()
+
+ ###
+
+ dw = flen(sink.d)
+ crc = crc_class(dw)
+ self.submodules += crc
+ ratio = crc.width//dw
+
+ error = Signal()
+ fifo = InsertReset(SyncFIFO(layout, ratio + 1))
+ self.submodules += fifo
+
+ fsm = FSM(reset_state="RESET")
+ self.submodules += fsm
+
+ fifo_in = Signal()
+ fifo_out = Signal()
+ fifo_full = Signal()
+
+ self.comb += [
+ fifo_full.eq(fifo.fifo.level == ratio),
+ fifo_in.eq(sink.stb & (~fifo_full | fifo_out)),
+ fifo_out.eq(source.stb & source.ack),
+
+ Record.connect(sink, fifo.sink),
+ fifo.sink.stb.eq(fifo_in),
+ self.sink.ack.eq(fifo_in),
+
+ source.stb.eq(sink.stb & fifo_full),
+ source.sop.eq(fifo.source.sop),
+ source.eop.eq(sink.eop),
+ fifo.source.ack.eq(fifo_out),
+ source.payload.eq(fifo.source.payload),
+
+ source.error.eq(sink.error | crc.error),
+ ]
+
+ fsm.act("RESET",
+ crc.reset.eq(1),
+ fifo.reset.eq(1),
+ NextState("IDLE"),
+ )
+ fsm.act("IDLE",
+ crc.d.eq(sink.d),
+ If(sink.stb & sink.sop & sink.ack,
+ crc.ce.eq(1),
+ NextState("COPY")
+ )
+ )
+ fsm.act("COPY",
+ crc.d.eq(sink.d),
+ If(sink.stb & sink.ack,
+ crc.ce.eq(1),
+ If(sink.eop,
+ NextState("RESET")
+ )
+ )
+ )
+ self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
+
+class CRC32Checker(CRCChecker):
+ def __init__(self, layout):
+ CRCChecker.__init__(self, CRC32, layout)
from migen.genlib.record import *
from migen.flow.actor import Sink, Source
-from misoclib.ethmac.common import *
+from liteethernet.common import *
+from liteethernet.mac.common import *
class TXLastBE(Module):
def __init__(self, d_w):
from migen.genlib.record import *
from migen.flow.actor import Sink, Source
-from misoclib.ethmac.common import *
+from liteethernet.common import *
+from liteethernet.ethmac.common import *
class PreambleInserter(Module):
def __init__(self, d_w):
from migen.bank.description import *
from migen.bank.eventmanager import *
-from misoclib.ethmac.common import *
+from liteethernet.common import *
+from liteethernet.mac.common import *
class SRAMWriter(Module, AutoCSR):
def __init__(self, depth, nslots=2):
--- /dev/null
+from liteethernet.common import *
+from liteethernet.mac import LiteEthernetMAC
+
+class LiteEthernetMACWishboneInterface(Module, AutoCSR):
+ def __init__(self, nrxslots=2, ntxslots=2):
+ self.sink = Sink(mac_description(dw))
+ self.source = Source(max_description(dw))
+ self.bus = wishbone.Interface()
+
+ ###
+
+ # SRAM Storage
+ sram_depth = buffer_depth//(32//8)
+ self.submodules.sram_writer = SRAMWriter(sram_depth, nrxslots)
+ self.submodules.sram_reader = SRAMReader(sram_depth, ntxslots)
+ self.submodules.ev = SharedIRQ(self.sram_writer.ev, self.sram_reader.ev)
+ self.comb += [
+ Record.connect(self.sink, self.sram_writer.sink),
+ Record.connect(self.sram_reader.source, self.source)
+ ]
+
+ # Interface
+ wb_rx_sram_ifs = [wishbone.SRAM(self.sram_writer.mems[n], read_only=True)
+ for n in range(nrxslots)]
+ # TODO: FullMemoryWE should move to Mibuild
+ wb_tx_sram_ifs = [FullMemoryWE(wishbone.SRAM(self.sram_reader.mems[n], read_only=False))
+ for n in range(ntxslots)]
+ wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs
+
+ wb_slaves = []
+ decoderoffset = log2_int(sram_depth)
+ decoderbits = log2_int(len(wb_sram_ifs))
+ for n, wb_sram_if in enumerate(wb_sram_ifs):
+ def slave_filter(a, v=n):
+ return a[decoderoffset:decoderoffset+decoderbits] == v
+ wb_slaves.append((slave_filter, wb_sram_if.bus))
+ self.submodules += wb_sram_if
+ wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True)
+ self.submodules += wb_con
+++ /dev/null
-from migen.fhdl.std import *
-from migen.flow.actor import Sink, Source
-from migen.bank.description import *
-from migen.genlib.resetsync import AsyncResetSynchronizer
-
-from misoclib.ethmac.common import *
-
-class GMIIPHYTX(Module):
- def __init__(self, pads):
- self.sink = sink = Sink(eth_description(8))
-
- ###
-
- self.sync += [
- pads.tx_er.eq(0),
- pads.tx_en.eq(sink.stb),
- pads.tx_data.eq(sink.d)
- ]
- self.comb += sink.ack.eq(1)
-
-class GMIIPHYRX(Module):
- def __init__(self, pads):
- self.source = source = Source(eth_description(8))
-
- ###
-
- dv_d = Signal()
- self.sync += dv_d.eq(pads.dv)
-
- sop = Signal()
- eop = Signal()
- self.comb += [
- sop.eq(pads.dv & ~dv_d),
- eop.eq(~pads.dv & dv_d)
- ]
- self.sync += [
- source.stb.eq(pads.dv),
- source.sop.eq(sop),
- source.d.eq(pads.rx_data)
- ]
- self.comb += source.eop.eq(eop)
-
-# CRG is the only Xilinx specific module.
-# TODO: use generic code or add support for others vendors
-class GMIIPHYCRG(Module, AutoCSR):
- def __init__(self, clock_pads, pads):
- self._reset = CSRStorage()
-
- ###
-
- self.clock_domains.cd_eth_rx = ClockDomain()
- self.clock_domains.cd_eth_tx = ClockDomain()
- self.specials += [
- Instance("ODDR",
- p_DDR_CLK_EDGE="SAME_EDGE",
- i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0,
- i_D1=1, i_D2=0, o_Q=clock_pads.gtx,
- ),
- Instance("BUFG", i_I=clock_pads.rx, o_O=self.cd_eth_rx.clk),
- ]
- self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk)
-
- reset = self._reset.storage
- self.comb += pads.rst_n.eq(~reset)
- self.specials += [
- AsyncResetSynchronizer(self.cd_eth_tx, reset),
- AsyncResetSynchronizer(self.cd_eth_rx, reset),
- ]
-
-class GMIIPHY(Module, AutoCSR):
- def __init__(self, clock_pads, pads):
- self.dw = 8
- self.submodules.crg = GMIIPHYCRG(clock_pads, pads)
- self.submodules.tx = RenameClockDomains(GMIIPHYTX(pads), "eth_tx")
- self.submodules.rx = RenameClockDomains(GMIIPHYRX(pads), "eth_rx")
- self.sink, self.source = self.tx.sink, self.rx.source
+++ /dev/null
-from migen.fhdl.std import *
-from migen.flow.actor import Sink, Source
-from migen.bank.description import *
-from migen.genlib.record import *
-
-from misoclib.ethmac.common import *
-
-class LoopbackPHYCRG(Module, AutoCSR):
- def __init__(self):
- self._reset = CSRStorage()
-
- ###
-
- self.clock_domains.cd_eth_rx = ClockDomain()
- self.clock_domains.cd_eth_tx = ClockDomain()
- self.comb += [
- self.cd_eth_rx.clk.eq(ClockSignal()),
- self.cd_eth_tx.clk.eq(ClockSignal())
- ]
-
- reset = self._reset.storage
- self.comb += [
- self.cd_eth_rx.rst.eq(reset),
- self.cd_eth_tx.rst.eq(reset)
- ]
-
-class LoopbackPHY(Module, AutoCSR):
- def __init__(self):
- self.dw = 8
- self.submodules.crg = LoopbackPHYCRG()
- self.sink = sink = Sink(eth_description(8))
- self.source = source = Source(eth_description(8))
- self.comb += Record.connect(self.sink, self.source)
+++ /dev/null
-from migen.fhdl.std import *
-from migen.genlib.fsm import FSM, NextState
-from migen.flow.actor import Sink, Source
-from migen.bank.description import *
-from migen.genlib.resetsync import AsyncResetSynchronizer
-
-from misoclib.ethmac.common import *
-
-class MIIPHYTX(Module):
- def __init__(self, pads):
- self.sink = sink = Sink(eth_description(8))
-
- ###
-
- tx_en_r = Signal()
- tx_data_r = Signal(4)
- self.sync += [
- pads.tx_er.eq(0),
- pads.tx_en.eq(tx_en_r),
- pads.tx_data.eq(tx_data_r),
- ]
-
- fsm = FSM(reset_state="IDLE")
- self.submodules += fsm
- fsm.act("IDLE",
- sink.ack.eq(1),
- If(sink.stb & sink.sop,
- sink.ack.eq(0),
- NextState("SEND_LO")
- )
- )
- fsm.act("SEND_LO",
- tx_data_r.eq(sink.d[0:4]),
- tx_en_r.eq(1),
- NextState("SEND_HI")
- )
- fsm.act("SEND_HI",
- tx_data_r.eq(sink.d[4:8]),
- tx_en_r.eq(1),
- sink.ack.eq(1),
- If(sink.stb & sink.eop,
- NextState("IDLE")
- ).Else(
- NextState("SEND_LO")
- )
- )
-
-class MIIPHYRX(Module):
- def __init__(self, pads):
- self.source = source = Source(eth_description(8))
-
- ###
-
- sop = source.sop
- set_sop = Signal()
- clr_sop = Signal()
- self.sync += \
- If(clr_sop,
- sop.eq(0)
- ).Elif(set_sop,
- sop.eq(1)
- )
-
- lo = Signal(4)
- hi = Signal(4)
- load_nibble = Signal(2)
- self.sync += \
- If(load_nibble[0],
- lo.eq(pads.rx_data)
- ).Elif(load_nibble[1],
- hi.eq(pads.rx_data)
- )
- self.comb += [
- source.d.eq(Cat(lo, hi))
- ]
-
- fsm = FSM(reset_state="IDLE")
- self.submodules += fsm
- fsm.act("IDLE",
- set_sop.eq(1),
- If(pads.dv,
- load_nibble.eq(0b01),
- NextState("LOAD_HI")
- )
- )
- fsm.act("LOAD_LO",
- source.stb.eq(1),
- If(pads.dv,
- clr_sop.eq(1),
- load_nibble.eq(0b01),
- NextState("LOAD_HI")
- ).Else(
- source.eop.eq(1),
- NextState("IDLE")
- )
- )
- fsm.act("LOAD_HI",
- load_nibble.eq(0b10),
- NextState("LOAD_LO")
- )
-
-class MIIPHYCRG(Module, AutoCSR):
- def __init__(self, clock_pads, pads):
- self._reset = CSRStorage()
-
- ###
-
- self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy)
-
- self.clock_domains.cd_eth_rx = ClockDomain()
- self.clock_domains.cd_eth_tx = ClockDomain()
- self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
- self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx)
-
- reset = self._reset.storage
- self.comb += pads.rst_n.eq(~reset)
- self.specials += [
- AsyncResetSynchronizer(self.cd_eth_tx, reset),
- AsyncResetSynchronizer(self.cd_eth_rx, reset),
- ]
-
-class MIIPHY(Module, AutoCSR):
- def __init__(self, clock_pads, pads):
- self.dw = 8
- self.submodules.crg = MIIPHYCRG(clock_pads, pads)
- self.submodules.tx = RenameClockDomains(MIIPHYTX(pads), "eth_tx")
- self.submodules.rx = RenameClockDomains(MIIPHYRX(pads), "eth_rx")
- self.sink, self.source = self.tx.sink, self.rx.source
--- /dev/null
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+from liteethernet.common import *
+
+class GMIIPHYTX(Module):
+ def __init__(self, pads):
+ self.sink = sink = Sink(eth_description(8))
+ ###
+ self.sync += [
+ pads.tx_er.eq(0),
+ pads.tx_en.eq(sink.stb),
+ pads.tx_data.eq(sink.d)
+ ]
+ self.comb += sink.ack.eq(1)
+
+class GMIIPHYRX(Module):
+ def __init__(self, pads):
+ self.source = source = Source(eth_description(8))
+ ###
+ dv_d = Signal()
+ self.sync += dv_d.eq(pads.dv)
+
+ sop = Signal()
+ eop = Signal()
+ self.comb += [
+ sop.eq(pads.dv & ~dv_d),
+ eop.eq(~pads.dv & dv_d)
+ ]
+ self.sync += [
+ source.stb.eq(pads.dv),
+ source.sop.eq(sop),
+ source.d.eq(pads.rx_data)
+ ]
+ self.comb += source.eop.eq(eop)
+
+# CRG is the only Xilinx specific module.
+# TODO: use generic code or add support for others vendors
+class GMIIPHYCRG(Module, AutoCSR):
+ def __init__(self, clock_pads, pads):
+ self._reset = CSRStorage()
+ ###
+ self.clock_domains.cd_eth_rx = ClockDomain()
+ self.clock_domains.cd_eth_tx = ClockDomain()
+ self.specials += [
+ Instance("ODDR",
+ p_DDR_CLK_EDGE="SAME_EDGE",
+ i_C=ClockSignal("eth_tx"), i_CE=1, i_S=0, i_R=0,
+ i_D1=1, i_D2=0, o_Q=clock_pads.gtx,
+ ),
+ Instance("BUFG", i_I=clock_pads.rx, o_O=self.cd_eth_rx.clk),
+ ]
+ self.comb += self.cd_eth_tx.clk.eq(self.cd_eth_rx.clk)
+
+ reset = self._reset.storage
+ self.comb += pads.rst_n.eq(~reset)
+ self.specials += [
+ AsyncResetSynchronizer(self.cd_eth_tx, reset),
+ AsyncResetSynchronizer(self.cd_eth_rx, reset),
+ ]
+
+class GMIIPHY(Module, AutoCSR):
+ def __init__(self, clock_pads, pads):
+ self.dw = 8
+ self.submodules.crg = GMIIPHYCRG(clock_pads, pads)
+ self.submodules.tx = RenameClockDomains(GMIIPHYTX(pads), "eth_tx")
+ self.submodules.rx = RenameClockDomains(GMIIPHYRX(pads), "eth_rx")
+ self.sink, self.source = self.tx.sink, self.rx.source
--- /dev/null
+from migen.fhdl.std import *
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.record import *
+
+from liteethernet.common import *
+
+class LoopbackPHYCRG(Module, AutoCSR):
+ def __init__(self):
+ self._reset = CSRStorage()
+ ###
+ self.clock_domains.cd_eth_rx = ClockDomain()
+ self.clock_domains.cd_eth_tx = ClockDomain()
+ self.comb += [
+ self.cd_eth_rx.clk.eq(ClockSignal()),
+ self.cd_eth_tx.clk.eq(ClockSignal())
+ ]
+
+ reset = self._reset.storage
+ self.comb += [
+ self.cd_eth_rx.rst.eq(reset),
+ self.cd_eth_tx.rst.eq(reset)
+ ]
+
+class LoopbackPHY(Module, AutoCSR):
+ def __init__(self):
+ self.dw = 8
+ self.submodules.crg = LoopbackPHYCRG()
+ self.sink = sink = Sink(eth_description(8))
+ self.source = source = Source(eth_description(8))
+ self.comb += Record.connect(self.sink, self.source)
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.fsm import FSM, NextState
+from migen.flow.actor import Sink, Source
+from migen.bank.description import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+from liteethernet.common import *
+
+class MIIPHYTX(Module):
+ def __init__(self, pads):
+ self.sink = sink = Sink(eth_description(8))
+ ###
+ tx_en_r = Signal()
+ tx_data_r = Signal(4)
+ self.sync += [
+ pads.tx_er.eq(0),
+ pads.tx_en.eq(tx_en_r),
+ pads.tx_data.eq(tx_data_r),
+ ]
+
+ fsm = FSM(reset_state="IDLE")
+ self.submodules += fsm
+ fsm.act("IDLE",
+ sink.ack.eq(1),
+ If(sink.stb & sink.sop,
+ sink.ack.eq(0),
+ NextState("SEND_LO")
+ )
+ )
+ fsm.act("SEND_LO",
+ tx_data_r.eq(sink.d[0:4]),
+ tx_en_r.eq(1),
+ NextState("SEND_HI")
+ )
+ fsm.act("SEND_HI",
+ tx_data_r.eq(sink.d[4:8]),
+ tx_en_r.eq(1),
+ sink.ack.eq(1),
+ If(sink.stb & sink.eop,
+ NextState("IDLE")
+ ).Else(
+ NextState("SEND_LO")
+ )
+ )
+
+class MIIPHYRX(Module):
+ def __init__(self, pads):
+ self.source = source = Source(eth_description(8))
+ ###
+ sop = source.sop
+ set_sop = Signal()
+ clr_sop = Signal()
+ self.sync += \
+ If(clr_sop,
+ sop.eq(0)
+ ).Elif(set_sop,
+ sop.eq(1)
+ )
+
+ lo = Signal(4)
+ hi = Signal(4)
+ load_nibble = Signal(2)
+ self.sync += \
+ If(load_nibble[0],
+ lo.eq(pads.rx_data)
+ ).Elif(load_nibble[1],
+ hi.eq(pads.rx_data)
+ )
+ self.comb += [
+ source.d.eq(Cat(lo, hi))
+ ]
+
+ fsm = FSM(reset_state="IDLE")
+ self.submodules += fsm
+ fsm.act("IDLE",
+ set_sop.eq(1),
+ If(pads.dv,
+ load_nibble.eq(0b01),
+ NextState("LOAD_HI")
+ )
+ )
+ fsm.act("LOAD_LO",
+ source.stb.eq(1),
+ If(pads.dv,
+ clr_sop.eq(1),
+ load_nibble.eq(0b01),
+ NextState("LOAD_HI")
+ ).Else(
+ source.eop.eq(1),
+ NextState("IDLE")
+ )
+ )
+ fsm.act("LOAD_HI",
+ load_nibble.eq(0b10),
+ NextState("LOAD_LO")
+ )
+
+class MIIPHYCRG(Module, AutoCSR):
+ def __init__(self, clock_pads, pads):
+ self._reset = CSRStorage()
+ ###
+ self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy)
+
+ self.clock_domains.cd_eth_rx = ClockDomain()
+ self.clock_domains.cd_eth_tx = ClockDomain()
+ self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
+ self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx)
+
+ reset = self._reset.storage
+ self.comb += pads.rst_n.eq(~reset)
+ self.specials += [
+ AsyncResetSynchronizer(self.cd_eth_tx, reset),
+ AsyncResetSynchronizer(self.cd_eth_rx, reset),
+ ]
+
+class MIIPHY(Module, AutoCSR):
+ def __init__(self, clock_pads, pads):
+ self.dw = 8
+ self.submodules.crg = MIIPHYCRG(clock_pads, pads)
+ self.submodules.tx = RenameClockDomains(MIIPHYTX(pads), "eth_tx")
+ self.submodules.rx = RenameClockDomains(MIIPHYRX(pads), "eth_rx")
+ self.sink, self.source = self.tx.sink, self.rx.source