Integrated Test
Integrated test can be marked by #[test(test_name)] attribute.
The marked block will be identified as test, and executed through veryl test command.
There are three ways to describe integrated test:
- Native test
- SystemVerilog test
- cocotb test
Native test uses Veryl’s built-in simulator, while SystemVerilog test and cocotb test use an external RTL simulator.
About external RTL simulators used by veryl test, see Simulator.
For all test types, if --wave option is specified, waveforms are generated.
Tests can be skipped by adding #[ignore] attribute.
Ignored tests are not executed by default, but can be run with --ignored option.
--include-ignored option runs both normal and ignored tests.
#[test(test_ignored)]
#[ignore]
module test_ignored {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen;
initial {
rst.assert(clk);
$finish ();
}
}
Native test
Native test allows writing testbenches directly in Veryl without embedding SystemVerilog or using external frameworks.
A module with #[test(test_name)] attribute (without embed declaration) is treated as a native test.
The following testbench components are available:
$tb::clock_gen— clock signal generator (with optional#(period: N)parameter)$tb::reset_gen— reset signal generator (with optional#(cycles: N)parameter)
And the following system functions can be used in initial blocks:
$assert(condition)or$assert(condition, message)— check assertions during simulation$finish()— terminate simulation
Basic example
module Counter (
clk: input clock ,
rst: input reset ,
cnt: output logic<32>,
) {
always_ff {
if_reset {
cnt = 0;
} else {
cnt += 1;
}
}
}
#[test(test_counter)]
module test_counter {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen;
var cnt: logic<32>;
inst dut: Counter (
clk: clk,
rst: rst,
cnt: cnt,
);
initial {
rst.assert(clk);
clk.next (10);
$assert (cnt == 32'd10);
$finish ();
}
}
Testbench methods
clock_gen provides the next method to advance clock cycles:
clk.next()— advance clock by 1 cycleclk.next(N)— advance clock byNcycles
reset_gen provides the assert method to assert reset:
rst.assert(clk)— assert reset synchronized to clockrst.assert(clk, duration)— assert reset for specified duration
The reset duration can also be configured at instantiation:
#[test(test_reset_cycles_param)]
module test_reset_cycles_param {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen #( cycles: 5 );
// ...
initial {
rst.assert(clk);
// ...
}
}
Function calls in testbench
Testbench methods like clk.next can be called from user-defined functions:
#[test(test_function_call)]
module test_function_call {
inst clk: $tb::clock_gen;
inst rst: $tb::reset_gen;
var cnt: logic<32>;
// inst dut: Counter (clk, rst, cnt);
function step_n (
n: input logic<32>,
) {
clk.next(n);
}
initial {
rst.assert(clk);
step_n (5);
step_n (5);
$assert (cnt == 32'd10);
$finish ();
}
}
SystemVerilog test
SystemVerilog test can be described with inline specifier.
The top level module of the block must have the same name as the test name.
The messages through $info, $warning, $error and $fatal system function are handled by Veryl compiler, and shown as exectution log.
The calls of $error and $fatal are treated as test failure.
The following example, a SystemVerilog source code embedded by embed declaration are marked as test.
#[test(test1)]
embed (inline) sv{{{
module test1;
initial begin
assert (0) else $error("error");
end
endmodule
}}}
cocotb test
cocotb test can be described with cocotb specifier.
The target module name for test should be specified by the second argument of #[test] attribute.
#[test(test1, ModuleA)]
embed (cocotb) py{{{
# cocotb code
}}}