-- -------------------------------------------------------------------------- -- -- TRASHERNET - A Trashy Ethernet Stack for FPGAs -- -- -------------------------------------------------------------------------- -- -- bench_trashernet_mac.vhd : Stimulus-only test bench for the MAC+PHY parts -- Tests TX path through the RX path. Not great, but whatever. -- -------------------------------------------------------------------------- -- -- Author : Markus Koch -- Contributors : None -- License : Mozilla Public License (MPL) Version 2 -- -------------------------------------------------------------------------- -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.bench_pkg.all; library trashernet; use trashernet.trashernet_pkg.all; library vunit_lib; context vunit_lib.vunit_context; entity bench_trashernet_rmii is generic( runner_cfg : string ); end entity bench_trashernet_rmii; architecture bench of bench_trashernet_rmii is signal clk : std_logic; signal rst : std_logic; signal phy_out : phy_out_t; signal phy_in : phy_in_t; signal rmii_ref_clk : std_logic; signal rmii_crs_dv : std_logic; signal rmii_rxd : std_logic_vector(1 downto 0); signal rmii_tx_en : std_logic; signal rmii_txd : std_logic_vector(1 downto 0); signal rmii_tx_start : std_logic := '0'; signal trashernet_tx_start : std_logic := '0'; begin trashernet_rmii_inst : entity trashernet.trashernet_rmii generic map( SYSCLK_IS_REFCLK => false ) port map( clk => clk, rst => rst, phy_out => phy_out, phy_in => phy_in, rmii_ref_clk => rmii_ref_clk, rmii_crs_dv => rmii_crs_dv, rmii_rxd => rmii_rxd, rmii_tx_en => rmii_tx_en, rmii_txd => rmii_txd ); clockDriver : process constant period : time := 10 ns; begin clk <= '0'; wait for period / 2; clk <= '1'; wait for period / 2; end process clockDriver; test : process is begin rst <= '1'; test_runner_setup(runner, runner_cfg); wait for 10 ns; rst <= '0'; wait for 10 ns; while test_suite loop if run("rx") then rmii_tx_start <= not rmii_tx_start; wait until phy_out.rx_active = '1' for 1 ms; assert phy_out.rx_active'event report "Timeout for reception to start"; wait until phy_out.rx_active = '0' for 1 ms; assert phy_out.rx_active'event report "Timeout for reception to end"; wait for 1 us; elsif run("tx") then trashernet_tx_start <= not trashernet_tx_start; wait until phy_out.tx_active = '1' for 1 ms; assert phy_out.tx_active'event report "Timeout for transmission to start"; wait until phy_out.tx_active = '0' for 1 ms; assert phy_out.tx_active'event report "Timeout for transmission to stop"; wait until phy_out.tx_active = '1' for 1 ms; assert phy_out.tx_active'event report "Timeout for transmission to start"; wait until phy_out.tx_active = '0' for 1 ms; assert phy_out.tx_active'event report "Timeout for transmission to stop"; end if; end loop; wait for 1 ns; test_runner_cleanup(runner); end process test; phy_rx : process(clk) is begin if rising_edge(clk) then if phy_out.rx_data_valid = '1' then report "RX data: " & to_hstring(phy_out.rx_data); end if; end if; end process phy_rx; rmiirefclk : process constant period : time := 20 ns; begin rmii_ref_clk <= '0'; wait for period / 2; rmii_ref_clk <= '1'; wait for period / 2; end process rmiirefclk; rmii_tx_gen : process is procedure send_frame(data : byte_vector) is variable sr : byte; begin wait until rising_edge(rmii_ref_clk); rmii_rxd <= "01"; wait for 2.5 ns; rmii_crs_dv <= '1'; for i in 0 to 10 loop wait until rmii_ref_clk; end loop; rmii_rxd <= "11"; wait until rmii_ref_clk; for i in data'range loop sr := data(i); for j in 0 to 3 loop rmii_rxd <= sr(1 downto 0); wait until rising_edge(rmii_ref_clk); sr := "XX" & sr(sr'high downto 2); end loop; end loop; rmii_crs_dv <= '0'; wait for 1 us; -- IPG end procedure send_frame; begin rmii_crs_dv <= '0'; rmii_rxd <= (others => '0'); wait on rmii_tx_start; send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", x"48", x"65", x"6c", x"6c", x"6f", x"20", x"57", x"6f", x"72", x"6c", x"64", x"21", x"0a", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea")); wait; end process rmii_tx_gen; trashernet_tx_gen : process is procedure send_frame(data : byte_vector) is begin for i in data'range loop phy_in.tx_data_en <= '1'; phy_in.tx_data <= data(i); wait until rising_edge(clk); while not phy_out.tx_data_ack loop wait until rising_edge(clk); end loop; end loop; phy_in.tx_data_en <= '0'; wait until rising_edge(clk); end procedure send_frame; begin phy_in.tx_data_en <= '0'; wait on trashernet_tx_start; wait until rising_edge(clk); send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", x"48", x"65", x"6c", x"6c", x"6f", x"20", x"57", x"6f", x"72", x"6c", x"64", x"21", x"0a", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea")); -- Currently, there's a bug in the CDC preventing single-cycle deassertions from deasserting the transmit enable wait until rising_edge(clk); wait until rising_edge(clk); wait until rising_edge(clk); send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", x"48", x"65", x"6c", x"6c", x"6f", x"20", x"57", x"6f", x"72", x"6c", x"64", x"21", x"0a", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea")); end process trashernet_tx_gen; rmii_receiver : process(rmii_ref_clk) is variable sr : byte; variable cnt : integer range 0 to 3; variable active : boolean := false; begin if rising_edge(rmii_ref_clk) then if rmii_tx_en then if not active then report "RMII RX start"; end if; sr := rmii_txd & sr(sr'high downto 2); if cnt = 3 then cnt := 0; report "RMII RX: " & to_hstring(sr); else cnt := cnt + 1; end if; active := true; else if active then report "RMII RX stop"; end if; cnt := 0; active := false; end if; end if; end process rmii_receiver; end architecture bench;