commit
32de4f041e
|
@ -0,0 +1,44 @@
|
||||||
|
--- # PHY ----------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2023 LumiGuide Fietsdetectie B.V. <goemansrowan@gmail.com>
|
||||||
|
# License: BSD
|
||||||
|
|
||||||
|
phy: LiteEthECP5PHYRGMII
|
||||||
|
phy_tx_delay: 0e-9
|
||||||
|
phy_rx_delay: 2e-9
|
||||||
|
device: LFE5U-25F-6BG256C
|
||||||
|
vendor: lattice
|
||||||
|
toolchain: trellis
|
||||||
|
# Core -------------------------------------------------------------------------
|
||||||
|
clk_freq: 125e6
|
||||||
|
core: udp
|
||||||
|
|
||||||
|
mac_address: 0x10e2d5000000
|
||||||
|
ip_address: 172.30.0.1
|
||||||
|
|
||||||
|
tx_cdc_depth: 16
|
||||||
|
tx_cdc_buffered: True
|
||||||
|
rx_cdc_depth: 16
|
||||||
|
rx_cdc_buffered: True
|
||||||
|
# UDP Ports --------------------------------------------------------------------
|
||||||
|
# mode `raw` vs `streamer` mode:
|
||||||
|
# The streamer mode is a convenience wrapper around a `raw` UDP port. A raw UDP
|
||||||
|
# port receives and requires the full UDP header information without filtering.
|
||||||
|
# In addition, when transmitting packets, it's required to make sure the user
|
||||||
|
# can burst a full packet without issuing a stall.
|
||||||
|
# The `streamer` mode on the other hand, allows a port to be specified to
|
||||||
|
# receive/transmit on. There is also a FIFO between the raw port and the
|
||||||
|
# streamer port. This means the user is not required to be able to burst packet
|
||||||
|
# into the core. But a limitation of this is that the user relinquishes
|
||||||
|
# control of transmitted UDP packet sizes.
|
||||||
|
udp_ports:
|
||||||
|
raw:
|
||||||
|
data_width: 32
|
||||||
|
mode: raw
|
||||||
|
streamer1:
|
||||||
|
data_width: 32
|
||||||
|
port: 1337
|
||||||
|
mode: streamer
|
||||||
|
streamer2:
|
||||||
|
data_width: 32
|
||||||
|
port: 6077
|
||||||
|
mode: streamer
|
196
liteeth/gen.py
196
liteeth/gen.py
|
@ -50,6 +50,7 @@ from liteeth.mac import LiteEthMAC
|
||||||
from liteeth.core import LiteEthUDPIPCore
|
from liteeth.core import LiteEthUDPIPCore
|
||||||
from liteeth.core.dhcp import LiteEthDHCP
|
from liteeth.core.dhcp import LiteEthDHCP
|
||||||
|
|
||||||
|
from liteeth.frontend.stream import LiteEthUDPStreamer
|
||||||
from liteeth.frontend.etherbone import LiteEthEtherbone
|
from liteeth.frontend.etherbone import LiteEthEtherbone
|
||||||
|
|
||||||
# IOs ----------------------------------------------------------------------------------------------
|
# IOs ----------------------------------------------------------------------------------------------
|
||||||
|
@ -179,6 +180,36 @@ def get_udp_port_ios(name, data_width, dynamic_params=False):
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_udp_raw_port_ios(name, data_width):
|
||||||
|
return [
|
||||||
|
(f"{name}", 0,
|
||||||
|
|
||||||
|
# Sink.
|
||||||
|
Subsignal("sink_ip_address", Pins(32)),
|
||||||
|
Subsignal("sink_src_port", Pins(16)),
|
||||||
|
Subsignal("sink_dst_port", Pins(16)),
|
||||||
|
Subsignal("sink_valid", Pins(1)),
|
||||||
|
Subsignal("sink_length", Pins(16)),
|
||||||
|
Subsignal("sink_last", Pins(1)),
|
||||||
|
Subsignal("sink_ready", Pins(1)),
|
||||||
|
Subsignal("sink_data", Pins(data_width)),
|
||||||
|
Subsignal("sink_last_be", Pins(data_width//8)),
|
||||||
|
|
||||||
|
# Source.
|
||||||
|
Subsignal("source_ip_address", Pins(32)),
|
||||||
|
Subsignal("source_src_port", Pins(16)),
|
||||||
|
Subsignal("source_dst_port", Pins(16)),
|
||||||
|
Subsignal("source_valid", Pins(1)),
|
||||||
|
Subsignal("source_length", Pins(16)),
|
||||||
|
Subsignal("source_last", Pins(1)),
|
||||||
|
Subsignal("source_ready", Pins(1)),
|
||||||
|
Subsignal("source_data", Pins(data_width)),
|
||||||
|
Subsignal("source_last_be", Pins(data_width//8)),
|
||||||
|
Subsignal("source_error", Pins(1)),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# PHY Core -----------------------------------------------------------------------------------------
|
# PHY Core -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class PHYCore(SoCMini):
|
class PHYCore(SoCMini):
|
||||||
|
@ -339,9 +370,107 @@ class MACCore(PHYCore):
|
||||||
# UDP Core -----------------------------------------------------------------------------------------
|
# UDP Core -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class UDPCore(PHYCore):
|
class UDPCore(PHYCore):
|
||||||
def __init__(self, platform, core_config):
|
def add_streamer_port(self, platform, name, port_cfg):
|
||||||
from liteeth.frontend.stream import LiteEthUDPStreamer
|
# Use default Data-Width of 8-bit when not specified.
|
||||||
|
data_width = port_cfg.get("data_width", 8)
|
||||||
|
|
||||||
|
# Used dynamic UDP-Port/IP-Address when not specified.
|
||||||
|
dynamic_params = port_cfg.get("ip_address", None) is None
|
||||||
|
|
||||||
|
# FIFO Depth.
|
||||||
|
tx_fifo_depth = port_cfg.get("tx_fifo_depth", 64)
|
||||||
|
rx_fifo_depth = port_cfg.get("rx_fifo_depth", 64)
|
||||||
|
|
||||||
|
# Create/Add IOs.
|
||||||
|
# ---------------
|
||||||
|
platform.add_extension(get_udp_port_ios(name,
|
||||||
|
data_width = data_width,
|
||||||
|
dynamic_params = dynamic_params
|
||||||
|
))
|
||||||
|
|
||||||
|
port_ios = platform.request(name)
|
||||||
|
|
||||||
|
if dynamic_params:
|
||||||
|
ip_address = port_ios.ip_address
|
||||||
|
udp_port = port_ios.udp_port
|
||||||
|
else:
|
||||||
|
ip_address = port_cfg.get("ip_address")
|
||||||
|
udp_port = port_cfg.get("udp_port")
|
||||||
|
|
||||||
|
# Create UDPStreamer.
|
||||||
|
# -------------------
|
||||||
|
udp_streamer = LiteEthUDPStreamer(self.core.udp,
|
||||||
|
ip_address = ip_address,
|
||||||
|
udp_port = udp_port,
|
||||||
|
data_width = data_width,
|
||||||
|
tx_fifo_depth = tx_fifo_depth,
|
||||||
|
rx_fifo_depth = rx_fifo_depth
|
||||||
|
)
|
||||||
|
self.submodules += udp_streamer
|
||||||
|
|
||||||
|
# Connect IOs.
|
||||||
|
# ------------
|
||||||
|
# Connect UDP Sink IOs to UDP Steamer.
|
||||||
|
self.comb += [
|
||||||
|
udp_streamer.sink.valid.eq(port_ios.sink_valid),
|
||||||
|
udp_streamer.sink.last.eq(port_ios.sink_last),
|
||||||
|
port_ios.sink_ready.eq(udp_streamer.sink.ready),
|
||||||
|
udp_streamer.sink.data.eq(port_ios.sink_data)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Connect UDP Streamer to UDP Source IOs.
|
||||||
|
self.comb += [
|
||||||
|
port_ios.source_valid.eq(udp_streamer.source.valid),
|
||||||
|
port_ios.source_last.eq(udp_streamer.source.last),
|
||||||
|
udp_streamer.source.ready.eq(port_ios.source_ready),
|
||||||
|
port_ios.source_data.eq(udp_streamer.source.data),
|
||||||
|
port_ios.source_error.eq(udp_streamer.source.error),
|
||||||
|
]
|
||||||
|
|
||||||
|
def add_raw_port(self, platform, name, port_cfg):
|
||||||
|
# Use default Data-Width of 8-bit when not specified.
|
||||||
|
data_width = port_cfg.get("data_width", 8)
|
||||||
|
|
||||||
|
# Create/Add IOs.
|
||||||
|
# ---------------
|
||||||
|
platform.add_extension(get_udp_raw_port_ios(name,
|
||||||
|
data_width = data_width,
|
||||||
|
))
|
||||||
|
|
||||||
|
port_ios = platform.request(name)
|
||||||
|
|
||||||
|
raw_port = self.core.udp.crossbar.get_port(port_ios.sink_dst_port, dw=data_width)
|
||||||
|
|
||||||
|
# Connect IOs.
|
||||||
|
# ------------
|
||||||
|
# Connect UDP Sink IOs to UDP.
|
||||||
|
self.comb += [
|
||||||
|
raw_port.sink.valid.eq(port_ios.sink_valid),
|
||||||
|
raw_port.sink.last.eq(port_ios.sink_last),
|
||||||
|
raw_port.sink.dst_port.eq(port_ios.sink_dst_port),
|
||||||
|
raw_port.sink.src_port.eq(port_ios.sink_src_port),
|
||||||
|
raw_port.sink.ip_address.eq(port_ios.sink_ip_address),
|
||||||
|
raw_port.sink.length.eq(port_ios.sink_length),
|
||||||
|
port_ios.sink_ready.eq(raw_port.sink.ready),
|
||||||
|
raw_port.sink.data.eq(port_ios.sink_data),
|
||||||
|
raw_port.sink.last_be.eq(port_ios.sink_last_be),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Connect UDP to UDP Source IOs.
|
||||||
|
self.comb += [
|
||||||
|
port_ios.source_valid.eq(raw_port.source.valid),
|
||||||
|
port_ios.source_last.eq(raw_port.source.last),
|
||||||
|
port_ios.source_dst_port.eq(raw_port.source.dst_port),
|
||||||
|
port_ios.source_src_port.eq(raw_port.source.src_port),
|
||||||
|
port_ios.source_ip_address.eq(raw_port.source.ip_address),
|
||||||
|
port_ios.source_length.eq(raw_port.source.length),
|
||||||
|
raw_port.source.ready.eq(port_ios.source_ready),
|
||||||
|
port_ios.source_data.eq(raw_port.source.data),
|
||||||
|
port_ios.source_last_be.eq(raw_port.source.last_be),
|
||||||
|
port_ios.source_error.eq(raw_port.source.error),
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, platform, core_config):
|
||||||
# Config -----------------------------------------------------------------------------------
|
# Config -----------------------------------------------------------------------------------
|
||||||
tx_cdc_depth = core_config.get("tx_cdc_depth", 32)
|
tx_cdc_depth = core_config.get("tx_cdc_depth", 32)
|
||||||
tx_cdc_buffered = core_config.get("tx_cdc_buffered", False)
|
tx_cdc_buffered = core_config.get("tx_cdc_buffered", False)
|
||||||
|
@ -417,63 +546,16 @@ class UDPCore(PHYCore):
|
||||||
self.comb += axil_bus.connect_to_pads(platform.request("mmap"), mode="master")
|
self.comb += axil_bus.connect_to_pads(platform.request("mmap"), mode="master")
|
||||||
|
|
||||||
# UDP Ports --------------------------------------------------------------------------------
|
# UDP Ports --------------------------------------------------------------------------------
|
||||||
for name, port in core_config["udp_ports"].items():
|
for name, port_cfg in core_config["udp_ports"].items():
|
||||||
# Parameters.
|
# mode either `raw` or `stream`, default to streamer to be backwards compatible
|
||||||
# -----------
|
mode = port_cfg.get("mode", "streamer")
|
||||||
|
assert mode == "raw" or mode == "streamer"
|
||||||
|
|
||||||
# Use default Data-Width of 8-bit when not specified.
|
if mode == "streamer":
|
||||||
data_width = port.get("data_width", 8)
|
self.add_streamer_port(platform, name, port_cfg)
|
||||||
|
elif mode == "raw":
|
||||||
|
self.add_raw_port(platform, name, port_cfg)
|
||||||
|
|
||||||
# Used dynamic UDP-Port/IP-Address when not specified.
|
|
||||||
dynamic_params = port.get("ip_address", None) is None
|
|
||||||
|
|
||||||
# FIFO Depth.
|
|
||||||
tx_fifo_depth = port.get("tx_fifo_depth", 64)
|
|
||||||
rx_fifo_depth = port.get("rx_fifo_depth", 64)
|
|
||||||
|
|
||||||
# Create/Add IOs.
|
|
||||||
# ---------------
|
|
||||||
platform.add_extension(get_udp_port_ios(name,
|
|
||||||
data_width = data_width,
|
|
||||||
dynamic_params = dynamic_params
|
|
||||||
))
|
|
||||||
port_ios = platform.request(name)
|
|
||||||
|
|
||||||
# Create UDPStreamer.
|
|
||||||
# -------------------
|
|
||||||
if dynamic_params:
|
|
||||||
ip_address = port_ios.ip_address
|
|
||||||
udp_port = port_ios.udp_port
|
|
||||||
else:
|
|
||||||
ip_address = port.get("ip_address")
|
|
||||||
udp_port = port.get("udp_port")
|
|
||||||
udp_streamer = LiteEthUDPStreamer(self.core.udp,
|
|
||||||
ip_address = ip_address,
|
|
||||||
udp_port = udp_port,
|
|
||||||
data_width = data_width,
|
|
||||||
tx_fifo_depth = tx_fifo_depth,
|
|
||||||
rx_fifo_depth = rx_fifo_depth
|
|
||||||
)
|
|
||||||
self.submodules += udp_streamer
|
|
||||||
|
|
||||||
# Connect IOs.
|
|
||||||
# ------------
|
|
||||||
# Connect UDP Sink IOs to UDP Steamer.
|
|
||||||
self.comb += [
|
|
||||||
udp_streamer.sink.valid.eq(port_ios.sink_valid),
|
|
||||||
udp_streamer.sink.last.eq(port_ios.sink_last),
|
|
||||||
port_ios.sink_ready.eq(udp_streamer.sink.ready),
|
|
||||||
udp_streamer.sink.data.eq(port_ios.sink_data)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Connect UDP Streamer to UDP Source IOs.
|
|
||||||
self.comb += [
|
|
||||||
port_ios.source_valid.eq(udp_streamer.source.valid),
|
|
||||||
port_ios.source_last.eq(udp_streamer.source.last),
|
|
||||||
udp_streamer.source.ready.eq(port_ios.source_ready),
|
|
||||||
port_ios.source_data.eq(udp_streamer.source.data),
|
|
||||||
port_ios.source_error.eq(udp_streamer.source.error),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Build --------------------------------------------------------------------------------------------
|
# Build --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,7 @@ class TestExamples(unittest.TestCase):
|
||||||
def test_wishbone_mii(self):
|
def test_wishbone_mii(self):
|
||||||
errors = build_config("wishbone_mii")
|
errors = build_config("wishbone_mii")
|
||||||
self.assertEqual(errors, 0)
|
self.assertEqual(errors, 0)
|
||||||
|
|
||||||
|
def test_udp_raw_rgmii(self):
|
||||||
|
errors = build_config("udp_raw_ecp5rgmii")
|
||||||
|
self.assertEqual(errors, 0)
|
||||||
|
|
Loading…
Reference in New Issue