ethernet: add initial (working!) amaranth-native design
This commit is contained in:
parent
9ac3181502
commit
31aea6fc4a
|
|
@ -0,0 +1,125 @@
|
|||
from time import sleep
|
||||
|
||||
from amaranth import *
|
||||
from amaranth.lib import io
|
||||
|
||||
from manta import *
|
||||
|
||||
|
||||
class EthernetIOCoreExample(Elaboratable):
|
||||
def __init__(self, platform, port):
|
||||
self.platform = platform
|
||||
|
||||
# Create Manta instance
|
||||
self.manta = Manta()
|
||||
|
||||
# Configure it to communicate over Ethernet
|
||||
self.manta.interface = EthernetInterface(
|
||||
phy="LiteEthPHYRMII",
|
||||
device="xc7a",
|
||||
vendor="xilinx",
|
||||
toolchain="vivado",
|
||||
refclk_freq=50e6,
|
||||
clk_freq = 50e6,
|
||||
fpga_ip_addr = "10.0.0.2",
|
||||
host_ip_addr = "10.0.0.1",
|
||||
udp_port=2000,
|
||||
)
|
||||
|
||||
# Autodetect the number of LEDs on the platform
|
||||
resources = platform.resources.keys()
|
||||
self.n_leds = max([i for name, i in resources if name == "led"])
|
||||
|
||||
# Add IOCore to Manta instance
|
||||
self.leds = Signal(self.n_leds)
|
||||
self.manta.cores.io = IOCore(outputs=[self.leds])
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
# Create 50MHz clock domain
|
||||
m.domains.ethclk = ethclk = ClockDomain()
|
||||
m.submodules.divider = Instance(
|
||||
"divider",
|
||||
("i", "clk", ClockSignal()),
|
||||
("o", "ethclk", ethclk.clk),
|
||||
)
|
||||
platform.add_file("divider.sv", open("divider.sv"))
|
||||
|
||||
# Add Manta as a submodule
|
||||
m.submodules.manta = DomainRenamer("ethclk")(self.manta)
|
||||
|
||||
# Wire each LED to Manta's IO Core output
|
||||
for i in range(self.n_leds):
|
||||
led = io.Buffer("o", platform.request("led", i, dir="-"))
|
||||
m.d.comb += led.o.eq(self.leds[i])
|
||||
m.submodules += led
|
||||
|
||||
# Wire Ethernet pins to the Manta instance
|
||||
|
||||
# This is only required for Amaranth < 0.5.2
|
||||
eth_pin_names = ["mdio", "mdc", "reset", "rxd", "rxerr", "txd", "txen", "crs_dv", "int", "clk"]
|
||||
eth_pin_dirs = {name: "-" for name in eth_pin_names}
|
||||
eth_pins = platform.request("eth", dir=eth_pin_dirs)
|
||||
|
||||
# For Amaranth > 0.5.2, this simpler syntax may be used:
|
||||
# eth_pins = platform.request("eth")
|
||||
|
||||
# self.manta.interface.set_phy_io(
|
||||
# rmii_clocks_ref_clk = eth_pins.clk,
|
||||
# rmii_rst_n = eth_pins.reset,
|
||||
# rmii_rx_data = eth_pins.rxd,
|
||||
# rmii_crs_dv = eth_pins.crs_dv,
|
||||
# rmii_tx_en = eth_pins.txen,
|
||||
# rmii_tx_data = eth_pins.txd,
|
||||
# rmii_mdc = eth_pins.mdc,
|
||||
# rmii_mdio = eth_pins.mdio,
|
||||
# )
|
||||
|
||||
# m.submodules.rxd = rxd = io.Buffer("i", eth_pins.rxd)
|
||||
# m.submodules.crs_dv = crs_dv = io.Buffer("i", eth_pins.crs_dv)
|
||||
# m.submodules.txen = txen = io.Buffer("o", eth_pins.txen)
|
||||
# m.submodules.txd = txd = io.Buffer("o", eth_pins.txd)
|
||||
# m.submodules.mdc = mdc = io.Buffer("o", eth_pins.mdc)
|
||||
# m.submodules.mdio = mdio = io.SingleEndedPort(io.Buffer("io", eth_pins.mdio))
|
||||
|
||||
m.submodules.eth_clk_io_buf = eth_clk_io_buf = io.Buffer("o", eth_pins.clk)
|
||||
m.d.comb += eth_clk_io_buf.o.eq(ethclk.clk)
|
||||
|
||||
self.manta.interface._phy_io = [
|
||||
("i", "rmii_clocks_ref_clk", ethclk.clk),
|
||||
("o", "rmii_rst_n", eth_pins.reset.io),
|
||||
("i", "rmii_rx_data", eth_pins.rxd.io),
|
||||
("i", "rmii_crs_dv", eth_pins.crs_dv.io),
|
||||
("o", "rmii_tx_en", eth_pins.txen.io),
|
||||
("o", "rmii_tx_data", eth_pins.txd.io),
|
||||
("o", "rmii_mdc", eth_pins.mdc.io),
|
||||
("io", "rmii_mdio", eth_pins.mdio.io),
|
||||
]
|
||||
|
||||
platform.add_file("liteeth.v", self.manta.interface.generate_liteeth_core())
|
||||
|
||||
return m
|
||||
|
||||
def test(self):
|
||||
# Build and program the FPGA
|
||||
# self.platform.build(self, do_program=True)
|
||||
|
||||
# Iterate through all the LEDs, blinking them off and on
|
||||
i = 0
|
||||
while True:
|
||||
self.manta.cores.io.set_probe("leds", 1 << i)
|
||||
i = (i + 1) % self.n_leds
|
||||
sleep(0.1)
|
||||
|
||||
|
||||
# Amaranth has a built-in build system, and well as a set of platform
|
||||
# definitions for a huge number of FPGA boards. The class defined above is
|
||||
# very generic, as it specifies a design independent of any particular FGPA
|
||||
# board. This means that by changing which platform you pass UARTIOCoreExample
|
||||
# below, you can port this example to any FPGA board!
|
||||
|
||||
if __name__ == "__main__":
|
||||
from amaranth_boards.nexys4ddr import Nexys4DDRPlatform
|
||||
|
||||
EthernetIOCoreExample(platform=Nexys4DDRPlatform(), port="auto").test()
|
||||
Loading…
Reference in New Issue