From: Florent Kermarrec Date: Tue, 27 Jan 2015 23:33:26 +0000 (+0100) Subject: start code adaptation X-Git-Tag: 24jan2021_ls180~2604^2~126 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8477974ef1441e9dc6bc48d72ccd8bc54fba5a72;p=litex.git start code adaptation --- diff --git a/liteethernet/common.py b/liteethernet/common.py new file mode 100644 index 00000000..177943ee --- /dev/null +++ b/liteethernet/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/liteethernet/mac/__init__.py b/liteethernet/mac/__init__.py index a7ea0c2c..e9b2ccfa 100644 --- a/liteethernet/mac/__init__.py +++ b/liteethernet/mac/__init__.py @@ -1,66 +1,9 @@ -# This file is Copyright (c) 2014 Florent Kermarrec -# 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 diff --git a/liteethernet/mac/core/__init__.py b/liteethernet/mac/core/__init__.py index e69de29b..a361d5dc 100644 --- a/liteethernet/mac/core/__init__.py +++ b/liteethernet/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/liteethernet/mac/core/crc.py b/liteethernet/mac/core/crc.py new file mode 100644 index 00000000..8a058fae --- /dev/null +++ b/liteethernet/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/liteethernet/mac/core/last_be.py b/liteethernet/mac/core/last_be.py index da55faee..555e523f 100644 --- a/liteethernet/mac/core/last_be.py +++ b/liteethernet/mac/core/last_be.py @@ -2,7 +2,8 @@ from migen.fhdl.std import * 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): diff --git a/liteethernet/mac/core/preamble.py b/liteethernet/mac/core/preamble.py index fe2078fe..f1c76034 100644 --- a/liteethernet/mac/core/preamble.py +++ b/liteethernet/mac/core/preamble.py @@ -4,7 +4,8 @@ from migen.genlib.misc import chooser 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): diff --git a/liteethernet/mac/frontend/sram.py b/liteethernet/mac/frontend/sram.py index 32c76f3c..c51c0ecb 100644 --- a/liteethernet/mac/frontend/sram.py +++ b/liteethernet/mac/frontend/sram.py @@ -6,7 +6,8 @@ from migen.flow.actor import Sink, Source 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): diff --git a/liteethernet/mac/frontend/wishbone.py b/liteethernet/mac/frontend/wishbone.py new file mode 100644 index 00000000..56601e12 --- /dev/null +++ b/liteethernet/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/liteethernet/mac/phy/__init__.py b/liteethernet/mac/phy/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/liteethernet/mac/phy/gmii.py b/liteethernet/mac/phy/gmii.py deleted file mode 100644 index ebdd0235..00000000 --- a/liteethernet/mac/phy/gmii.py +++ /dev/null @@ -1,76 +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 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 diff --git a/liteethernet/mac/phy/loopback.py b/liteethernet/mac/phy/loopback.py deleted file mode 100644 index 50c9f995..00000000 --- a/liteethernet/mac/phy/loopback.py +++ /dev/null @@ -1,33 +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 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) diff --git a/liteethernet/mac/phy/mii.py b/liteethernet/mac/phy/mii.py deleted file mode 100644 index 17f2ba7b..00000000 --- a/liteethernet/mac/phy/mii.py +++ /dev/null @@ -1,128 +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 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 diff --git a/liteethernet/phy/__init__.py b/liteethernet/phy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/liteethernet/phy/gmii.py b/liteethernet/phy/gmii.py new file mode 100644 index 00000000..c1b8243d --- /dev/null +++ b/liteethernet/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/liteethernet/phy/loopback.py b/liteethernet/phy/loopback.py new file mode 100644 index 00000000..04a51d07 --- /dev/null +++ b/liteethernet/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/liteethernet/phy/mii.py b/liteethernet/phy/mii.py new file mode 100644 index 00000000..a58b52b5 --- /dev/null +++ b/liteethernet/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