Compare commits

...

2 Commits

Author SHA1 Message Date
Markus Koch 9f2f3223e5 add micobu ci test 2022-04-18 09:13:15 +02:00
Markus Koch 64f6197dbb bench: Add cocotb based test bench 2022-02-25 21:14:45 +01:00
6 changed files with 224 additions and 3 deletions

View File

@ -18,10 +18,10 @@ use work.bench_pkg.all;
library trashernet;
use trashernet.trashernet_pkg.all;
entity bench_trashernet_phy is
end entity bench_trashernet_phy;
entity cocotb_trashernet_phy is
end entity cocotb_trashernet_phy;
architecture bench of bench_trashernet_phy is
architecture bench of cocotb_trashernet_phy is
signal clk : std_logic;
signal phy_clk : std_logic;
signal rst : std_logic;

4
cocotb/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.pyc
*.o
results.xml
*.cf

14
cocotb/Makefile Normal file
View 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 += $(BASE)/cocotb/*.vhd
TOPLEVEL = cocotb_trashernet_phy
MODULE = cocotb_trashernet_phy
include $(shell cocotb-config --makefiles)/Makefile.sim

View 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()

View 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;

9
micobu.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
cd cocotb
make
RET=$?
mv ./* -t $MCB_ARTEFACTS/
exit $RET