124 lines
3.6 KiB
VHDL
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;
|