mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
soc/cores: remove liteeth_mini and use liteeth
This commit is contained in:
parent
16ba646b1b
commit
a2aa5726bf
24 changed files with 7 additions and 1808 deletions
|
@ -13,9 +13,9 @@ from litex.soc.integration.soc_core import mem_decoder
|
|||
from litex.soc.integration.soc_sdram import *
|
||||
from litex.soc.integration.builder import *
|
||||
|
||||
# TODO: use liteeth
|
||||
from litex.soc.cores.liteeth_mini.phy import LiteEthPHY
|
||||
from litex.soc.cores.liteeth_mini.mac import LiteEthMAC
|
||||
from liteeth.phy import LiteEthPHY
|
||||
from liteeth.core.mac import LiteEthMAC
|
||||
|
||||
|
||||
class _CRG(Module):
|
||||
def __init__(self, platform):
|
||||
|
|
|
@ -14,8 +14,8 @@ from litex.soc.cores.sdram.settings import PhySettings, IS42S16160
|
|||
from litex.soc.cores.sdram.model import SDRAMPHYModel
|
||||
from litex.soc.integration.soc_core import mem_decoder
|
||||
|
||||
from litex.soc.cores.liteeth_mini.phy.model import LiteEthPHYModel
|
||||
from litex.soc.cores.liteeth_mini.mac import LiteEthMAC
|
||||
from liteeth.phy.model import LiteEthPHYModel
|
||||
from liteeth.core.mac import LiteEthMAC
|
||||
|
||||
class BaseSoC(SoCSDRAM):
|
||||
def __init__(self, **kwargs):
|
||||
|
|
|
@ -9,10 +9,8 @@ from litex.gen.genlib.io import CRG
|
|||
from litex.soc.integration.soc_core import *
|
||||
from litex.soc.integration.builder import *
|
||||
|
||||
# TODO: use liteeth
|
||||
from litex.soc.cores.liteeth_mini.phy import LiteEthPHY
|
||||
from litex.soc.cores.liteeth_mini.mac import LiteEthMAC
|
||||
|
||||
from liteeth.phy import LiteEthPHY
|
||||
from liteeth.core.mac import LiteEthMAC
|
||||
|
||||
class BaseSoC(SoCCore):
|
||||
def __init__(self, platform, **kwargs):
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Other authors retain ownership of their contributions. If a submission can
|
||||
reasonably be considered independently copyrightable, it's yours and we
|
||||
encourage you to claim it with appropriate copyright notices. This submission
|
||||
then falls under the "otherwise noted" category. All submissions are strongly
|
||||
encouraged to use the two-clause BSD license reproduced above.
|
|
@ -1,58 +0,0 @@
|
|||
__ _ __ ______ __ __ ____ _
|
||||
/ / (_) /____ / __/ /_/ / / |/ (_)__ (_)
|
||||
/ /__/ / __/ -_) _// __/ _ \/ /|_/ / / _ \/ /
|
||||
/____/_/\__/\__/___/\__/_//_/_/ /_/_/_//_/_/
|
||||
|
||||
Copyright 2012-2015 / EnjoyDigital / M-Labs Ltd
|
||||
|
||||
A small footprint and configurable minimal Ethernet core
|
||||
powered by Migen
|
||||
|
||||
[> Intro
|
||||
---------
|
||||
LiteEthMini is a subset of LiteEth (https://github.com/enjoy-digital/liteeth)
|
||||
intended to be used with a CPU and a software stack.
|
||||
|
||||
[> Features
|
||||
-----------
|
||||
- Ethernet MAC with various various PHYs (GMII, MII, RGMII, Loopback)
|
||||
- SRAM storage and wishbone interface
|
||||
|
||||
[> Possible improvements
|
||||
-------------------------
|
||||
- add DMA interface to MAC
|
||||
- add SGMII PHY
|
||||
- ... See below Support and consulting :)
|
||||
|
||||
If you want to support these features, please contact us at florent [AT]
|
||||
enjoy-digital.fr. You can also contact our partner on the public mailing list
|
||||
devel [AT] lists.m-labs.hk.
|
||||
|
||||
[> License
|
||||
-----------
|
||||
LiteEthMini is released under the very permissive two-clause BSD license. Under
|
||||
the terms of this license, you are authorized to use LiteEthMini 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 LiteEthMini
|
||||
- cite LiteEthMini 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 LiteEthMini.
|
||||
|
||||
[> Support and consulting
|
||||
--------------------------
|
||||
We love open-source hardware and like sharing our designs with others.
|
||||
|
||||
LiteEthMini is mainly developed and maintained by EnjoyDigital.
|
||||
|
||||
If you would like to know more about LiteEthMini 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.
|
||||
|
||||
So feel free to contact us, we'd love to work with you! (and eventually shorten
|
||||
the list of the possible improvements :)
|
||||
|
||||
[> Contact
|
||||
E-mail: florent [AT] enjoy-digital.fr
|
|
@ -1,38 +0,0 @@
|
|||
from litex.gen import *
|
||||
from litex.gen.genlib.record import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect.stream import *
|
||||
|
||||
|
||||
class Port:
|
||||
def connect(self, port):
|
||||
r = [
|
||||
Record.connect(self.source, port.sink),
|
||||
Record.connect(port.source, self.sink)
|
||||
]
|
||||
return r
|
||||
|
||||
eth_mtu = 1532
|
||||
eth_min_len = 46
|
||||
eth_interpacket_gap = 12
|
||||
eth_preamble = 0xD555555555555555
|
||||
buffer_depth = 2**log2_int(eth_mtu, need_pow2=False)
|
||||
|
||||
|
||||
def eth_phy_description(dw):
|
||||
payload_layout = [
|
||||
("data", dw),
|
||||
("last_be", dw//8),
|
||||
("error", dw//8)
|
||||
]
|
||||
return EndpointDescription(payload_layout, packetized=True)
|
||||
|
||||
|
||||
def eth_mac_description(dw):
|
||||
payload_layout = mac_header.get_layout() + [
|
||||
("data", dw),
|
||||
("last_be", dw//8),
|
||||
("error", dw//8)
|
||||
]
|
||||
return EndpointDescription(payload_layout, packetized=True)
|
|
@ -1,25 +0,0 @@
|
|||
from litex.gen import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
from litex.soc.cores.liteeth_mini.mac.core import LiteEthMACCore
|
||||
from litex.soc.cores.liteeth_mini.mac.frontend.wishbone import LiteEthMACWishboneInterface
|
||||
|
||||
|
||||
class LiteEthMAC(Module, AutoCSR):
|
||||
def __init__(self, phy, dw,
|
||||
interface="wishbone",
|
||||
endianness="big",
|
||||
with_preamble_crc=True):
|
||||
self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc)
|
||||
self.csrs = []
|
||||
if interface == "wishbone":
|
||||
self.submodules.interface = LiteEthMACWishboneInterface(dw, 2, 2)
|
||||
self.comb += Port.connect(self.interface, self.core)
|
||||
self.ev, self.bus = self.interface.sram.ev, self.interface.bus
|
||||
self.csrs = self.interface.get_csrs() + self.core.get_csrs()
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_csrs(self):
|
||||
return self.csrs
|
|
@ -1,100 +0,0 @@
|
|||
from litex.gen import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
from litex.soc.cores.liteeth_mini.mac.core import gap, preamble, crc, padding, last_be
|
||||
from litex.soc.cores.liteeth_mini.phy.mii import LiteEthPHYMII
|
||||
|
||||
|
||||
class LiteEthMACCore(Module, AutoCSR):
|
||||
def __init__(self, phy, dw, endianness="big",
|
||||
with_preamble_crc=True,
|
||||
with_padding=True):
|
||||
if dw < phy.dw:
|
||||
raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw))
|
||||
|
||||
rx_pipeline = [phy]
|
||||
tx_pipeline = [phy]
|
||||
|
||||
# Interpacket gap
|
||||
tx_gap_inserter = gap.LiteEthMACGap(phy.dw)
|
||||
rx_gap_checker = gap.LiteEthMACGap(phy.dw, ack_on_gap=True)
|
||||
self.submodules += ClockDomainsRenamer("eth_tx")(tx_gap_inserter)
|
||||
self.submodules += ClockDomainsRenamer("eth_rx")(rx_gap_checker)
|
||||
|
||||
tx_pipeline += [tx_gap_inserter]
|
||||
rx_pipeline += [rx_gap_checker]
|
||||
|
||||
# Preamble / CRC
|
||||
if with_preamble_crc:
|
||||
self._preamble_crc = CSRStatus(reset=1)
|
||||
# Preamble insert/check
|
||||
preamble_inserter = preamble.LiteEthMACPreambleInserter(phy.dw)
|
||||
preamble_checker = preamble.LiteEthMACPreambleChecker(phy.dw)
|
||||
self.submodules += ClockDomainsRenamer("eth_tx")(preamble_inserter)
|
||||
self.submodules += ClockDomainsRenamer("eth_rx")(preamble_checker)
|
||||
|
||||
# CRC insert/check
|
||||
crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw))
|
||||
crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw))
|
||||
self.submodules += ClockDomainsRenamer("eth_tx")(crc32_inserter)
|
||||
self.submodules += ClockDomainsRenamer("eth_rx")(crc32_checker)
|
||||
|
||||
tx_pipeline += [preamble_inserter, crc32_inserter]
|
||||
rx_pipeline += [preamble_checker, crc32_checker]
|
||||
|
||||
# Padding
|
||||
if with_padding:
|
||||
padding_inserter = padding.LiteEthMACPaddingInserter(phy.dw, 60)
|
||||
padding_checker = padding.LiteEthMACPaddingChecker(phy.dw, 60)
|
||||
self.submodules += ClockDomainsRenamer("eth_tx")(padding_inserter)
|
||||
self.submodules += ClockDomainsRenamer("eth_rx")(padding_checker)
|
||||
|
||||
tx_pipeline += [padding_inserter]
|
||||
rx_pipeline += [padding_checker]
|
||||
|
||||
# Delimiters
|
||||
if dw != 8:
|
||||
tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw)
|
||||
rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw)
|
||||
self.submodules += ClockDomainsRenamer("eth_tx")(tx_last_be)
|
||||
self.submodules += ClockDomainsRenamer("eth_rx")(rx_last_be)
|
||||
|
||||
tx_pipeline += [tx_last_be]
|
||||
rx_pipeline += [rx_last_be]
|
||||
|
||||
# Converters
|
||||
if dw != phy.dw:
|
||||
reverse = endianness == "big"
|
||||
tx_converter = Converter(eth_phy_description(dw),
|
||||
eth_phy_description(phy.dw),
|
||||
reverse=reverse)
|
||||
rx_converter = Converter(eth_phy_description(phy.dw),
|
||||
eth_phy_description(dw),
|
||||
reverse=reverse)
|
||||
self.submodules += ClockDomainsRenamer("eth_tx")(tx_converter)
|
||||
self.submodules += ClockDomainsRenamer("eth_rx")(rx_converter)
|
||||
|
||||
tx_pipeline += [tx_converter]
|
||||
rx_pipeline += [rx_converter]
|
||||
|
||||
# Cross Domain Crossing
|
||||
if isinstance(phy, LiteEthPHYMII):
|
||||
fifo_depth = 8
|
||||
else:
|
||||
fifo_depth = 64
|
||||
tx_cdc = AsyncFIFO(eth_phy_description(dw), fifo_depth)
|
||||
rx_cdc = AsyncFIFO(eth_phy_description(dw), fifo_depth)
|
||||
self.submodules += ClockDomainsRenamer({"write": "sys", "read": "eth_tx"})(tx_cdc)
|
||||
self.submodules += ClockDomainsRenamer({"write": "eth_rx", "read": "sys"})(rx_cdc)
|
||||
|
||||
tx_pipeline += [tx_cdc]
|
||||
rx_pipeline += [rx_cdc]
|
||||
|
||||
tx_pipeline_r = list(reversed(tx_pipeline))
|
||||
for s, d in zip(tx_pipeline_r, tx_pipeline_r[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
for s, d in zip(rx_pipeline, rx_pipeline[1:]):
|
||||
self.comb += s.source.connect(d.sink)
|
||||
self.sink = tx_pipeline[-1].sink
|
||||
self.source = rx_pipeline[-1].source
|
|
@ -1,287 +0,0 @@
|
|||
from collections import OrderedDict
|
||||
from functools import reduce
|
||||
from operator import xor
|
||||
|
||||
from litex.gen import *
|
||||
from litex.gen.genlib.misc import chooser
|
||||
|
||||
from litex.soc.interconnect.stream import *
|
||||
|
||||
|
||||
class LiteEthMACCRCEngine(Module):
|
||||
"""Cyclic Redundancy Check Engine
|
||||
|
||||
Compute next CRC value from last CRC value and data input using
|
||||
an optimized asynchronous LFSR.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_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
|
||||
----------
|
||||
data : in
|
||||
Data input.
|
||||
last : in
|
||||
last CRC value.
|
||||
next :
|
||||
next CRC value.
|
||||
"""
|
||||
def __init__(self, data_width, width, polynom):
|
||||
self.data = Signal(data_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(data_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.data[n]]
|
||||
self.comb += self.next[i].eq(reduce(xor, xors))
|
||||
|
||||
|
||||
@ResetInserter()
|
||||
@CEInserter()
|
||||
class LiteEthMACCRC32(Module):
|
||||
"""IEEE 802.3 CRC
|
||||
|
||||
Implement an IEEE 802.3 CRC generator/checker.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_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, data_width):
|
||||
self.data = Signal(data_width)
|
||||
self.value = Signal(self.width)
|
||||
self.error = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom)
|
||||
reg = Signal(self.width, reset=self.init)
|
||||
self.sync += reg.eq(self.engine.next)
|
||||
self.comb += [
|
||||
self.engine.data.eq(self.data),
|
||||
self.engine.last.eq(reg),
|
||||
|
||||
self.value.eq(~reg[::-1]),
|
||||
self.error.eq(self.engine.next != self.check)
|
||||
]
|
||||
|
||||
|
||||
class LiteEthMACCRCInserter(Module):
|
||||
"""CRC Inserter
|
||||
|
||||
Append a CRC at the end of each packet.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
description : description
|
||||
description of the dataflow.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
sink : in
|
||||
Packets input without CRC.
|
||||
source : out
|
||||
Packets output with CRC.
|
||||
"""
|
||||
def __init__(self, crc_class, description):
|
||||
self.sink = sink = Sink(description)
|
||||
self.source = source = Source(description)
|
||||
self.busy = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
dw = len(sink.data)
|
||||
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.data.eq(sink.data),
|
||||
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.data, 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.data.eq(crc.value),
|
||||
If(source.ack, NextState("IDLE"))
|
||||
)
|
||||
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
|
||||
class LiteEthMACCRC32Inserter(LiteEthMACCRCInserter):
|
||||
def __init__(self, description):
|
||||
LiteEthMACCRCInserter.__init__(self, LiteEthMACCRC32, description)
|
||||
|
||||
|
||||
class LiteEthMACCRCChecker(Module):
|
||||
"""CRC Checker
|
||||
|
||||
Check CRC at the end of each packet.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
description : description
|
||||
description 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, description):
|
||||
self.sink = sink = Sink(description)
|
||||
self.source = source = Source(description)
|
||||
self.busy = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
dw = len(sink.data)
|
||||
crc = crc_class(dw)
|
||||
self.submodules += crc
|
||||
ratio = crc.width//dw
|
||||
|
||||
fifo = ResetInserter()(SyncFIFO(description, 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"),
|
||||
)
|
||||
self.comb += crc.data.eq(sink.data)
|
||||
fsm.act("IDLE",
|
||||
If(sink.stb & sink.sop & sink.ack,
|
||||
crc.ce.eq(1),
|
||||
NextState("COPY")
|
||||
)
|
||||
)
|
||||
fsm.act("COPY",
|
||||
If(sink.stb & sink.ack,
|
||||
crc.ce.eq(1),
|
||||
If(sink.eop,
|
||||
NextState("RESET")
|
||||
)
|
||||
)
|
||||
)
|
||||
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
|
||||
|
||||
|
||||
class LiteEthMACCRC32Checker(LiteEthMACCRCChecker):
|
||||
def __init__(self, description):
|
||||
LiteEthMACCRCChecker.__init__(self, LiteEthMACCRC32, description)
|
|
@ -1,42 +0,0 @@
|
|||
import math
|
||||
|
||||
from litex.gen import *
|
||||
from litex.gen.genlib.fsm import *
|
||||
|
||||
from litex.soc.interconnect.stream import Sink, Source
|
||||
from litex.soc.cores.liteeth_mini.common import eth_phy_description, eth_interpacket_gap
|
||||
|
||||
|
||||
class LiteEthMACGap(Module):
|
||||
def __init__(self, dw, ack_on_gap=False):
|
||||
self.sink = sink = Sink(eth_phy_description(dw))
|
||||
self.source = source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
gap = math.ceil(eth_interpacket_gap/(dw//8))
|
||||
counter = Signal(max=gap)
|
||||
counter_reset = Signal()
|
||||
counter_ce = Signal()
|
||||
self.sync += \
|
||||
If(counter_reset,
|
||||
counter.eq(0)
|
||||
).Elif(counter_ce,
|
||||
counter.eq(counter + 1)
|
||||
)
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="COPY")
|
||||
fsm.act("COPY",
|
||||
counter_reset.eq(1),
|
||||
Record.connect(sink, source),
|
||||
If(sink.stb & sink.eop & sink.ack,
|
||||
NextState("GAP")
|
||||
)
|
||||
)
|
||||
fsm.act("GAP",
|
||||
counter_ce.eq(1),
|
||||
sink.ack.eq(int(ack_on_gap)),
|
||||
If(counter == (gap-1),
|
||||
NextState("COPY")
|
||||
)
|
||||
)
|
|
@ -1,46 +0,0 @@
|
|||
from litex.gen import *
|
||||
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import eth_phy_description
|
||||
|
||||
|
||||
class LiteEthMACTXLastBE(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = sink = Sink(eth_phy_description(dw))
|
||||
self.source = source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
ongoing = Signal()
|
||||
self.sync += \
|
||||
If(sink.stb & sink.ack,
|
||||
If(sink.sop,
|
||||
ongoing.eq(1)
|
||||
).Elif(sink.last_be,
|
||||
ongoing.eq(0)
|
||||
)
|
||||
)
|
||||
self.comb += [
|
||||
source.stb.eq(sink.stb & (sink.sop | ongoing)),
|
||||
source.sop.eq(sink.sop),
|
||||
source.eop.eq(sink.last_be),
|
||||
source.data.eq(sink.data),
|
||||
sink.ack.eq(source.ack)
|
||||
]
|
||||
|
||||
|
||||
class LiteEthMACRXLastBE(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = sink = Sink(eth_phy_description(dw))
|
||||
self.source = source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
self.comb += [
|
||||
source.stb.eq(sink.stb),
|
||||
source.sop.eq(sink.sop),
|
||||
source.eop.eq(sink.eop),
|
||||
source.data.eq(sink.data),
|
||||
source.last_be.eq(sink.eop),
|
||||
sink.ack.eq(source.ack)
|
||||
]
|
|
@ -1,68 +0,0 @@
|
|||
import math
|
||||
|
||||
from litex.gen import *
|
||||
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import eth_phy_description
|
||||
|
||||
|
||||
class LiteEthMACPaddingInserter(Module):
|
||||
def __init__(self, dw, padding):
|
||||
self.sink = sink = Sink(eth_phy_description(dw))
|
||||
self.source = source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
padding_limit = math.ceil(padding/(dw/8))-1
|
||||
|
||||
counter = Signal(16, reset=1)
|
||||
counter_done = Signal()
|
||||
counter_reset = Signal()
|
||||
counter_ce = Signal()
|
||||
self.sync += If(counter_reset,
|
||||
counter.eq(1)
|
||||
).Elif(counter_ce,
|
||||
counter.eq(counter + 1)
|
||||
)
|
||||
self.comb += [
|
||||
counter_reset.eq(sink.stb & sink.sop & sink.ack),
|
||||
counter_ce.eq(source.stb & source.ack),
|
||||
counter_done.eq(counter >= padding_limit),
|
||||
]
|
||||
|
||||
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||
fsm.act("IDLE",
|
||||
Record.connect(sink, source),
|
||||
If(source.stb & source.ack,
|
||||
counter_ce.eq(1),
|
||||
If(sink.eop,
|
||||
If(~counter_done,
|
||||
source.eop.eq(0),
|
||||
NextState("PADDING")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("PADDING",
|
||||
source.stb.eq(1),
|
||||
source.eop.eq(counter_done),
|
||||
source.data.eq(0),
|
||||
If(source.ack,
|
||||
If(counter_done,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class LiteEthMACPaddingChecker(Module):
|
||||
def __init__(self, dw, packet_min_length):
|
||||
self.sink = sink = Sink(eth_phy_description(dw))
|
||||
self.source = source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
# TODO: see if we should drop the packet when
|
||||
# payload size < minimum ethernet payload size
|
||||
self.comb += Record.connect(sink, source)
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
from litex.gen import *
|
||||
from litex.gen.genlib.fsm import *
|
||||
from litex.gen.genlib.misc import chooser
|
||||
from litex.gen.genlib.record import Record
|
||||
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import eth_phy_description, eth_preamble
|
||||
|
||||
|
||||
class LiteEthMACPreambleInserter(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = Sink(eth_phy_description(dw))
|
||||
self.source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
preamble = Signal(64, reset=eth_preamble)
|
||||
cnt_max = (64//dw)-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.data),
|
||||
If(cnt == cnt_max,
|
||||
If(self.source.ack, NextState("COPY"))
|
||||
).Else(
|
||||
inc_cnt.eq(self.source.ack)
|
||||
)
|
||||
)
|
||||
|
||||
self.comb += [
|
||||
self.source.data.eq(self.sink.data),
|
||||
self.source.last_be.eq(self.sink.last_be)
|
||||
]
|
||||
fsm.act("COPY",
|
||||
Record.connect(self.sink, self.source, leave_out=set(["data", "last_be"])),
|
||||
self.source.sop.eq(0),
|
||||
|
||||
If(self.sink.stb & self.sink.eop & self.source.ack,
|
||||
NextState("IDLE"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class LiteEthMACPreambleChecker(Module):
|
||||
def __init__(self, dw):
|
||||
self.sink = Sink(eth_phy_description(dw))
|
||||
self.source = Source(eth_phy_description(dw))
|
||||
|
||||
# # #
|
||||
|
||||
preamble = Signal(64, reset=eth_preamble)
|
||||
cnt_max = (64//dw) - 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(dw)
|
||||
match = Signal()
|
||||
self.comb += [
|
||||
chooser(preamble, cnt, ref),
|
||||
match.eq(self.sink.data == 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
self.comb += [
|
||||
self.source.data.eq(self.sink.data),
|
||||
self.source.last_be.eq(self.sink.last_be)
|
||||
]
|
||||
fsm.act("COPY",
|
||||
Record.connect(self.sink, self.source, leave_out=set(["data", "last_be"])),
|
||||
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"),
|
||||
)
|
||||
)
|
|
@ -1,252 +0,0 @@
|
|||
from litex.soc import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect.csr_eventmanager import *
|
||||
from litex.soc.interconnect.stream import *
|
||||
|
||||
from litex.soc.cores.liteeth_mini.common import eth_phy_description
|
||||
|
||||
|
||||
class LiteEthMACSRAMWriter(Module, AutoCSR):
|
||||
def __init__(self, dw, depth, nslots=2):
|
||||
self.sink = sink = Sink(eth_phy_description(dw))
|
||||
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
|
||||
increment = Signal(3)
|
||||
self.comb += \
|
||||
If(sink.last_be[3],
|
||||
increment.eq(1)
|
||||
).Elif(sink.last_be[2],
|
||||
increment.eq(2)
|
||||
).Elif(sink.last_be[1],
|
||||
increment.eq(3)
|
||||
).Else(
|
||||
increment.eq(4)
|
||||
)
|
||||
counter = Signal(lengthbits)
|
||||
counter_reset = Signal()
|
||||
counter_ce = Signal()
|
||||
self.sync += If(counter_reset,
|
||||
counter.eq(0)
|
||||
).Elif(counter_ce,
|
||||
counter.eq(counter + increment)
|
||||
)
|
||||
|
||||
# slot computation
|
||||
slot = Signal(slotbits)
|
||||
slot_ce = Signal()
|
||||
self.sync += If(slot_ce, slot.eq(slot + 1))
|
||||
|
||||
ongoing = 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",
|
||||
If(sink.stb & sink.sop,
|
||||
If(fifo.sink.ack,
|
||||
ongoing.eq(1),
|
||||
counter_ce.eq(1),
|
||||
NextState("WRITE")
|
||||
)
|
||||
)
|
||||
)
|
||||
fsm.act("WRITE",
|
||||
counter_ce.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",
|
||||
counter_reset.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
self.comb += [
|
||||
fifo.sink.slot.eq(slot),
|
||||
fifo.sink.length.eq(counter)
|
||||
]
|
||||
fsm.act("TERMINATE",
|
||||
counter_reset.eq(1),
|
||||
slot_ce.eq(1),
|
||||
fifo.sink.stb.eq(1),
|
||||
NextState("IDLE")
|
||||
)
|
||||
self.comb += [
|
||||
fifo.source.ack.eq(self.ev.available.clear),
|
||||
self.ev.available.trigger.eq(fifo.source.stb),
|
||||
self._slot.status.eq(fifo.source.slot),
|
||||
self._length.status.eq(fifo.source.length),
|
||||
]
|
||||
|
||||
# memory
|
||||
mems = [None]*nslots
|
||||
ports = [None]*nslots
|
||||
for n in range(nslots):
|
||||
mems[n] = Memory(dw, 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(counter[2:]),
|
||||
ports[n].dat_w.eq(sink.data),
|
||||
If(sink.stb & ongoing,
|
||||
ports[n].we.eq(0xf)
|
||||
)
|
||||
]
|
||||
self.comb += Case(slot, cases)
|
||||
|
||||
|
||||
class LiteEthMACSRAMReader(Module, AutoCSR):
|
||||
def __init__(self, dw, depth, nslots=2):
|
||||
self.source = source = Source(eth_phy_description(dw))
|
||||
|
||||
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.sink.stb.eq(self._start.re),
|
||||
fifo.sink.slot.eq(self._slot.storage),
|
||||
fifo.sink.length.eq(self._length.storage),
|
||||
self._ready.status.eq(fifo.sink.ack)
|
||||
]
|
||||
|
||||
# length computation
|
||||
counter = Signal(lengthbits)
|
||||
counter_reset = Signal()
|
||||
counter_ce = Signal()
|
||||
self.sync += If(counter_reset,
|
||||
counter.eq(0)
|
||||
).Elif(counter_ce,
|
||||
counter.eq(counter + 4)
|
||||
)
|
||||
|
||||
|
||||
# fsm
|
||||
first = Signal()
|
||||
last = Signal()
|
||||
last_d = Signal()
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
counter_reset.eq(1),
|
||||
If(fifo.source.stb,
|
||||
NextState("CHECK")
|
||||
)
|
||||
)
|
||||
fsm.act("CHECK",
|
||||
If(~last_d,
|
||||
NextState("SEND"),
|
||||
).Else(
|
||||
NextState("END"),
|
||||
)
|
||||
)
|
||||
length_lsb = fifo.source.length[0:2]
|
||||
self.comb += [
|
||||
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)
|
||||
)
|
||||
)
|
||||
]
|
||||
fsm.act("SEND",
|
||||
source.stb.eq(1),
|
||||
source.sop.eq(first),
|
||||
source.eop.eq(last),
|
||||
If(source.ack,
|
||||
counter_ce.eq(~last),
|
||||
NextState("CHECK")
|
||||
)
|
||||
)
|
||||
fsm.act("END",
|
||||
fifo.source.ack.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((counter + 4) >= fifo.source.length)
|
||||
self.sync += last_d.eq(last)
|
||||
|
||||
# memory
|
||||
rd_slot = fifo.source.slot
|
||||
|
||||
mems = [None]*nslots
|
||||
ports = [None]*nslots
|
||||
for n in range(nslots):
|
||||
mems[n] = Memory(dw, 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(counter[2:])
|
||||
cases[n] = [source.data.eq(port.dat_r)]
|
||||
self.comb += Case(rd_slot, cases)
|
||||
|
||||
|
||||
class LiteEthMACSRAM(Module, AutoCSR):
|
||||
def __init__(self, dw, depth, nrxslots, ntxslots):
|
||||
self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots)
|
||||
self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots)
|
||||
self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev)
|
||||
self.sink, self.source = self.writer.sink, self.reader.source
|
|
@ -1,44 +0,0 @@
|
|||
from litex.gen import *
|
||||
from litex.gen.fhdl.simplify import FullMemoryWE
|
||||
|
||||
from litex.soc.interconnect import wishbone
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import eth_phy_description, buffer_depth
|
||||
from litex.soc.cores.liteeth_mini.mac.frontend import sram
|
||||
|
||||
|
||||
class LiteEthMACWishboneInterface(Module, AutoCSR):
|
||||
def __init__(self, dw, nrxslots=2, ntxslots=2):
|
||||
self.sink = Sink(eth_phy_description(dw))
|
||||
self.source = Source(eth_phy_description(dw))
|
||||
self.bus = wishbone.Interface()
|
||||
|
||||
# # #
|
||||
|
||||
# storage in SRAM
|
||||
sram_depth = buffer_depth//(dw//8)
|
||||
self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots)
|
||||
self.comb += [
|
||||
Record.connect(self.sink, self.sram.sink),
|
||||
Record.connect(self.sram.source, self.source)
|
||||
]
|
||||
|
||||
# Wishbone 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
|
|
@ -1,23 +0,0 @@
|
|||
from litex.soc.cores.liteeth_mini.common import *
|
||||
|
||||
|
||||
def LiteEthPHY(clock_pads, pads, clk_freq=None, **kwargs):
|
||||
# Autodetect PHY
|
||||
if hasattr(clock_pads, "gtx") and len(pads.tx_data) == 8:
|
||||
if hasattr(clock_pads, "tx"):
|
||||
# This is a 10/100/1G PHY
|
||||
from litex.soc.cores.liteeth_mini.phy.gmii_mii import LiteEthPHYGMIIMII
|
||||
return LiteEthPHYGMIIMII(clock_pads, pads, clk_freq=clk_freq, **kwargs)
|
||||
else:
|
||||
# This is a pure 1G PHY
|
||||
from litex.soc.cores.liteeth_mini.phy.gmii import LiteEthPHYGMII
|
||||
return LiteEthPHYGMII(clock_pads, pads, **kwargs)
|
||||
elif hasattr(pads, "rx_ctl"):
|
||||
# This is a 10/100/1G RGMII PHY
|
||||
raise ValueError("RGMII PHYs are specific to vendors (for now), use direct instantiation")
|
||||
elif len(pads.tx_data) == 4:
|
||||
# This is a MII PHY
|
||||
from litex.soc.cores.liteeth_mini.phy.mii import LiteEthPHYMII
|
||||
return LiteEthPHYMII(clock_pads, pads, **kwargs)
|
||||
else:
|
||||
raise ValueError("Unable to autodetect PHY from platform file, use direct instantiation")
|
|
@ -1,98 +0,0 @@
|
|||
from litex.gen import *
|
||||
from litex.gen.genlib.io import DDROutput
|
||||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
|
||||
|
||||
class LiteEthPHYGMIITX(Module):
|
||||
def __init__(self, pads, pads_register=True):
|
||||
self.sink = sink = Sink(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
if hasattr(pads, "tx_er"):
|
||||
self.sync += pads.tx_er.eq(0)
|
||||
pads_eq = [
|
||||
pads.tx_en.eq(sink.stb),
|
||||
pads.tx_data.eq(sink.data)
|
||||
]
|
||||
if pads_register:
|
||||
self.sync += pads_eq
|
||||
else:
|
||||
self.comb += pads_eq
|
||||
self.comb += sink.ack.eq(1)
|
||||
|
||||
|
||||
class LiteEthPHYGMIIRX(Module):
|
||||
def __init__(self, pads):
|
||||
self.source = source = Source(eth_phy_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.data.eq(pads.rx_data)
|
||||
]
|
||||
self.comb += source.eop.eq(eop)
|
||||
|
||||
|
||||
class LiteEthPHYGMIICRG(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset, mii_mode=0):
|
||||
self._reset = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
self.clock_domains.cd_eth_rx = ClockDomain()
|
||||
self.clock_domains.cd_eth_tx = ClockDomain()
|
||||
|
||||
# RX : Let the synthesis tool insert the appropriate clock buffer
|
||||
self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
|
||||
|
||||
# TX : GMII: Drive clock_pads.gtx, clock_pads.tx unused
|
||||
# MII: Use PHY clock_pads.tx as eth_tx_clk, do not drive clock_pads.gtx
|
||||
self.specials += DDROutput(1, mii_mode, clock_pads.gtx, ClockSignal("eth_tx"))
|
||||
# XXX Xilinx specific, replace BUFGMUX with a generic clock buffer?
|
||||
self.specials += Instance("BUFGMUX",
|
||||
i_I0=self.cd_eth_rx.clk,
|
||||
i_I1=clock_pads.tx,
|
||||
i_S=mii_mode,
|
||||
o_O=self.cd_eth_tx.clk)
|
||||
|
||||
if with_hw_init_reset:
|
||||
reset = Signal()
|
||||
counter = Signal(max=512)
|
||||
counter_done = Signal()
|
||||
counter_ce = Signal()
|
||||
self.sync += If(counter_ce, counter.eq(counter + 1))
|
||||
self.comb += [
|
||||
counter_done.eq(counter == 256),
|
||||
counter_ce.eq(~counter_done),
|
||||
reset.eq(~counter_done | self._reset.storage)
|
||||
]
|
||||
else:
|
||||
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 LiteEthPHYGMII(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset=True):
|
||||
self.dw = 8
|
||||
self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset)
|
||||
self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYGMIITX(pads))
|
||||
self.submodules.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYGMIIRX(pads))
|
||||
self.sink, self.source = self.tx.sink, self.rx.source
|
|
@ -1,170 +0,0 @@
|
|||
from litex.gen import *
|
||||
from litex.gen.genlib.io import DDROutput
|
||||
from litex.gen.genlib.cdc import PulseSynchronizer
|
||||
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
from litex.soc.cores.liteeth_mini.phy.gmii import LiteEthPHYGMIICRG
|
||||
from litex.soc.cores.liteeth_mini.phy.mii import LiteEthPHYMIITX, LiteEthPHYMIIRX
|
||||
from litex.soc.cores.liteeth_mini.phy.gmii import LiteEthPHYGMIITX, LiteEthPHYGMIIRX
|
||||
|
||||
|
||||
modes = {
|
||||
"GMII": 0,
|
||||
"MII": 1
|
||||
}
|
||||
|
||||
tx_pads_layout = [("tx_er", 1), ("tx_en", 1), ("tx_data", 8)]
|
||||
rx_pads_layout = [("rx_er", 1), ("dv", 1), ("rx_data", 8)]
|
||||
|
||||
|
||||
class LiteEthPHYGMIIMIITX(Module):
|
||||
def __init__(self, pads, mode):
|
||||
self.sink = sink = Sink(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
gmii_tx_pads = Record(tx_pads_layout)
|
||||
gmii_tx = LiteEthPHYGMIITX(gmii_tx_pads, pads_register=False)
|
||||
self.submodules += gmii_tx
|
||||
|
||||
mii_tx_pads = Record(tx_pads_layout)
|
||||
mii_tx = LiteEthPHYMIITX(mii_tx_pads, pads_register=False)
|
||||
self.submodules += mii_tx
|
||||
|
||||
demux = Demultiplexer(eth_phy_description(8), 2)
|
||||
self.submodules += demux
|
||||
self.comb += [
|
||||
demux.sel.eq(mode == modes["MII"]),
|
||||
Record.connect(sink, demux.sink),
|
||||
Record.connect(demux.source0, gmii_tx.sink),
|
||||
Record.connect(demux.source1, mii_tx.sink),
|
||||
]
|
||||
|
||||
if hasattr(pads, "tx_er"):
|
||||
self.comb += pads.tx_er.eq(0)
|
||||
self.sync += [
|
||||
If(mode == modes["MII"],
|
||||
pads.tx_en.eq(mii_tx_pads.tx_en),
|
||||
pads.tx_data.eq(mii_tx_pads.tx_data),
|
||||
).Else(
|
||||
pads.tx_en.eq(gmii_tx_pads.tx_en),
|
||||
pads.tx_data.eq(gmii_tx_pads.tx_data),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class LiteEthPHYGMIIMIIRX(Module):
|
||||
def __init__(self, pads, mode):
|
||||
self.source = source = Source(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
pads_d = Record(rx_pads_layout)
|
||||
self.sync += [
|
||||
pads_d.dv.eq(pads.dv),
|
||||
pads_d.rx_data.eq(pads.rx_data)
|
||||
]
|
||||
|
||||
gmii_rx = LiteEthPHYGMIIRX(pads_d)
|
||||
self.submodules += gmii_rx
|
||||
|
||||
mii_rx = LiteEthPHYMIIRX(pads_d)
|
||||
self.submodules += mii_rx
|
||||
|
||||
mux = Multiplexer(eth_phy_description(8), 2)
|
||||
self.submodules += mux
|
||||
self.comb += [
|
||||
mux.sel.eq(mode == modes["MII"]),
|
||||
Record.connect(gmii_rx.source, mux.sink0),
|
||||
Record.connect(mii_rx.source, mux.sink1),
|
||||
Record.connect(mux.source, source)
|
||||
]
|
||||
|
||||
|
||||
class LiteEthGMIIMIIModeDetection(Module, AutoCSR):
|
||||
def __init__(self, clk_freq):
|
||||
self.mode = Signal()
|
||||
self._mode = CSRStatus()
|
||||
|
||||
# # #
|
||||
|
||||
mode = Signal()
|
||||
update_mode = Signal()
|
||||
self.sync += \
|
||||
If(update_mode,
|
||||
self.mode.eq(mode)
|
||||
)
|
||||
self.comb += self._mode.status.eq(self.mode)
|
||||
|
||||
# Principle:
|
||||
# sys_clk >= 125MHz
|
||||
# eth_rx <= 125Mhz
|
||||
# We generate ticks every 1024 clock cycles in eth_rx domain
|
||||
# and measure ticks period in sys_clk domain.
|
||||
|
||||
# Generate a tick every 1024 clock cycles (eth_rx clock domain)
|
||||
eth_tick = Signal()
|
||||
eth_counter = Signal(10)
|
||||
self.sync.eth_rx += eth_counter.eq(eth_counter + 1)
|
||||
self.comb += eth_tick.eq(eth_counter == 0)
|
||||
|
||||
# Synchronize tick (sys clock domain)
|
||||
sys_tick = Signal()
|
||||
eth_ps = PulseSynchronizer("eth_rx", "sys")
|
||||
self.comb += [
|
||||
eth_ps.i.eq(eth_tick),
|
||||
sys_tick.eq(eth_ps.o)
|
||||
]
|
||||
self.submodules += eth_ps
|
||||
|
||||
# sys_clk domain counter
|
||||
sys_counter = Signal(24)
|
||||
sys_counter_reset = Signal()
|
||||
sys_counter_ce = Signal()
|
||||
self.sync += [
|
||||
If(sys_counter_reset,
|
||||
sys_counter.eq(0)
|
||||
).Elif(sys_counter_ce,
|
||||
sys_counter.eq(sys_counter + 1)
|
||||
)
|
||||
]
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
sys_counter_reset.eq(1),
|
||||
If(sys_tick,
|
||||
NextState("COUNT")
|
||||
)
|
||||
)
|
||||
fsm.act("COUNT",
|
||||
sys_counter_ce.eq(1),
|
||||
If(sys_tick,
|
||||
NextState("DETECTION")
|
||||
)
|
||||
)
|
||||
fsm.act("DETECTION",
|
||||
update_mode.eq(1),
|
||||
# if freq < 125MHz-5% use MII mode
|
||||
If(sys_counter > int((clk_freq/125000000)*1024*1.05),
|
||||
mode.eq(1)
|
||||
# if freq >= 125MHz-5% use GMII mode
|
||||
).Else(
|
||||
mode.eq(0)
|
||||
),
|
||||
NextState("IDLE")
|
||||
)
|
||||
|
||||
|
||||
class LiteEthPHYGMIIMII(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, clk_freq, with_hw_init_reset=True):
|
||||
self.dw = 8
|
||||
# Note: we can use GMII CRG since it also handles tx clock pad used for MII
|
||||
self.submodules.mode_detection = LiteEthGMIIMIIModeDetection(clk_freq)
|
||||
mode = self.mode_detection.mode
|
||||
self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset, mode == modes["MII"])
|
||||
self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYGMIIMIITX(pads, mode))
|
||||
self.submodules.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYGMIIMIIRX(pads, mode))
|
||||
self.sink, self.source = self.tx.sink, self.rx.source
|
|
@ -1,35 +0,0 @@
|
|||
from litex.gen import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
from litex.soc.cores.liteeth.mini.generic import *
|
||||
|
||||
|
||||
class LiteEthPHYLoopbackCRG(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 LiteEthPHYLoopback(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.dw = 8
|
||||
self.submodules.crg = LiteEthLoopbackPHYCRG()
|
||||
self.sink = Sink(eth_phy_description(8))
|
||||
self.source = Source(eth_phy_description(8))
|
||||
self.comb += Record.connect(self.sink, self.source)
|
|
@ -1,110 +0,0 @@
|
|||
from litex.gen import *
|
||||
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
|
||||
|
||||
def converter_description(dw):
|
||||
payload_layout = [("data", dw)]
|
||||
return EndpointDescription(payload_layout, packetized=True)
|
||||
|
||||
|
||||
class LiteEthPHYMIITX(Module):
|
||||
def __init__(self, pads, pads_register=True):
|
||||
self.sink = sink = Sink(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
if hasattr(pads, "tx_er"):
|
||||
self.sync += pads.tx_er.eq(0)
|
||||
converter = Converter(converter_description(8),
|
||||
converter_description(4))
|
||||
self.submodules += converter
|
||||
self.comb += [
|
||||
converter.sink.stb.eq(sink.stb),
|
||||
converter.sink.data.eq(sink.data),
|
||||
sink.ack.eq(converter.sink.ack),
|
||||
converter.source.ack.eq(1)
|
||||
]
|
||||
pads_eq = [
|
||||
pads.tx_en.eq(converter.source.stb),
|
||||
pads.tx_data.eq(converter.source.data)
|
||||
]
|
||||
if pads_register:
|
||||
self.sync += pads_eq
|
||||
else:
|
||||
self.comb += pads_eq
|
||||
|
||||
|
||||
class LiteEthPHYMIIRX(Module):
|
||||
def __init__(self, pads):
|
||||
self.source = source = Source(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
sop = Signal(reset=1)
|
||||
sop_set = Signal()
|
||||
sop_clr = Signal()
|
||||
self.sync += If(sop_set, sop.eq(1)).Elif(sop_clr, sop.eq(0))
|
||||
|
||||
converter = Converter(converter_description(4),
|
||||
converter_description(8))
|
||||
converter = ResetInserter()(converter)
|
||||
self.submodules += converter
|
||||
|
||||
self.sync += [
|
||||
converter.reset.eq(~pads.dv),
|
||||
converter.sink.stb.eq(1),
|
||||
converter.sink.data.eq(pads.rx_data)
|
||||
]
|
||||
self.sync += [
|
||||
sop_set.eq(~pads.dv),
|
||||
sop_clr.eq(pads.dv)
|
||||
]
|
||||
self.comb += [
|
||||
converter.sink.sop.eq(sop),
|
||||
converter.sink.eop.eq(~pads.dv)
|
||||
]
|
||||
self.comb += Record.connect(converter.source, source)
|
||||
|
||||
|
||||
class LiteEthPHYMIICRG(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset):
|
||||
self._reset = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
if hasattr(clock_pads, "phy"):
|
||||
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)
|
||||
|
||||
if with_hw_init_reset:
|
||||
reset = Signal()
|
||||
counter_done = Signal()
|
||||
self.submodules.counter = counter = Counter(max=512)
|
||||
self.comb += [
|
||||
counter_done.eq(counter.value == 256),
|
||||
counter.ce.eq(~counter_done),
|
||||
reset.eq(~counter_done | self._reset.storage)
|
||||
]
|
||||
else:
|
||||
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 LiteEthPHYMII(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset=True):
|
||||
self.dw = 8
|
||||
self.submodules.crg = LiteEthPHYMIICRG(clock_pads, pads, with_hw_init_reset)
|
||||
self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYMIITX(pads))
|
||||
self.submodules.rx = ClockDomainsRenamer("eth_tx")(LiteEthPHYMIIRX(pads))
|
||||
self.sink, self.source = self.tx.sink, self.rx.source
|
|
@ -1,58 +0,0 @@
|
|||
import os
|
||||
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
|
||||
|
||||
class LiteEthPHYModelCRG(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 LiteEthPHYModel(Module, AutoCSR):
|
||||
def __init__(self, pads, tap="tap0", ip_address="192.168.0.14"):
|
||||
self.dw = 8
|
||||
self.submodules.crg = LiteEthPHYModelCRG()
|
||||
self.sink = sink = Sink(eth_phy_description(8))
|
||||
self.source = source = Source(eth_phy_description(8))
|
||||
self.tap = tap
|
||||
self.ip_address = ip_address
|
||||
|
||||
self.comb += [
|
||||
pads.source_stb.eq(self.sink.stb),
|
||||
pads.source_data.eq(self.sink.data),
|
||||
self.sink.ack.eq(1)
|
||||
]
|
||||
|
||||
self.sync += [
|
||||
self.source.stb.eq(pads.sink_stb),
|
||||
self.source.sop.eq(pads.sink_stb & ~self.source.stb),
|
||||
self.source.data.eq(pads.sink_data),
|
||||
]
|
||||
self.comb += [
|
||||
self.source.eop.eq(~pads.sink_stb & self.source.stb),
|
||||
]
|
||||
|
||||
# TODO avoid use of os.system
|
||||
os.system("openvpn --mktun --dev {}".format(self.tap))
|
||||
os.system("ifconfig {} {} up".format(self.tap, self.ip_address))
|
||||
os.system("mknod /dev/net/{} c 10 200".format(self.tap))
|
||||
|
||||
def do_exit(self, *args, **kwargs):
|
||||
# TODO avoid use of os.system
|
||||
os.system("rm -f /dev/net/{}".format(self.tap))
|
||||
os.system("openvpn --rmtun --dev {}".format(self.tap))
|
|
@ -1,161 +0,0 @@
|
|||
# RGMII PHY for Spartan-6
|
||||
|
||||
from litex.gen import *
|
||||
from litex.gen.genlib.io import DDROutput
|
||||
from litex.gen.genlib.misc import WaitTimer
|
||||
from litex.gen.genlib.fsm import FSM, NextState
|
||||
|
||||
from litex.soc.interconnect.stream import *
|
||||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.cores.liteeth_mini.common import *
|
||||
|
||||
|
||||
class LiteEthPHYRGMIITX(Module):
|
||||
def __init__(self, pads, pads_register=True):
|
||||
self.sink = sink = Sink(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
self.specials += Instance("ODDR2",
|
||||
p_DDR_ALIGNMENT="C0", p_INIT=0, p_SRTYPE="ASYNC",
|
||||
i_C0=ClockSignal("eth_tx"), i_C1=~ClockSignal("eth_tx"),
|
||||
i_CE=1, i_S=0, i_R=0,
|
||||
i_D0=sink.stb, i_D1=sink.stb, o_Q=pads.tx_ctl,
|
||||
)
|
||||
for i in range(4):
|
||||
self.specials += Instance("ODDR2",
|
||||
p_DDR_ALIGNMENT="C0", p_INIT=0, p_SRTYPE="ASYNC",
|
||||
i_C0=ClockSignal("eth_tx"), i_C1=~ClockSignal("eth_tx"),
|
||||
i_CE=1, i_S=0, i_R=0,
|
||||
i_D0=sink.data[i], i_D1=sink.data[4+i], o_Q=pads.tx_data[i],
|
||||
)
|
||||
self.comb += sink.ack.eq(1)
|
||||
|
||||
|
||||
class LiteEthPHYRGMIIRX(Module):
|
||||
def __init__(self, pads):
|
||||
self.source = source = Source(eth_phy_description(8))
|
||||
|
||||
# # #
|
||||
|
||||
rx_ctl = Signal()
|
||||
rx_data = Signal(8)
|
||||
|
||||
self.specials += Instance("IDDR2",
|
||||
p_DDR_ALIGNMENT="C0", p_INIT_Q0=0, p_INIT_Q1=0, p_SRTYPE="ASYNC",
|
||||
i_C0=ClockSignal("eth_rx"), i_C1=~ClockSignal("eth_rx"),
|
||||
i_CE=1, i_S=0, i_R=0,
|
||||
i_D=pads.rx_ctl, o_Q1=rx_ctl,
|
||||
)
|
||||
for i in range(4):
|
||||
self.specials += Instance("IDDR2",
|
||||
p_DDR_ALIGNMENT="C0", p_INIT_Q0=0, p_INIT_Q1=0, p_SRTYPE="ASYNC",
|
||||
i_C0=ClockSignal("eth_rx"), i_C1=~ClockSignal("eth_rx"),
|
||||
i_CE=1, i_S=0, i_R=0,
|
||||
i_D=pads.rx_data[i], o_Q0=rx_data[4+i], o_Q1=rx_data[i],
|
||||
)
|
||||
|
||||
|
||||
rx_ctl_d = Signal()
|
||||
self.sync += rx_ctl_d.eq(rx_ctl)
|
||||
|
||||
sop = Signal()
|
||||
eop = Signal()
|
||||
self.comb += [
|
||||
sop.eq(rx_ctl & ~rx_ctl_d),
|
||||
eop.eq(~rx_ctl & rx_ctl_d)
|
||||
]
|
||||
self.sync += [
|
||||
source.stb.eq(rx_ctl),
|
||||
source.sop.eq(sop),
|
||||
source.data.eq(rx_data)
|
||||
]
|
||||
self.comb += source.eop.eq(eop)
|
||||
|
||||
|
||||
class LiteEthPHYRGMIICRG(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset):
|
||||
self._reset = CSRStorage()
|
||||
|
||||
# # #
|
||||
|
||||
self.clock_domains.cd_eth_rx = ClockDomain()
|
||||
self.clock_domains.cd_eth_tx = ClockDomain()
|
||||
|
||||
|
||||
# RX
|
||||
dcm_reset = Signal()
|
||||
dcm_locked = Signal()
|
||||
|
||||
timer = WaitTimer(1024)
|
||||
fsm = FSM(reset_state="DCM_RESET")
|
||||
self.submodules += timer, fsm
|
||||
|
||||
fsm.act("DCM_RESET",
|
||||
dcm_reset.eq(1),
|
||||
timer.wait.eq(1),
|
||||
If(timer.done,
|
||||
timer.wait.eq(0),
|
||||
NextState("DCM_WAIT")
|
||||
)
|
||||
)
|
||||
fsm.act("DCM_WAIT",
|
||||
timer.wait.eq(1),
|
||||
If(timer.done,
|
||||
NextState("DCM_CHECK_LOCK")
|
||||
)
|
||||
)
|
||||
fsm.act("DCM_CHECK_LOCK",
|
||||
If(~dcm_locked,
|
||||
NextState("DCM_RESET")
|
||||
)
|
||||
)
|
||||
|
||||
clk90_rx = Signal()
|
||||
clk0_rx = Signal()
|
||||
clk0_rx_bufg = Signal()
|
||||
self.specials += Instance("DCM",
|
||||
i_CLKIN=clock_pads.rx,
|
||||
i_CLKFB=clk0_rx_bufg,
|
||||
o_CLK0=clk0_rx,
|
||||
o_CLK90=clk90_rx,
|
||||
o_LOCKED=dcm_locked,
|
||||
i_PSEN=0,
|
||||
i_PSCLK=0,
|
||||
i_PSINCDEC=0,
|
||||
i_RST=dcm_reset
|
||||
)
|
||||
|
||||
self.specials += Instance("BUFG", i_I=clk0_rx, o_O=clk0_rx_bufg)
|
||||
self.specials += Instance("BUFG", i_I=clk90_rx, o_O=self.cd_eth_rx.clk)
|
||||
|
||||
# TX
|
||||
self.specials += DDROutput(1, 0, clock_pads.tx, ClockSignal("eth_tx"))
|
||||
self.specials += Instance("BUFG", i_I=self.cd_eth_rx.clk, o_O=self.cd_eth_tx.clk)
|
||||
|
||||
# Reset
|
||||
if with_hw_init_reset:
|
||||
reset = Signal()
|
||||
counter_done = Signal()
|
||||
self.submodules.counter = counter = Counter(max=512)
|
||||
self.comb += [
|
||||
counter_done.eq(counter.value == 256),
|
||||
counter.ce.eq(~counter_done),
|
||||
reset.eq(~counter_done | self._reset.storage)
|
||||
]
|
||||
else:
|
||||
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 LiteEthPHYRGMII(Module, AutoCSR):
|
||||
def __init__(self, clock_pads, pads, with_hw_init_reset=True):
|
||||
self.dw = 8
|
||||
self.submodules.crg = LiteEthPHYRGMIICRG(clock_pads, pads, with_hw_init_reset)
|
||||
self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(pads))
|
||||
self.submodules.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(pads))
|
||||
self.sink, self.source = self.tx.sink, self.rx.source
|
Loading…
Reference in a new issue