diff --git a/gen_liteeth.py b/gen_liteeth.py index 93fa24f..898b8cf 100755 --- a/gen_liteeth.py +++ b/gen_liteeth.py @@ -28,6 +28,7 @@ from liteeth.common import * from liteeth import phy as liteeth_phys from liteeth.mac import LiteEthMAC from liteeth.core import LiteEthUDPIPCore +from liteeth.core.udp import LiteEthUDP # IOs ---------------------------------------------------------------------------------------------- @@ -40,12 +41,8 @@ _io = [ ("sys_reset", 1, Pins(1)), # IP/MAC Address. - ("mac_address", 0, Pins(48)), ("ip_address", 0, Pins(32)), - # Interrupt - ("interrupt", 0, Pins(1)), - # MII PHY Pads ("mii_eth_clocks", 0, Subsignal("tx", Pins(1)), @@ -63,61 +60,10 @@ _io = [ Subsignal("col", Pins(1)), Subsignal("crs", Pins(1)) ), - - # RMII PHY Pads - ("rmii_eth_clocks", 0, - Subsignal("ref_clk", Pins(1)) - ), - ("rmii_eth", 0, - Subsignal("rst_n", Pins(1)), - Subsignal("rx_data", Pins(2)), - Subsignal("crs_dv", Pins(1)), - Subsignal("tx_en", Pins(1)), - Subsignal("tx_data", Pins(2)), - Subsignal("mdc", Pins(1)), - Subsignal("mdio", Pins(1)), - ), - - # GMII PHY Pads - ("gmii_eth_clocks", 0, - Subsignal("tx", Pins(1)), - Subsignal("gtx", Pins(1)), - Subsignal("rx", Pins(1)) - ), - ("gmii_eth", 0, - Subsignal("rst_n", Pins(1)), - Subsignal("int_n", Pins(1)), - Subsignal("mdio", Pins(1)), - Subsignal("mdc", Pins(1)), - Subsignal("rx_dv", Pins(1)), - Subsignal("rx_er", Pins(1)), - Subsignal("rx_data", Pins(8)), - Subsignal("tx_en", Pins(1)), - Subsignal("tx_er", Pins(1)), - Subsignal("tx_data", Pins(8)), - Subsignal("col", Pins(1)), - Subsignal("crs", Pins(1)) - ), - - # RGMII PHY Pads - ("rgmii_eth_clocks", 0, - Subsignal("tx", Pins(1)), - Subsignal("rx", Pins(1)) - ), - ("rgmii_eth", 0, - Subsignal("rst_n", Pins(1)), - Subsignal("int_n", Pins(1)), - Subsignal("mdio", Pins(1)), - Subsignal("mdc", Pins(1)), - Subsignal("rx_ctl", Pins(1)), - Subsignal("rx_data", Pins(4)), - Subsignal("tx_ctl", Pins(1)), - Subsignal("tx_data", Pins(4)) - ), ] -def get_udp_port_ios(name, data_width): +def get_udp_streamer_port_ios(name, data_width): return [ (f"{name}", 0, Subsignal("ip_address", Pins(32)), @@ -137,6 +83,36 @@ def get_udp_port_ios(name, data_width): ] +def get_udp_port_ios(name, data_width): + return [ + (f"{name}", 0, + Subsignal("bind_port", Pins(16)), + + # Sink. + Subsignal("sink_dst_ip_address", Pins(32)), + Subsignal("sink_dst_port", Pins(16)), + + Subsignal("sink_length", Pins(16)), + Subsignal("sink_valid", Pins(1)), + Subsignal("sink_last", Pins(1)), + Subsignal("sink_last_be", Pins(data_width // 8)), + Subsignal("sink_ready", Pins(1)), + Subsignal("sink_data", Pins(data_width)), + + # Source. + Subsignal("source_src_ip_address", Pins(32)), + Subsignal("source_src_port", Pins(16)), + + Subsignal("source_length", Pins(16)), + Subsignal("source_valid", Pins(1)), + Subsignal("source_last", Pins(1)), + Subsignal("source_last_be", Pins(data_width // 8)), + Subsignal("source_ready", Pins(1)), + Subsignal("source_data", Pins(data_width)), + ), + ] + + class PHYCore(SoCMini): def __init__(self, platform): super().__init__(platform, clk_freq=CLK_FREQ) @@ -178,6 +154,16 @@ class PHYCore(SoCMini): ) +class UDPPort(Module, AutoCSR): + def __init__(self, udp_core: LiteEthUDP, bind_port, data_width=8, cd="sys"): + udp_port = udp_core.crossbar.get_port(bind_port, dw=data_width, cd=cd) + self.sink = udp_port.sink + self.comb += [ + self.sink.src_port.eq(bind_port), + ] + self.source = udp_port.source + + class UDPCore(PHYCore): def __init__(self, platform): from liteeth.frontend.stream import LiteEthUDPStreamer @@ -199,7 +185,7 @@ class UDPCore(PHYCore): # DHCP port data_width = 32 - platform.add_extension(get_udp_port_ios( + platform.add_extension(get_udp_streamer_port_ios( "dhcp", data_width=data_width, )) @@ -229,6 +215,46 @@ class UDPCore(PHYCore): dhcp_ios.source_data.eq(dhcp_streamer.source.data), ] + # Pixel port + data_width = 32 + + platform.add_extension(get_udp_port_ios( + "pixel", + data_width=data_width, + )) + pixel_ios = platform.request("pixel") + + pixel_port = UDPPort( + self.core.udp, + bind_port=pixel_ios.bind_port, + data_width=data_width, + ) + self.submodules += pixel_port + + self.comb += [ + # Connect UDP Sink IOs to UDPPort + pixel_port.sink.ip_address.eq(pixel_ios.sink_dst_ip_address), + pixel_port.sink.dst_port.eq(pixel_ios.sink_dst_port), + + pixel_port.sink.length.eq(pixel_ios.sink_length), + pixel_port.sink.valid.eq(pixel_ios.sink_valid), + pixel_port.sink.last.eq(pixel_ios.sink_last), + pixel_port.sink.last_be.eq(pixel_ios.sink_last_be), + pixel_ios.sink_ready.eq(pixel_port.sink.ready), + pixel_port.sink.data.eq(pixel_ios.sink_data), + + # Connect UDPPort to UDP Source IOs. + pixel_ios.source_src_ip_address.eq(pixel_port.source.ip_address), + pixel_ios.source_src_port.eq(pixel_port.source.src_port), + + pixel_ios.source_length.eq(pixel_port.source.length), + pixel_ios.source_valid.eq(pixel_port.source.valid), + pixel_ios.source_last.eq(pixel_port.source.last), + pixel_ios.source_last_be.eq(pixel_port.source.last_be), + pixel_port.source.ready.eq(pixel_ios.source_ready), + pixel_ios.source_data.eq(pixel_port.source.data), + ] + # Build -------------------------------------------------------------------------------------------- @@ -244,7 +270,7 @@ def main(): platform = XilinxPlatform( "xc7a35ticsg324-1L", io=[], - toolchain="symbiflow" + toolchain="yosys+nextpnr" ) platform.add_extension(_io) diff --git a/vhdl/arty_a7.vhdl b/vhdl/arty_a7.vhdl index 70d67f2..6419831 100644 --- a/vhdl/arty_a7.vhdl +++ b/vhdl/arty_a7.vhdl @@ -53,6 +53,7 @@ architecture a of arty_a7 is port ( sys_clock : in std_logic; sys_reset : in std_logic; + mii_eth_clocks_tx : in std_logic; mii_eth_clocks_rx : in std_logic; mii_eth_rst_n : out std_logic; @@ -65,20 +66,55 @@ architecture a of arty_a7 is mii_eth_tx_data : out std_logic_vector(3 downto 0); mii_eth_col : in std_logic; mii_eth_crs : in std_logic; + ip_address : in std_logic_vector(31 downto 0); + + --== DHCP PORT ==-- dhcp_ip_address : in std_logic_vector(31 downto 0); + dhcp_sink_valid : in std_logic; dhcp_sink_last : in std_logic; dhcp_sink_ready : out std_logic; dhcp_sink_data : in std_logic_vector(31 downto 0); + dhcp_source_valid : out std_logic; dhcp_source_last : out std_logic; dhcp_source_ready : in std_logic; - dhcp_source_data : out std_logic_vector(31 downto 0) + dhcp_source_data : out std_logic_vector(31 downto 0); + + --== PIXEL DATA PORT ==-- + pixel_bind_port : in std_logic_vector(15 downto 0); + + -- sink + pixel_sink_dst_ip_address : in std_logic_vector(31 downto 0); + pixel_sink_dst_port : in std_logic_vector(15 downto 0); + + pixel_sink_length : in std_logic_vector(15 downto 0); + pixel_sink_valid : in std_logic; + pixel_sink_last : in std_logic; + pixel_sink_last_be : in std_logic_vector(3 downto 0); + pixel_sink_ready : out std_logic; + pixel_sink_data : in std_logic_vector(31 downto 0); + + -- source + pixel_source_src_ip_address : out std_logic_vector(31 downto 0); + pixel_source_src_port : out std_logic_vector(15 downto 0); + + pixel_source_length : out std_logic_vector(15 downto 0); + pixel_source_valid : out std_logic; + pixel_source_last : out std_logic; + pixel_source_last_be : out std_logic_vector(3 downto 0); + pixel_source_ready : in std_logic; + pixel_source_data : out std_logic_vector(31 downto 0) ); end component; - signal dhcp_source_valid : std_logic; + signal pixel_sink_length : std_logic_vector(15 downto 0); + signal pixel_sink_valid : std_logic; + signal pixel_sink_last : std_logic; + signal pixel_sink_last_be : std_logic_vector(3 downto 0); + signal pixel_sink_ready : std_logic; + signal pixel_sink_data : std_logic_vector(31 downto 0); component PLLE2_BASE generic ( @@ -181,8 +217,32 @@ begin dhcp_sink_last => '1', dhcp_sink_data => x"cafebebe", - dhcp_source_valid => dhcp_source_valid, - dhcp_source_ready => '1' + dhcp_source_ready => '1', + + --== PIXEL DATA PORT ==-- + pixel_bind_port => x"effd", -- port 61437 - "PIXEL" + + -- sink + pixel_sink_dst_ip_address => x"0a141e29", + pixel_sink_dst_port => x"303a", -- port 12346 + + pixel_sink_length => pixel_sink_length, + pixel_sink_valid => pixel_sink_valid, + pixel_sink_last => pixel_sink_last, + pixel_sink_last_be => pixel_sink_last_be, + pixel_sink_ready => pixel_sink_ready, + pixel_sink_data => pixel_sink_data, + + -- source + pixel_source_src_ip_address => open, + pixel_source_src_port => open, + + pixel_source_length => open, + pixel_source_valid => open, + pixel_source_last => open, + pixel_source_last_be => open, + pixel_source_ready => '1', + pixel_source_data => open ); -- 800 MHz VCO @@ -259,6 +319,35 @@ begin pmod_d(6) <= '0'; pmod_d(7) <= '0'; + sender: process(clk_sys) + constant COUNTER_MAX: natural := 80000000; + variable counter: natural range 0 to COUNTER_MAX; + + constant NUM_WORDS: natural := 10; + variable words_sent: natural range 0 to NUM_WORDS; + begin + if rising_edge(clk_sys) then + if counter = COUNTER_MAX then + pixel_sink_length <= std_logic_vector(to_unsigned(NUM_WORDS, 16)); + pixel_sink_data <= std_logic_vector(to_unsigned(16#30# + words_sent, 32)); + pixel_sink_valid <= '1'; + + pixel_sink_last <= '1' when words_sent = NUM_WORDS-1 else '0'; + + if words_sent = NUM_WORDS then + pixel_sink_valid <= '0'; + counter := 0; + words_sent := 0; + elsif pixel_sink_ready then + words_sent := words_sent + 1; + end if; + else + counter := counter + 1; + end if; + end if; + end process; + pixel_sink_last_be <= (pixel_sink_last, others => '0'); + splink: entity work.splink generic map ( NUM_DRIVERS => NUM_DRIVERS