bench: Add cocotb-based test bench for MAC test
This commit is contained in:
parent
7d86f6a262
commit
f45ded2ca9
10
.gitignore
vendored
10
.gitignore
vendored
@ -1 +1,11 @@
|
|||||||
|
# VUnit
|
||||||
vunit_out
|
vunit_out
|
||||||
|
|
||||||
|
# Cocotb
|
||||||
|
__pycache__
|
||||||
|
*.o
|
||||||
|
cocotb/results.xml
|
||||||
|
cocotb/sim_build
|
||||||
|
cocotb/wave.ghw
|
||||||
|
|
||||||
|
cocotb/cocotb_top_mac_test
|
||||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "cocotb/cocotb_helpers"]
|
||||||
|
path = cocotb/cocotb_helpers
|
||||||
|
url = https://git.notsyncing.net/fpga/cocotb_helpers.git
|
42
bench/pll0.vhd
Normal file
42
bench/pll0.vhd
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- pll0.vhd : Simulates the PLL component present in some demo designs
|
||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- 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 pll0 is
|
||||||
|
port(
|
||||||
|
CLK : in std_logic;
|
||||||
|
CLKOP : out std_logic;
|
||||||
|
LOCK : out std_logic
|
||||||
|
);
|
||||||
|
end entity pll0;
|
||||||
|
|
||||||
|
architecture bench of pll0 is
|
||||||
|
begin
|
||||||
|
clock_driver : process
|
||||||
|
constant period : time := 7.1429 ns; -- 140 MHz configured clock
|
||||||
|
begin
|
||||||
|
CLKOP <= '0';
|
||||||
|
LOCK <= '0';
|
||||||
|
|
||||||
|
wait until CLK'event;
|
||||||
|
wait for 12 ns;
|
||||||
|
LOCK <= '1';
|
||||||
|
|
||||||
|
loop
|
||||||
|
CLKOP <= '0';
|
||||||
|
wait for period / 2;
|
||||||
|
CLKOP <= '1';
|
||||||
|
wait for period / 2;
|
||||||
|
end loop;
|
||||||
|
end process clock_driver;
|
||||||
|
end architecture bench;
|
17
cocotb/Makefile
Normal file
17
cocotb/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# defaults
|
||||||
|
SIM ?= ghdl
|
||||||
|
TOPLEVEL_LANG ?= vhdl
|
||||||
|
BASE = $(PWD)/..
|
||||||
|
|
||||||
|
COMPILE_ARGS=--std=08
|
||||||
|
SIM_ARGS ?= --wave=wave.ghw
|
||||||
|
|
||||||
|
VHDL_SOURCES_trashernet += $(BASE)/trashernet/*.vhd
|
||||||
|
VHDL_SOURCES_design += $(BASE)/design/*.vhd
|
||||||
|
VHDL_SOURCES_design += $(BASE)/bench/pll0.vhd
|
||||||
|
VHDL_SOURCES += $(BASE)/cocotb/*.vhd
|
||||||
|
|
||||||
|
TOPLEVEL = cocotb_top_mac_test
|
||||||
|
MODULE = cocotb_top_mac_test
|
||||||
|
|
||||||
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
1
cocotb/cocotb_helpers
Submodule
1
cocotb/cocotb_helpers
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit e2c72ef61d7742fbcb7e048bc928446ed4b56a82
|
48
cocotb/cocotb_top_mac_test.py
Normal file
48
cocotb/cocotb_top_mac_test.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import cocotb
|
||||||
|
from cocotb.triggers import *
|
||||||
|
from cocotb.result import *
|
||||||
|
|
||||||
|
from cocotb.queue import Queue
|
||||||
|
|
||||||
|
from cocotb_helpers import buffers
|
||||||
|
|
||||||
|
ETH_HEAD = b'\x55\x55\x55\x55\xD5'
|
||||||
|
|
||||||
|
async def timeout(dut):
|
||||||
|
await Timer(1, units="ms")
|
||||||
|
assert False, "Timeout"
|
||||||
|
|
||||||
|
async def rxprinter(dut, rx):
|
||||||
|
while True:
|
||||||
|
frame = await rx.queue.get()
|
||||||
|
dut._log.info("RX Frame: " + str(frame))
|
||||||
|
|
||||||
|
@cocotb.test()
|
||||||
|
async def my_test(dut):
|
||||||
|
"""TBD description."""
|
||||||
|
|
||||||
|
# Start verification components
|
||||||
|
eth_tx = buffers.tx_buffer(dut, "cocovc_eth_inst.cocotb_tx_")
|
||||||
|
await eth_tx.start()
|
||||||
|
eth_rx = buffers.rx_buffer(dut, "cocovc_eth_inst.cocotb_rx_")
|
||||||
|
await eth_rx.start()
|
||||||
|
|
||||||
|
# Start local monitors
|
||||||
|
await cocotb.start(rxprinter(dut, eth_rx))
|
||||||
|
|
||||||
|
# Start timeout
|
||||||
|
await cocotb.start(timeout(dut))
|
||||||
|
|
||||||
|
# Wait for VHDL part to be ready
|
||||||
|
await Edge(dut.bench_ready)
|
||||||
|
|
||||||
|
# Start test procedure
|
||||||
|
await Timer(400, units="us") # The device will send a frame at startup
|
||||||
|
|
||||||
|
dut._log.info("Send first frame"); # This will trigger a response frame
|
||||||
|
await eth_tx.send(ETH_HEAD + b'\x00\xff\xff\x11"3\x00\xff\xff\xaa\xbb\xcc\x00\n\n\t\x08\x07\x06\x05\x04\x03\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x10\x90\xe0');
|
||||||
|
dut._log.info("Send second frame");
|
||||||
|
await eth_tx.send(ETH_HEAD + b'World');
|
||||||
|
await Timer(400, units="us") # Wait for response
|
77
cocotb/cocotb_top_mac_test.vhd
Normal file
77
cocotb/cocotb_top_mac_test.vhd
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- cocotb_top_mac_test.vhd : Test bench for the MAC test demo.
|
||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- 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;
|
||||||
|
|
||||||
|
library design;
|
||||||
|
use design.all;
|
||||||
|
|
||||||
|
entity cocotb_top_mac_test is
|
||||||
|
end entity cocotb_top_mac_test;
|
||||||
|
|
||||||
|
architecture bench of cocotb_top_mac_test is
|
||||||
|
-- DUT signals
|
||||||
|
signal clk : std_logic;
|
||||||
|
signal rst_n : std_logic;
|
||||||
|
signal rx_p : std_logic;
|
||||||
|
signal tx_p : std_logic;
|
||||||
|
signal tx_n : std_logic;
|
||||||
|
signal led_n : std_logic_vector(7 downto 0);
|
||||||
|
signal button_n : std_logic_vector(3 downto 0);
|
||||||
|
signal debug_data : std_logic_vector(7 downto 0);
|
||||||
|
|
||||||
|
-- Generic test bench
|
||||||
|
signal bench_ready : std_logic := '0';
|
||||||
|
begin
|
||||||
|
top_mac_test_inst : entity design.top_mac_test
|
||||||
|
port map(
|
||||||
|
clk => clk,
|
||||||
|
rst_n => rst_n,
|
||||||
|
rx_p => rx_p,
|
||||||
|
tx_p => tx_p,
|
||||||
|
tx_n => tx_n,
|
||||||
|
led_n => led_n,
|
||||||
|
button_n => button_n,
|
||||||
|
debug_data => debug_data
|
||||||
|
);
|
||||||
|
|
||||||
|
clock_driver : process
|
||||||
|
constant period : time := 20 ns;
|
||||||
|
begin
|
||||||
|
clk <= '0';
|
||||||
|
wait for period / 2;
|
||||||
|
clk <= '1';
|
||||||
|
wait for period / 2;
|
||||||
|
end process clock_driver;
|
||||||
|
|
||||||
|
rstsim : process is
|
||||||
|
begin
|
||||||
|
rst_n <= '0';
|
||||||
|
wait for 400 ns;
|
||||||
|
rst_n <= '1';
|
||||||
|
wait for 100 ns;
|
||||||
|
wait until rising_edge(clk);
|
||||||
|
bench_ready <= '1';
|
||||||
|
wait;
|
||||||
|
end process rstsim;
|
||||||
|
|
||||||
|
cocovc_eth_inst : entity work.cocovc_eth
|
||||||
|
port map(
|
||||||
|
rx_p => tx_p,
|
||||||
|
rx_n => tx_n,
|
||||||
|
tx_p => rx_p,
|
||||||
|
tx_n => open
|
||||||
|
);
|
||||||
|
|
||||||
|
button_n <= (others => '1');
|
||||||
|
|
||||||
|
end architecture bench;
|
123
cocotb/cocovc_eth.vhd
Normal file
123
cocotb/cocovc_eth.vhd
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- 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;
|
Loading…
Reference in New Issue
Block a user