-
-
Notifications
You must be signed in to change notification settings - Fork 22
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
Minimal support of unit test #550
Comments
I would also like to add the possible concept of a native Rust component as a DPI function to be called into the testbench. Veryl compiler would process the .veryl file as normal, but also separate out any #Pragma__Rust DPI component into a compiled .so library so it could be linked with a verilator simulation. An example testbench is shown below. Note that this testbench is in the same Veryl module as the design. Also note the example uses psuedo-code for brevity. Finally the [#derive(dual_use)] macro would allow the struct to be translated to SystemVerilog but also used for Rust .so compile. #[testbench]
// A standard type way
import "DPI-C" function u32 triple_add (input u32 a, input u32 b, input u32 c);
// OR another way
import "DPI-Functions" // since the DPI function is attached to this module; Veryl would know to include it.
module tb {
// instantiate DUT as dut
// clock generator code ...
// reset generator code ...
#[derive(dual_use, rust_component)]
struct Dut {
a : u32,
b : u32,
c : u32,
result : u32,
}
#Pragma__Rust
impl Dut {
pub fn triple_add (&mut self) {
self.result = (self.a + self.b + self.c) >> 5
}
}
#Pragma_End
var dut : Dut;
// main loop
while index < NUM {
dut.a = some_value;
dut.b = some_value_b;
dut.c = some_value_c;
delay_some_clock_cycles;
assert(dut.result == triple_add(some_value, some_value_b, some_value_c, "adder failure!");
} |
Looks good! Syntax to embed other languages seems to be required too. inline!("rust") {
// Rust code
}
inline!("sv") {
// SystemVerilog code
}
inline!("cocotb") {
// Python code
}
include!("rust", "test.rs");
include!("sv", "test.sv");
include!("cocotb", "test.py"); Additionally the |
The inline! macro does look like a very good approach, and is also flexible with placing code within the original source file and also in external files. Very effective feature to handle multiple languages in one file. The only issue that I would see is that if you inline python, the target output should be a separate file (versus the SystemVerilog that is transpiled from the Veryl code) Perhaps some code syntax needs to be incorporated that tells Veryl where to place the output of inline! macro - and how to import it into the final SystemVerilog output. An idea is shown below. inline!("rust") {
#[derive(dpi_import)] /// generate a .so shared object, but also import into final SystemVerilog target
/// some function here
}
However, if one wanted to manually create a large amount of DPI Rust functions in a separate file, this would not be necessary if the user wanted to manually import into a SystemVerilog file. |
Sure. embed!("sv", "inline") {
}
embed!("python", "cocotb") {
}
embed!("rust", "dpi_import") {
} |
The embed! macro seems like a very good approach. The syntax appears to be of the form "embed! (arg1=lang, arg2=file_target)" This approach would allow for future expansion if needed, like for example "embed! (arg1=lang, arg2=file_target, arg3=sim_target)" So extending this to assertions: embed!("sv", "inline", "assert") {
property req_gnt;
@(posedge clk) req |=> gnt;
endproperty
assert_req_gnt: assert property (req_gnt) else $error("req not followed by gnt.");
} The assert argument would be used if simulating with verilator, but something like arg3 could default to "none". |
First of all, I'll try to implement the following two SystemVerilog tests. // external SystemVerilog test
#[testbench]
include!("tb.sv", "inline");
#[test]
include!("test1.sv", "inline");
// embedded SystemVerilog test
#[testbench]
embed!("sv", "inline") {
// SystemVerilog code
}
#[test]
embed!("sv", "inline") {
// SystemVerilog code
}
// external cocotb test
#[test]
include!("test1.py", "cocotb");
// embedded cocotb test
#[test]
embed!("python", "cocotb") {
// Python code
} |
I explored the syntax implementation of For example, the following code block can be identified by embed("inline") {sv{
// SystemVerilog code
}sv} |
That marking of the code blocks as you posted above appears very reasonable; such that someone adding this code in the main veryl file has a short and efficient syntax to mark code as sv. Another idea is to use double under syntax as shown below. Perhaps the end sv symbol could be removed (as shown), and the double under syntax could be made as as "private attribute" in Veryl similar to Python. embed("inline") { __sv__ {
// SystemVerilog code
}} |
A simple end pattern causes mistake of syntax highlighting engine because the engine usually uses simple regex pattern matching.
So there is trade-off like " // Markdown code block style
embed("inline") ```sv
// SystemVerilog code
```
// C# Raw string literal style
embed("inline") sv"""
// SystemVerilog code
""" |
The markdown style syntax seems a little more clear. But I wanted to see what this would look like, and generated a quick example below where is an embedded rust snippet, that is called as DPI function in the embedded SV snippet. The flow that I use most often is SystemVerilog --> Cocotb --> Verilator; however, after using this flow #[test]
//----------------------------
// Embed SV test case
//----------------------------
embed("inline") rust"""
// This is a DPI function, meant to be called
// by systemverilog testbench for immediate
// verification of the adder result. The
// no_mangle is used for C abi.
#[no_mangle]
pub fn adder_check(arg_a : i32, arg_b : i32) -> i32 {
arg_a + arg_b
}
/// Note that the above function could be much more
/// complicated; this is where the value of a DPI
/// function comes in, e.g. something like a Taylor
/// series expansion or matrix multiplier i.e.
#[no_mangle]
pub fn taylor_cosine_check (x : i32) -> i32 {
1 - 0.5*(x**2) + (1/24)*(x**4) + . . . .
}
""" embed("inline") sv""" module adder_tb;
/// Basic testbench for the adder in the alu
// import the C-ABI Rust DPI function declared earlier
import "DPI" function int adder_check(input int i_adder_arg_a, input int i_adder_arg_b);
logic i_clock, i_reset
int i_adder_arg_a, i_adder_arg_b;
int o_adder_result;
veryl_adder uAdd (.*); // instantiate the DUT, transpiled from Veryl
initial begin
i_clock <= 0;
end
always begin
#10 i_clock <= !i_clock;
end
initial begin
i_reset <= 0;
#10 i_reset <= 1'b0;
#40 i_reset <= 1'b1;
#100 i_reset <= 1'b0;
#100;
i_adder_arg_a = 10;
i_adder_arg_b = 35;
#2;
$assert (dut.o_adder_result == adder_check(adder_arg_a, adder_arg_b));
$finish(2);
end
endmodule """ |
The current idea is like below: // "dpi_rust" means that the code block will be compiled as Rust to a shared object,
// and the object will be loaded as a DPI module on RTL simulator.
embed("dpi_rust") rust"""
#[no_mangle]
pub fn adder_check(arg_a : i32, arg_b : i32) -> i32 {
arg_a + arg_b
}
"""
// `#[test]` means that the annotated block will be enabled at test only.
// If there are some annotated blocks, they are executed as individual tests.
// "inline" means that the code block will be expanded to the transpiled code as is.
#[test(test_name)]
embed("inline") sv"""
module test;
import "DPI" function int adder_check(input int i_adder_arg_a, input int i_adder_arg_b);
logic i_clock, i_reset
int i_adder_arg_a, i_adder_arg_b;
int o_adder_result;
veryl_adder uAdd (.*);
initial begin
i_clock <= 0;
end
always begin
#10 i_clock <= !i_clock;
end
initial begin
i_reset <= 0;
#10 i_reset <= 1'b0;
#40 i_reset <= 1'b1;
#100 i_reset <= 1'b0;
#100;
i_adder_arg_a = 10;
i_adder_arg_b = 35;
#2;
$assert (dut.o_adder_result == adder_check(adder_arg_a, adder_arg_b));
$finish(2);
end
endmodule
""" |
The combined #[test] and embed("inline") markers definitely convey the functionality and the overall syntax is clear, looks good. |
I've rethought about code block syntax.
|
One more minor update.
|
I've added
|
That is excellent work, look forward to testing this feature with verilator. |
I've merged #648. |
Unit test feature has many consideration points from simulation only description like delay and clocking to testing framework like UVM.
I'll add minimal support of unit test because these consideration takes many time.
This proposal is syntax to include tests which are written by other languages.
I think SystemVerilog and cocotb are appropriate candidates.
Especially cocotb may be suitable with unit test because it doesn't required testbench module.
veryl test
command like below can be implemented if these syntax is added.In the future, Veryl native test will be written like below:
Related: #209 #164
The text was updated successfully, but these errors were encountered: