bench: Add cocotb based test bench
This commit is contained in:
parent
5c27b685dc
commit
64f6197dbb
4
cocotb/.gitignore
vendored
Normal file
4
cocotb/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.pyc
|
||||
*.o
|
||||
results.xml
|
||||
*.cf
|
14
cocotb/Makefile
Normal file
14
cocotb/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# defaults
|
||||
SIM ?= ghdl
|
||||
TOPLEVEL_LANG ?= vhdl
|
||||
BASE = $(PWD)/..
|
||||
|
||||
COMPILE_ARGS=--std=08
|
||||
|
||||
VHDL_SOURCES_trashernet += $(BASE)/trashernet/*.vhd
|
||||
VHDL_SOURCES_bench += $(BASE)/cocotb/*.vhd
|
||||
|
||||
TOPLEVEL = bench.cocotb_trashernet_phy
|
||||
MODULE = cocotb_trashernet_phy
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
90
cocotb/cocotb_trashernet_phy.py
Normal file
90
cocotb/cocotb_trashernet_phy.py
Normal file
@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import cocotb
|
||||
from cocotb.triggers import *
|
||||
from cocotb.result import *
|
||||
|
||||
from cocotb.queue import Queue
|
||||
|
||||
# DUT interface (internal)
|
||||
tb_rx_queue = Queue()
|
||||
tb_rx_queue_done_ev = Event()
|
||||
|
||||
async def dut_receiver(dut):
|
||||
scoreboard = tb_rx_queue
|
||||
while True:
|
||||
await Edge(dut.coco_dut_rxs)
|
||||
byte = dut.coco_dut_rxd.value
|
||||
dut._log.info("[DT:RX <-] Byte %02X", byte)
|
||||
assert not scoreboard.empty(), "Data was provided on the RX interface even though none was expected"
|
||||
if not scoreboard.empty():
|
||||
sb_byte = await scoreboard.get()
|
||||
assert byte == sb_byte, f"Received {byte} instead of {sb_byte}"
|
||||
|
||||
async def dut_rxa(dut):
|
||||
scoreboard = tb_rx_queue
|
||||
await Edge(dut.coco_dut_rxa) # TODO: Workaround for some stupid bug somewhere
|
||||
while True:
|
||||
await Edge(dut.coco_dut_rxa)
|
||||
if dut.coco_dut_rxa.value:
|
||||
dut._log.info("[DT:RX <-] Start of frame")
|
||||
else:
|
||||
dut._log.info("[DT:RX <-] End of frame")
|
||||
if scoreboard.empty():
|
||||
tb_rx_queue_done_ev.set()
|
||||
|
||||
# TB / ETH interface (external)
|
||||
tb_tx_queue = Queue()
|
||||
|
||||
async def tb_transmitter(dut):
|
||||
queue = tb_tx_queue
|
||||
while True:
|
||||
byte = await queue.get()
|
||||
if byte == -1:
|
||||
dut._log.info("[TB:TX ->] Wait IPG")
|
||||
await Timer(16, units="us") # IPG
|
||||
else: # TX byte
|
||||
dut._log.info("[TB:TX ->] Byte %02X", byte)
|
||||
dut.coco_tb_txd.value = byte
|
||||
dut.coco_tb_txs.value = not dut.coco_tb_txs.value
|
||||
await Edge(dut.coco_tb_txa);
|
||||
|
||||
async def tb_send_packet(dut, data):
|
||||
queue = tb_tx_queue
|
||||
scoreboard = tb_rx_queue
|
||||
|
||||
queue.put_nowait(0x55)
|
||||
queue.put_nowait(0x55)
|
||||
queue.put_nowait(0x55)
|
||||
queue.put_nowait(0xD5)
|
||||
|
||||
for byte in data:
|
||||
scoreboard.put_nowait(byte)
|
||||
queue.put_nowait(byte)
|
||||
|
||||
queue.put_nowait(-1)
|
||||
|
||||
async def timeout(dut):
|
||||
await Timer(50, units="us")
|
||||
assert False, "Timeout"
|
||||
|
||||
@cocotb.test()
|
||||
async def simple_rx(dut):
|
||||
"""Receive a packet using trashernet."""
|
||||
|
||||
await cocotb.start(timeout(dut))
|
||||
|
||||
await cocotb.start(dut_receiver(dut))
|
||||
await cocotb.start(dut_rxa(dut))
|
||||
await cocotb.start(tb_transmitter(dut))
|
||||
|
||||
dut.rst.value = 1
|
||||
await Timer(20, units="ns")
|
||||
dut.rst.value = 0
|
||||
await Timer(20, units="ns")
|
||||
|
||||
await tb_send_packet(dut, b'\x02\x03\x05\x07')
|
||||
|
||||
await cocotb.start(timeout(dut))
|
||||
|
||||
await tb_rx_queue_done_ev.wait()
|
104
cocotb/cocotb_trashernet_phy.vhd
Normal file
104
cocotb/cocotb_trashernet_phy.vhd
Normal file
@ -0,0 +1,104 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- bench_trashernet_phy.vhd : Cocotb test bench for the 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;
|
||||
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
entity cocotb_trashernet_phy is
|
||||
end entity cocotb_trashernet_phy;
|
||||
|
||||
architecture bench of cocotb_trashernet_phy is
|
||||
signal clk : std_logic;
|
||||
signal phy_clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
signal rx_p : std_logic;
|
||||
signal tx_p : std_logic;
|
||||
signal tx_n : std_logic;
|
||||
|
||||
-- Cocotb interface
|
||||
signal coco_dut_rxd : std_logic_vector(7 downto 0); -- RX data
|
||||
signal coco_dut_rxs : std_logic := '0'; -- RX toggle
|
||||
signal coco_dut_rxa : std_logic; -- RX active
|
||||
|
||||
signal coco_tb_txd : std_logic_vector(7 downto 0); -- TX data
|
||||
signal coco_tb_txs : std_logic := '0'; -- TX toggle
|
||||
signal coco_tb_txa : std_logic := '0'; -- TX done toggle
|
||||
|
||||
begin
|
||||
-- Instantiate design
|
||||
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
|
||||
generic map(
|
||||
F_CLK => 49000000,
|
||||
F_CLK_PHY => 100000000
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
phy_clk => phy_clk,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
-- Create infrastructure
|
||||
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;
|
||||
|
||||
phy_clock_driver : process
|
||||
constant period : time := 10 ns;
|
||||
begin
|
||||
phy_clk <= '0';
|
||||
wait for period / 2;
|
||||
phy_clk <= '1';
|
||||
wait for period / 2;
|
||||
end process phy_clock_driver;
|
||||
|
||||
-- Cocotb adapter (ETH)
|
||||
test : process is
|
||||
begin
|
||||
rx_p <= '0';
|
||||
|
||||
loop
|
||||
wait on coco_tb_txs;
|
||||
|
||||
for j in coco_tb_txd'low to coco_tb_txd'high loop
|
||||
rx_p <= not coco_tb_txd(j);
|
||||
wait for 50 ns;
|
||||
rx_p <= coco_tb_txd(j);
|
||||
wait for 50 ns;
|
||||
end loop;
|
||||
coco_tb_txa <= not coco_tb_txa;
|
||||
end loop;
|
||||
end process test;
|
||||
|
||||
receiver : process is
|
||||
begin
|
||||
wait until rising_edge(clk);
|
||||
if phy_out.rx_data_valid then
|
||||
coco_dut_rxd <= phy_out.rx_data;
|
||||
coco_dut_rxs <= not coco_dut_rxs;
|
||||
end if;
|
||||
end process receiver;
|
||||
coco_dut_rxa <= phy_out.rx_active;
|
||||
end architecture bench;
|
Loading…
Reference in New Issue
Block a user