manta/examples/amaranth/ethernet_io_core.py

121 lines
3.9 KiB
Python
Raw Normal View History

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,
2024-11-27 05:51:18 +01:00
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),
)
2024-11-27 05:51:18 +01:00
platform.add_file("divider.sv", open("../common/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
# This is only required for Amaranth < 0.5.2
2024-11-27 05:51:18 +01:00
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")
2024-11-27 05:51:18 +01:00
# Run the PHY's ethclk from the 50MHz divider
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)
2024-11-27 05:51:18 +01:00
# Wire Ethernet pins to the Manta instance
self.manta.interface.set_phy_io(
rmii_clocks_ref_clk=ethclk.clk,
rmii_rst_n=eth_pins.reset.io,
rmii_rx_data=eth_pins.rxd.io,
rmii_crs_dv=eth_pins.crs_dv.io,
rmii_tx_en=eth_pins.txen.io,
rmii_tx_data=eth_pins.txd.io,
rmii_mdc=eth_pins.mdc.io,
rmii_mdio=eth_pins.mdio.io,
)
return m
def test(self):
# Build and program the FPGA
2024-11-27 05:51:18 +01:00
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)
2024-11-27 05:51:18 +01:00
# Although Amaranth provides an environment that is almost entirely independent
# of FPGA vendor or family, it does not provide any facilites for clock
# generation. As a result, this example design includes an external Verilog
# snippet containing a clock generator created by Vivado's Clock Wizard.
# This uses a MMCM clock generation primitive to make a 50MHz clock from the
# onboard 100MHz oscillator, in order to drive the Ethernet PHY. This primitive
# is only available on Xilinx Series-7 parts, so this example will only work on
# Series-7 parts clocked at 100MHz that have RMII PHYs connected...which is
# pretty much just the Nexys4DDR and the Arty A7 :)
if __name__ == "__main__":
from amaranth_boards.nexys4ddr import Nexys4DDRPlatform
EthernetIOCoreExample(platform=Nexys4DDRPlatform(), port="auto").test()