trashernet/cocotb/cocovc_eth.vhd

124 lines
3.6 KiB
VHDL

-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- cocovc_eth.vhd : Cocotb-based verification component for 10M Ethernet.
-- Note: This is cheapskated to work with Trashernet. It may not work with
-- every PHY.
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity cocovc_eth is
port(
rx_p : in std_logic;
rx_n : in std_logic;
tx_p : out std_logic;
tx_n : out std_logic
);
end entity cocovc_eth;
architecture bench of cocovc_eth is
-- Ethernet TX (TB/Python to DUT/VHDL)
signal cocotb_tx_d : std_logic_vector(7 downto 0) := x"00"; -- Data to be transferred to VHDL
signal cocotb_tx_dv : std_logic := '0'; -- '1' when `d` holds valid data that is to be processed by VHDL
signal cocotb_tx_da : std_logic := '0'; -- Toggles when VHDL has latched `d`, Python must advance data or deassert `dv`
-- Ethernet RX (DUT/VHDL to TB/Python)
signal cocotb_rx_d : std_logic_vector(7 downto 0); -- Data to be transferred to Python
signal cocotb_rx_dt : std_logic := '0'; -- Toggles to announce new data (Python has to latch immediately)
signal cocotb_rx_a : std_logic := '0'; -- Block active indicator (rising on block start, falling on block end)
begin
-- Ethernet TX
eth_tx_proc : process is
variable d : std_logic_vector(cocotb_rx_d'range);
begin
tx_p <= '0';
wait until cocotb_tx_dv = '1';
loop
exit when not cocotb_tx_dv;
d := cocotb_tx_d;
cocotb_tx_da <= not cocotb_tx_da;
for j in d'low to d'high loop
tx_p <= not d(j);
wait for 50 ns;
tx_p <= d(j);
wait for 50 ns;
end loop;
end loop;
wait for 16 us; -- Wait IPG
cocotb_tx_da <= not cocotb_tx_da; -- Signal that end-of-block was latched
end process eth_tx_proc;
tx_n <= not tx_p;
-- Ethernet RX
eth_rx_proc : process is
constant SYM_TIME : time := 50 ns;
variable d : std_logic_vector(cocotb_rx_d'range) := x"00";
variable cnt : integer range d'low to d'high;
variable bit : std_logic;
variable ignore : boolean;
variable first : boolean; -- Just a cheap workaround because the weather is too nice to implement it properly this afternoon
procedure latch(value : std_logic) is
begin
d(cnt) := value;
if (cnt = 7) then
cocotb_rx_d <= d;
cocotb_rx_dt <= not cocotb_rx_dt;
cnt := 0;
else
cnt := cnt + 1;
end if;
end procedure latch;
begin
cocotb_rx_a <= '0';
loop
wait on rx_p;
exit when (rx_p'last_value = '0' or rx_p'last_value = '1');
end loop;
cocotb_rx_a <= '1';
wait for 0 ns;
cnt := 0;
bit := '0';
ignore := false;
first := true;
loop
wait on rx_p for SYM_TIME * 1.5;
exit when rx_p'last_event > 2.5 * SYM_TIME;
if rx_p'event then -- short
if not ignore then
latch(bit);
end if;
ignore := not ignore;
else -- long
if (first) then
bit := '1';
cnt := 1;
d(0) := '1';
end if;
bit := not bit;
latch(bit);
ignore := true;
end if;
first := false;
end loop;
end process eth_rx_proc;
end architecture bench;