Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for bsg_serial_in_parallel_out #685

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions testing/bsg_dataflow/bsg_serial_in_parallel_out/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# This file is public domain, it can be freely copied without restrictions.
# SPDX-License-Identifier: CC0-1.0

# Cadenv Include
include /home/projects/ee478.2024spr/cadenv/cadenv_ee.mk

# CAD Tool Paths
VCS_BIN = $(VCS_HOME)/bin
VERDI_BIN = $(VERDI_HOME)/bin
VCS_BIN_DIR = $(VCS_BIN)
export PATH:=$(PATH):$(VCS_BIN):$(VERDI_BIN)

# VCS Arguments
EXTRA_ARGS += +v2k -l vcs.log
EXTRA_ARGS += -debug_pp +vcs+vcdpluson
EXTRA_ARGS += +lint=all,noSVA-UA,noSVA-NSVU,noVCDE,noNS -assert svaext
EXTRA_ARGS += -cm line+fsm+branch+cond+tgl
EXTRA_ARGS += -kdb -debug_access+all

# defaults
SIM ?= vcs
TOPLEVEL_LANG ?= verilog

# basejump_stl path
export BASEJUMP_STL_DIR = $(shell git rev-parse --show-toplevel)/../basejump_stl

# basejump_stl verilog header include path
EXTRA_ARGS += +incdir+$(BASEJUMP_STL_DIR)/bsg_misc

# basejump_stl verilog filelist
VERILOG_SOURCES += $(BASEJUMP_STL_DIR)/bsg_dataflow/bsg_serial_in_parallel_out.sv

# testbench verilog filelist
VERILOG_SOURCES += $(PWD)/bsg_serial_in_parallel_out_wrapper.sv
VERILOG_SOURCES += $(PWD)/bsg_serial_in_parallel_out_cov.sv

# TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file
TOPLEVEL = bsg_serial_in_parallel_out_wrapper

# MODULE is the basename of the Python test file
MODULE = bsg_serial_in_parallel_out_testbench

# include cocotb's make rules to take care of the simulator setup
include $(shell cocotb-config --makefiles)/Makefile.sim


# VERDI open waveform
ee-verdi:
verdi -ssf novas.fsdb &

# VERDI open coverages
ee-verdi-cov:
verdi -cov -covdir sim_build/simv.vdb &

# **DEPRECATED** DVE open waveform
ee-dve:
dve -full64 -vpd vcdplus.vpd &

# **DEPRECATED** DVE open coverages
ee-dve-cov:
dve -full64 -cov -covdir sim_build/simv.vdb &

# Clean simulation files
ee-clean:
make clean
rm -rf __pycache__ DVEfiles vcs.log vcdplus.vpd results.xml
rm -rf verdiLog vdCovLog novas.conf novas.fsdb novas.rc novas_dump.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//
// Paul Gao 08/2021
//
// This module defines functional coverages of module bsg_serial_in_parallel_out
//
//

`include "bsg_defines.sv"

module bsg_serial_in_parallel_out_cov

#(parameter width_p = "inv"
,parameter els_p = "inv"
,parameter out_els_p = els_p
)

( input clk_i
, input reset_i

// interface signals
, input valid_i

, input [$clog2(out_els_p+1)-1:0] yumi_cnt_i

// internal registers
, input [$clog2(els_p+1)-1:0] num_els_r
, input [els_p-1:0] valid_r

// ,input [ptr_width_lp-1:0] rptr_r
// ,input [ptr_width_lp-1:0] wptr_r
// ,input full // registered in sub-module bsg_fifo_tracker
// ,input empty // registered in sub-module bsg_fifo_tracker
// ,input read_write_same_addr_r
);

// reset
covergroup cg_reset @(negedge clk_i);
coverpoint reset_i;
endgroup

// Partitioning covergroup into smaller ones
// empty
covergroup cg_empty @ (negedge clk_i iff ~reset_i & num_els_r == 0);

cp_v: coverpoint valid_i;
cp_ready: coverpoint ready_and_o {illegal_bins ig0 = {0};}
// Cannot be something other than 0 (valid_i low) or 1 (valid_i high)
cp_yumi: coverpoint yumi_cnt_i {illegal_bins ig0 = {[2:$]};}
// Cannot be something other than 0 (valid_i low) or 1 (valid_i high)
cp_valid_o: coverpoint valid_o {illegal_bins ig0 = {[2:$]};}

cross_all: cross cp_v, cp_ready, cp_yumi, cp_valid_o {
illegal_bins ig0 = cross_all with (cp_valid_o == 0 & cp_yumi == 1);
illegal_bins ig1 = cross_all with (cp_v == 0 & cp_yumi == 1);

illegal_bins ig2 = cross_all with (cp_v == 0 & cp_valid_o == 1);
illegal_bins ig3 = cross_all with (cp_v == 1 & cp_valid_o == 0);
}
endgroup

// full
covergroup cg_full @ (negedge clk_i iff ~reset_i & num_els_r == out_els_p);

cp_v: coverpoint valid_i;
// cannot supply when full
cp_ready: coverpoint ready_and_o {illegal_bins ig0 = {1};}
// Cannot consume more than els_p values
cp_yumi: coverpoint yumi_cnt_i {illegal_bins ig0 = {[els_p+1:$]};}
// Cannot be something other 15
cp_valid_o: coverpoint valid_o {illegal_bins ig0 = {[0:(2 ** out_els_p) - 2]};} // TODO: not hardcoded?

cross_all: cross cp_v, cp_ready, cp_yumi, cp_valid_o {
}

endgroup

// fifo normal
covergroup cg_normal @ (negedge clk_i iff ~reset_i & 0 < num_els_r & num_els_r < out_els_p);

cp_v: coverpoint valid_i;
// must accept values in normal
cp_ready: coverpoint ready_and_o {illegal_bins ig0 = {0};}
// Cannot consume more than els_p values
cp_yumi: coverpoint yumi_cnt_i {illegal_bins ig0 = {[els_p+1:$]};}
cp_valid_o: coverpoint valid_o {illegal_bins ig0 = {0, 2, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14};} // TODO: not hardcoded?

cross_all: cross cp_v, cp_ready, cp_yumi, cp_valid_o {
// If no input data, we cannot read max objects
illegal_bins ig0 = cross_all with (!cp_v & cp_yumi == els_p);
// Cannot read more than existing objects
illegal_bins ig1 = cross_all with (cp_yumi > $clog2(cp_valid_o + 1));
// If no input data, cannot have max objects
illegal_bins ig2 = cross_all with (!cp_v & cp_valid_o == 15);

// Covered in empty
illegal_bins ig3 = cross_all with (cp_v & cp_valid_o == 1);
}
endgroup

// create cover groups
cg_reset cov_reset = new;
cg_empty cov_empty = new;
cg_full cov_full = new;
cg_normal cov_normal = new;

// print coverages when simulation is done
final
begin
$display("");
$display("Instance: %m");
$display("---------------------- Functional Coverage Results ----------------------");
$display("Reset functional coverage is %f%%", cov_reset.get_coverage());
$display("Sipo empty functional coverage is %f%%", cov_empty.cross_all.get_coverage());
$display("Sipo full functional coverage is %f%%", cov_full.cross_all.get_coverage());
$display("Sipo normal functional coverage is %f%%", cov_normal.cross_all.get_coverage());
// $display("Sipo impossible functional coverage is %f%%", cov_impossible.cross_all.get_coverage());
$display("-------------------------------------------------------------------------");
$display("");
end

endmodule
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Import python libraries
import math
import time
import random

# Import cocotb libraries
import cocotb
from cocotb.clock import Clock, Timer
from cocotb.triggers import RisingEdge, FallingEdge, Timer


# Data width, matching width_p parameter in DUT
WIDTH_P = 16

# Testbench iterations
ITERATION = 3500

# Flow control random seed
# Use different seeds on input and output sides for more randomness
CTRL_INPUT_SEED = 1
CTRL_OUTPUT_SEED = 2

# Testbench clock period
CLK_PERIOD = 10


async def input_side_testbench(dut, seed):
"""Handle input traffic"""

# Create local random generator for data generation
data_random = random.Random()
data_random.seed(seed)

# Create control random generator for flow control
control_random = random.Random()
control_random.seed(CTRL_INPUT_SEED)

# Initialize DUT interface values
dut.valid_i.value = 0
dut.data_i.value = 0

# Wait for reset deassertion
while 1:
await RisingEdge(dut.clk_i); await Timer(1, units="ps")
if dut.reset_i == 0: break

# Main iterations
i = 0
while 1:
await RisingEdge(dut.clk_i); await Timer(1, units="ps")
# Half chance to send data flit
if control_random.random() >= 0.5:
# Assert DUT valid signal
dut.valid_i.setimmediatevalue(1)
# Check DUT ready signal
if dut.ready_and_o == 1:
# Generate send data
dut.data_i.setimmediatevalue(math.floor(data_random.random()*pow(2, WIDTH_P)))
# iteration increment
i += 1
# Check iteration
if i == ITERATION:
# Test finished
break
else:
# Deassert DUT valid signal
dut.valid_i.setimmediatevalue(0)

await RisingEdge(dut.clk_i); await Timer(1, units="ps")
# Deassert DUT valid signal
dut.valid_i.value = 0


async def output_side_testbench(dut, seed):
"""Handle input traffic"""

# Create local random generator for data generation
data_random = random.Random()
data_random.seed(seed)

# Create control random generator for flow control
control_random = random.Random()
control_random.seed(CTRL_OUTPUT_SEED)

# Initialize DUT interface values
dut.yumi_cnt_i.value = 0

# Wait for reset deassertion
while 1:
await RisingEdge(dut.clk_i); await Timer(1, units="ps")
if dut.reset_i == 0: break

# Main iterations
i = 0
while 1:
await RisingEdge(dut.clk_i); await Timer(2, units="ps") # MUST RUN AFTER INPUT
if dut.valid_o.value != 0 and control_random.random() >= 0.2:
available = int(math.log2(dut.valid_o.value + 1))
to_take = control_random.choice(range(available + 1))
# print(f"Available: {available}, taking: {to_take}")

# Assert DUT yumi signal
dut.yumi_cnt_i.setimmediatevalue(to_take)

# Generate check data and compare with receive data
for j in range(to_take):
value = math.floor(data_random.random()*pow(2, WIDTH_P))
# print(f"{dut.data_o[j].value} == {value}")
assert dut.data_o[j].value == value, "data mismatch!"
# print()

# iteration increment
i += to_take
# Check iteration
if i == ITERATION:
# Test finished
break
else:
# Deassert DUT yumi signal
dut.yumi_cnt_i.setimmediatevalue(0)

await RisingEdge(dut.clk_i); await Timer(1, units="ps")
# Deassert DUT yumi signal
dut.yumi_cnt_i.value = 0


@cocotb.test()
async def testbench(dut):
"""Try accessing the design."""

# Random seed assignment
seed = time.time()

# Create a 10ps period clock on DUT port clk_i
clock = Clock(dut.clk_i, CLK_PERIOD, units="ps")

# Start the clock. Start it low to avoid issues on the first RisingEdge
clock_thread = cocotb.start_soon(clock.start(start_high=False))

# Launch input and output testbench threads
input_thread = cocotb.start_soon(input_side_testbench(dut, seed))
output_thread = cocotb.start_soon(output_side_testbench(dut, seed))

# Reset initialization
dut.reset_i.value = 1

# Wait for 5 clock cycles
await Timer(CLK_PERIOD*5, units="ps")
await RisingEdge(dut.clk_i); await Timer(1, units="ps")

# Deassert reset
dut.reset_i.value = 0

# Wait for threads to finish
await input_thread
await output_thread

# Wait for 5 clock cycles
await Timer(CLK_PERIOD*5, units="ps")
await RisingEdge(dut.clk_i); await Timer(1, units="ps")

# Assert reset
dut.reset_i.value = 1

# Wait for 5 clock cycles
await Timer(CLK_PERIOD*5, units="ps")

# Test finished!
dut._log.info("Test finished! Current reset_i value = %s", dut.reset_i.value)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

module bsg_serial_in_parallel_out_wrapper #(parameter width_p = 16
, parameter els_p = 4
, parameter out_els_p = els_p
)
( input clk_i
, input reset_i
, input valid_i
, input [width_p-1:0] data_i
, output ready_and_o

, output logic [out_els_p-1:0] valid_o
, output logic [out_els_p-1:0][width_p-1:0] data_o

, input [$clog2(out_els_p+1)-1:0] yumi_cnt_i
);

// Instantiate DUT
bsg_serial_in_parallel_out #(.width_p(width_p)
,.els_p(els_p)
,.out_els_p(out_els_p)
) sipo
(.*);

// Bind Covergroups
bind bsg_serial_in_parallel_out bsg_serial_in_parallel_out_cov
#(.width_p(width_p)
,.els_p(els_p)
,.out_els_p(out_els_p)
) pc_cov
(.*
);

// Dump Waveforms
initial begin
$fsdbDumpvars;
end

endmodule