Commit 17ac0f43 authored by Konstantinos Blantos's avatar Konstantinos Blantos

First draft of the demo of gc_edge_detect TB using VUnit

parent 3051df4c
This is a generic testbench which checks the width of the ouput pulse, depending on the given generics in the beginning of the simulation. The output pulse is high in the falling or in the rising edge of the input pulse.
# Testbench for [gc_edge_detect](../../../modules/common/gc_edge_detect.vhd) common core
## Requirements for Linux
1. Install [VUnit](https://vunit.github.io/installing.html).
2. Install [GHDL](https://github.com/ghdl/ghdl/releases). Note that GHDL with GCC backend is required for code coverage. For Ubuntu 20.04 it works with gcc 9.4.
## Testbench description
The testbench is running using VUnit and it uses OSVVM for randomization of the inputs and for basic coverage. It checks the width of the ouput pulse, depending on the given generics in the beginning of the simulation. It also verifys if the the output pulse is high in the falling or in the rising edge of the input pulse.
The generics of this core are the following:
- g_async_rst : Synchronous or asynchronous reset
- g_pulse_edge : Detect of positive or negative edges
- g_clock_edge : Clock edge sensitivity of edge detection
Regarding on the values of the above generics, there are the following test cases:
1. TRUE, positive, positive
2. TRUE, positive, negative
3. TRUE, negative, negative
4. TRUE, negative, negative
5. FALSE, positive, positive
6. FALSE, positive, negative
7. FALSE, negative, positive
8. FALSE, negative, negative
The testbench is using OSVVM for randomization of the inputs and for basic coverage. Simple assertions are using, to verify that the output is the same as the input, after one clock.
| Generics | Type | Values |
|--------------|:----------:|----------------:|
| g_async_rst | Boolean | True/False |
| g_pulse_edge | String |Positive/Negative|
| g_clock_edge | String |Positive/Negative|
Regarding on the values of the above generics and their combinations, there are 8 different test cases.
## How to run the testbench
This testbench has been tested using GHDL and Aldec Riviera Pro. VUnit will select the simulator based on the specified `$PATH`. So the same commands are used to run the testbench, regardless the simulator.
```
python3 run.py -v
```
Note: Each testbench using a common python module `sim_utils.py` which includes various functions.
## Basic information for VUnit
1. Basic arguments for VUnit `python3 run.py`
- `--list` for viewing the list of the tests
- `-v` for verbode run
- `--gtkwave-fmt=(vcd,ghw)` for generating a waveform file (only when using GHDL)
- `-g <name of the test>` for opening in GUI the running test
2. Each testbench requires a `run.py` file
#!/usr/bin/env python3
import subprocess
import shutil
import sys
import os
from pathlib import Path
from itertools import product
from vunit import VUnit
from subprocess import call
from vunit.sim_if.factory import SIMULATOR_FACTORY
import random
# Fetch the path where file exists and specify the path
# for the RTL design and Testbench
ROOT = Path(__file__).resolve().parent
RTL_PATH = ROOT / "../../../modules/common/"
TB_PATH = ROOT
# Find where the sim_utils.py is or any other python package
sys.path.append(str(ROOT))
import sim_utils
# Create VUnit instance
VU = VUnit.from_argv(compile_builtins=False)
VU.add_vhdl_builtins()
VU.add_osvvm()
VU.add_verification_components()
# Create design library
common_lib = VU.add_library("common_lib")
# Add design source files to design lib
# Define the groups of the files
src_files_list = ["gc_edge_detect.vhd"]
testbench_list = ["tb_gc_edge_detect.vhd"]
# Add the RTL source files in the common_lib
for src_file in src_files_list:
common_lib.add_source_files(RTL_PATH/src_file)
# Add the Testbench source files in the common_lib
for tb_file in testbench_list:
common_lib.add_source_files(TB_PATH/tb_file)
# Specify constant values to some generics
for tb in common_lib.get_test_benches():
tb.set_generic("g_seed", random.randrange(2**31))
tb.set_generic("g_clk_cycles", 200)
# Set the value of a generic
tb_gc_edge_detect=common_lib.test_bench("tb_gc_edge_detect");
for g_ASYNC_RST,g_PULSE_EDGE,g_CLOCK_EDGE in product([False, True], ['positive','negative'], ['positive', 'negative']):
tb_gc_edge_detect.add_config(
name=f"g_ASYNC_RST={g_ASYNC_RST}.g_PULSE_EDGE={g_PULSE_EDGE}.g_CLOCK_EDGE={g_CLOCK_EDGE}",
generics=dict(g_ASYNC_RST=g_ASYNC_RST,g_PULSE_EDGE=g_PULSE_EDGE,g_CLOCK_EDGE=g_CLOCK_EDGE))
# Define arguments for compile and simulation
common_lib.set_compile_option('ghdl.a_flags', ['--std=08', '-frelaxed', '-Wc,-fprofile-arcs', '-Wc,-ftest-coverage'])
common_lib.set_sim_option("ghdl.elab_flags", ['-frelaxed-rules', '-fsynopsys', '-Wl,-lgcov'])
common_lib.set_sim_option("ghdl.sim_flags", ['--ieee-asserts=disable'])
sim_utils.vunit_enable_coverage(common_lib)
VU.main(post_run=sim_utils.vunit_postrun(Path(__file__).resolve()))
This diff is collapsed.
--------------------------------------------------------------------------------
-- SPDX-FileCopyrightText: 2022 CERN (home.cern)
--
-- SPDX-License-Identifier: CERN-OHL-W-2.0+
--------------------------------------------------------------------------------
-- CERN BE-CEM-EDL
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
......@@ -11,31 +15,27 @@
-- description: testbench for simple edge detector
--
--------------------------------------------------------------------------------
-- Copyright CERN 2014-2020
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- OSVVM library
library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.vc_context;
library osvvm;
use osvvm.RandomPkg.all;
use osvvm.CoveragePkg.all;
library common_lib;
entity tb_gc_edge_detect is
generic (
runner_cfg : string;
g_seed : natural;
g_clk_cycles : natural;
g_ASYNC_RST : boolean := FALSE;
g_PULSE_EDGE : string := "positive";
g_CLOCK_EDGE : string := "positive");
......@@ -49,104 +49,102 @@ architecture tb of tb_gc_edge_detect is
-- Signals
signal tb_clk_i : std_logic;
signal tb_rst_i : std_logic;
signal tb_d_i : std_logic := '0';
signal tb_d_i : std_logic;
signal tb_pulse_o : std_logic;
signal stop : boolean;
signal stop : boolean := false;
-- Shared variables, used for coverage
shared variable cp_rst_i : covPType;
shared variable rnd : RandomPType;
begin
-- Unit Under Test
UUT : entity work.gc_edge_detect
generic map (
g_ASYNC_RST => g_ASYNC_RST,
g_PULSE_EDGE => g_PULSE_EDGE,
g_CLOCK_EDGE => g_CLOCK_EDGE)
port map (
clk_i => tb_clk_i,
rst_n_i => tb_rst_i,
data_i => tb_d_i,
pulse_o => tb_pulse_o);
-- Clock generation
clk_proc : process
p_clk_proc : process
begin
while STOP = FALSE loop
while true loop
tb_clk_i <= '1';
wait for C_CLK_PERIOD/2;
tb_clk_i <= '0';
wait for C_CLK_PERIOD/2;
end loop;
wait;
end process clk_proc;
end process p_clk_proc;
-- Reset generation
tb_rst_i <= '0', '1' after 4*C_CLK_PERIOD;
-- Stimulus
stim : process
variable data : RandomPType;
variable ncycles : natural;
tb_rst_i <= '0', '1' after 10*C_CLK_PERIOD;
--------------------------------------------------------------------------
--! Main Test Process required by VUnit
p_main_test : process
begin
data.InitSeed(g_seed);
report "[STARTING] with seed = " & to_string(g_seed);
while NOW < 4 ms loop
wait until (rising_edge(tb_clk_i) and tb_rst_i = '1');
tb_d_i <= data.randSlv(1)(1);
ncycles := ncycles + 1;
test_runner_setup(runner, runner_cfg);
rnd.InitSeed(g_seed);
info("[STARTING] Seed = " & integer'image(g_seed));
while test_suite loop
reset_checker_stat;
if run("test_edge_detect") then
tb_d_i <= '0';
wait until rising_edge(tb_rst_i);
for i in 0 to g_clk_cycles-1 loop
wait until rising_edge(tb_clk_i);
tb_d_i <= rnd.randSlv(1)(1);
end loop;
end if;
end loop;
report "Number of simulation cycles = " & to_string(ncycles);
report "Test PASS!";
stop <= TRUE;
wait;
end process;
stop <= true;
test_runner_cleanup(runner);
end process p_main_test;
-- Timeout watchdog (optional)
test_runner_watchdog(runner, 2 ms);
--sets up coverpoint bins
init_coverage : process
p_init_coverage : process
begin
cp_rst_i.AddBins("reset has asserted", ONE_BIN);
wait;
end process init_coverage;
end process p_init_coverage;
--Assertion to check that the width of the output pulse
--is asserted for only one clock cycle
one_clk_width : process
--Assertion to check that the width of the output
--pulse is asserted for only one clock cycle
p_one_clk_width : process
begin
if rising_edge(tb_clk_i) then
if tb_pulse_o = '1' then
wait for C_CLK_PERIOD;
assert (tb_pulse_o = '0')
report "output pulse remains high for more than one clock"
severity failure;
while true loop
wait until rising_edge(tb_pulse_o);
wait until rising_edge(tb_clk_i);
wait for 1 ps; --minor time delay just for the simulator to detect the change
check_equal(tb_pulse_o,'0',"Wrond duration of the output pulse");
end loop;
end process p_one_clk_width;
--Assertion to verify the output based on the clock edge
gen_pos_edge : if g_CLOCK_EDGE = "positive" generate
p_pos_check_output : process
begin
wait until rising_edge(tb_clk_i);
if rising_edge(tb_d_i) then
check_equal(tb_pulse_o,'1',"Pulse not detected in positive clk edge");
end if;
end if;
wait;
end process;
end process p_pos_check_output;
--Assertion to check that output is the same as input
--after one clock cycle
check_output : process
begin
if rising_edge(tb_clk_i) then
if tb_d_i /= tb_pulse_o then
wait for C_CLK_PERIOD;
assert (tb_d_i = tb_pulse_o)
report "Input and Output signals are different"
severity failure;
else
wait for C_CLK_PERIOD;
assert (tb_d_i /= tb_pulse_o)
report "Input and Output signals still the same"
severity failure;
end generate gen_pos_edge;
gen_neg_edge : if g_CLOCK_EDGE = "negative" generate
p_neg_check_output : process
begin
wait until rising_edge(tb_clk_i);
if falling_edge(tb_d_i) then
check_equal(tb_pulse_o,'1',"Pulse not detected when negative clk edge");
end if;
end if;
wait;
end process check_output;
end process p_neg_check_output;
end generate gen_neg_edge;
sample : process
p_sample_cov : process
begin
loop
wait on tb_rst_i;
......@@ -154,12 +152,26 @@ begin
--sample the coverpoints
cp_rst_i.ICover(to_integer(tb_rst_i = '1'));
end loop;
end process sample;
end process p_sample_cov;
cover_report: process
p_cover_report: process
begin
wait until stop;
report"**** Coverage Report ****";
cp_rst_i.writebin;
end process;
report "";
end process p_cover_report;
-- Unit Under Test
UUT : entity common_lib.gc_edge_detect
generic map (
g_ASYNC_RST => g_ASYNC_RST,
g_PULSE_EDGE => g_PULSE_EDGE,
g_CLOCK_EDGE => g_CLOCK_EDGE)
port map (
clk_i => tb_clk_i,
rst_n_i => tb_rst_i,
data_i => tb_d_i,
pulse_o => tb_pulse_o);
end tb;
end architecture tb;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment