Closed dalance closed 6 months ago
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! If not only generating expected value but also whole testbench can be written by Rust, it seems to be better. Then verification framework like UVM can be constructed by Rust.
Syntax to embed other languages seems to be required too. Rust macro style will be like below?
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 inline!("sv")
can be used in the design for rare case description which can't be represented by Veryl like inline assembly.
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.
The inline
keyword may not be suitable because it means a way for integration.
Is it better that both language and integration way are specified like below?
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. The second is cocotb integration.
// 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 embed
.
So I found the code blocks of foreign language should be marked more clearly.
This is because editors supporting embedded language search the embedded code block by a simple regex.
For example, the following code block can be identified by {sv{
and }sv}
, and these pattern probably don't appear in SystemVerilog code.
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.
embed("inline") { __sv__ {
assign a = {1{1'b1}}; // This `}}` will be identified as the end of code block
}}
So there is trade-off like "}sv}
is ugly, but difficult to make mistake" and "}}
is clean, but easy to make mistake".
We need to explore an appropriate compromise.
// 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 many times, I still prefer to have a "basic" testbench module that is pure SV. The example below takes a look at how this would work, where all of this would be in the #[test] portion of the Veryl main module.
#[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.
"""
or ```
can't check match of open and close, it causes syntax ambiguous.
So {{{
and }}}
seems to be better.
If there is }}}
in code block, } } }
can be used instead of it to avoid syntax highlight error.
#[test(test_name)]
embed ("inline") sv{{{
// SystemVerilog code
assign a = {1{1{1'b1}}}; // highlight error, but not syntax error
assign a = {1{1{1'b1} } }; // no error on both highlight and syntax
}}}
One more minor update.
The way of embedding is not an arbitrary string, but one of the pre-defined candidates.
So embed(inline)
may be better than embed("inline")
.
#[test(test_name)]
embed (inline) sv{{{
// SystemVerilog code
}}}
I've added veryl test
command at #648.
Currently Verilator and Synopsys VCS are supported.
$ veryl test testcases/veryl/48_test.veryl
[INFO ] Processing file (testcases/veryl/48_test.veryl)
[INFO ] Processing file (/home/hatta/.cache/veryl/dependencies/f2cfd55b8b535b4497ddd9984ae36e1d/src/delay.veryl)
[INFO ] Processing file (/home/hatta/.cache/veryl/dependencies/535a83e749a9560ea19660a7a19f8eed/src/delay.veryl)
[INFO ] Output filelist (/home/hatta/work/repos/veryl/veryl_testcase.f)
[INFO ] Compiling test (test1)
[INFO ] Executing test (test1)
[INFO ] Succeeded test (test1)
$ veryl test --verbose testcases/veryl/48_test.veryl
[DEBUG] Loaded metadata (/home/hatta/work/repos/veryl/Veryl.toml)
[DEBUG] Loaded metadata (/home/hatta/.cache/veryl/dependencies/535a83e749a9560ea19660a7a19f8eed/Veryl.toml)
[DEBUG] Loaded metadata (/home/hatta/.cache/veryl/dependencies/f2cfd55b8b535b4497ddd9984ae36e1d/Veryl.toml)
[DEBUG] Loaded metadata (/home/hatta/.cache/veryl/dependencies/f2cfd55b8b535b4497ddd9984ae36e1d/Veryl.toml)
[DEBUG] Found file (/home/hatta/.cache/veryl/dependencies/f2cfd55b8b535b4497ddd9984ae36e1d/src/delay.veryl)
[DEBUG] Loaded metadata (/home/hatta/.cache/veryl/dependencies/535a83e749a9560ea19660a7a19f8eed/Veryl.toml)
[DEBUG] Found file (/home/hatta/.cache/veryl/dependencies/535a83e749a9560ea19660a7a19f8eed/src/delay.veryl)
[INFO ] Processing file (testcases/veryl/48_test.veryl)
[INFO ] Processing file (/home/hatta/.cache/veryl/dependencies/f2cfd55b8b535b4497ddd9984ae36e1d/src/delay.veryl)
[INFO ] Processing file (/home/hatta/.cache/veryl/dependencies/535a83e749a9560ea19660a7a19f8eed/src/delay.veryl)
[DEBUG] Output file (/home/hatta/work/repos/veryl/testcases/sv/48_test.sv)
[DEBUG] Output file (/home/hatta/work/repos/veryl/dependencies/veryl_sample2/src/delay.sv)
[DEBUG] Output file (/home/hatta/work/repos/veryl/dependencies/veryl_sample1/src/delay.sv)
[INFO ] Output filelist (/home/hatta/work/repos/veryl/veryl_testcase.f)
[INFO ] Compiling test (test1)
[DEBUG] Verilator log: make: Entering directory '/tmp/.tmp4aQPTZ/obj_dir'
[DEBUG] Verilator log: g++ -Os -I. -MMD -I/home/hatta/local/share/verilator/include -I/home/hatta/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -DVL_TIME_CONTEXT -c -o verilated.o /home/hatta/local/share/verilator/include/verilated.cpp
[DEBUG] Verilator log: g++ -Os -I. -MMD -I/home/hatta/local/share/verilator/include -I/home/hatta/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -DVL_TIME_CONTEXT -c -o verilated_threads.o /home/hatta/local/share/verilator/include/verilated_threads.cpp
[DEBUG] Verilator log: /usr/bin/python3 /home/hatta/local/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vtest1.cpp Vtest1___024root__DepSet_h92c8f392__0.cpp Vtest1___024root__DepSet_h5799c38b__0.cpp Vtest1__main.cpp Vtest1___024root__Slow.cpp Vtest1___024root__DepSet_h5799c38b__0__Slow.cpp Vtest1__Syms.cpp > Vtest1__ALL.cpp
[DEBUG] Verilator log: g++ -Os -I. -MMD -I/home/hatta/local/share/verilator/include -I/home/hatta/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -DVL_TIME_CONTEXT -c -o Vtest1__ALL.o Vtest1__ALL.cpp
[DEBUG] Verilator log: echo "" > Vtest1__ALL.verilator_deplist.tmp
[DEBUG] Verilator log: Archive ar -rcs Vtest1__ALL.a Vtest1__ALL.o
[DEBUG] Verilator log: g++ verilated.o verilated_threads.o Vtest1__ALL.a -pthread -lpthread -o Vtest1
[DEBUG] Verilator log: rm Vtest1__ALL.verilator_deplist.tmp
[DEBUG] Verilator log: make: Leaving directory '/tmp/.tmp4aQPTZ/obj_dir'
[DEBUG] Verilator log: - V e r i l a t i o n R e p o r t: Verilator 5.024 2024-04-05 rev v5.024-42-gc561fe8ba
[DEBUG] Verilator log: - Verilator: Built from 0.030 MB sources in 5 modules, into 0.018 MB in 7 C++ files needing 0.000 MB
[DEBUG] Verilator log: - Verilator: Walltime 4.630 s (elab=0.000, cvt=0.003, bld=4.622); cpu 0.000 s on 1 threads; alloced 8.254 MB
[INFO ] Executing test (test1)
[DEBUG] Verilator log: hello
[DEBUG] Verilator log: - /home/hatta/work/repos/veryl/testcases/sv/48_test.sv:9: Verilog $finish
[DEBUG] Verilator log: - S i m u l a t i o n R e p o r t: Verilator 5.024 2024-04-05
[DEBUG] Verilator log: - Verilator: $finish at 1ps; walltime 0.001 s; speed 0.000 s/s
[DEBUG] Verilator log: - Verilator: cpu 0.000 s on 1 threads; alloced 249 MB
[INFO ] Succeeded test (test1)
[DEBUG] Elapsed time (4759 milliseconds)
$ veryl test --sim vcs testcases/veryl/48_test.veryl
[INFO ] Removing dependency (https://github.com/veryl-lang/sample @ 0.10.0)
[INFO ] Removing dependency (https://github.com/veryl-lang/sample @ 0.9.0)
[INFO ] Processing file (testcases/veryl/48_test.veryl)
[INFO ] Output filelist (/home/hatta/work/repos/veryl/veryl_testcase.f)
[INFO ] Compiling test (test1)
[INFO ] Executing test (test1)
Error: "/home/hatta/work/repos/veryl/testcases/sv/48_test.sv", 8: test1.unnamed$$_0: at time 0
aaa
[WARN ] Failed test (test1)
$ veryl test --sim vcs --verbose testcases/veryl/48_test.veryl
[DEBUG] Loaded metadata (/home/hatta/work/repos/veryl/Veryl.toml)
[INFO ] Processing file (testcases/veryl/48_test.veryl)
[DEBUG] Output file (/home/hatta/work/repos/veryl/testcases/sv/48_test.sv)
[INFO ] Output filelist (/home/hatta/work/repos/veryl/veryl_testcase.f)
[INFO ] Compiling test (test1)
[DEBUG] VCS log: Info: [VCS_SAVE_RESTORE_INFO] ASLR (Address Space Layout Randomization) is detected on the machine. To enable $save functionality, ASLR will be switched off and simv re-executed.
[DEBUG] VCS log: Please use '-no_save' simv switch to avoid this.
[DEBUG] VCS log: Chronologic VCS (TM)
[DEBUG] VCS log: Version U-2023.03_Full64 -- Thu Apr 11 17:02:38 2024
[DEBUG] VCS log:
[DEBUG] VCS log: Copyright (c) 1991 - 2023 Synopsys, Inc.
[DEBUG] VCS log: This software and the associated documentation are proprietary to Synopsys,
[DEBUG] VCS log: Inc. This software may only be used in accordance with the terms and conditions
[DEBUG] VCS log: of a written license agreement with Synopsys, Inc. All other use, reproduction,
[DEBUG] VCS log: or distribution of this software is strictly prohibited. Licensed Products
[DEBUG] VCS log: communicate with Synopsys servers for the purpose of providing software
[DEBUG] VCS log: updates, detecting software piracy and verifying that customers are using
[DEBUG] VCS log: Licensed Products in conformity with the applicable License Key for such
[DEBUG] VCS log: Licensed Products. Synopsys will use information gathered in connection with
[DEBUG] VCS log: this process to deliver software updates and pursue software pirates and
[DEBUG] VCS log: infringers.
[DEBUG] VCS log:
[DEBUG] VCS log: Inclusivity & Diversity - Visit SolvNetPlus to read the "Synopsys Statement on
[DEBUG] VCS log: Inclusivity and Diversity" (Refer to article 000036315 at
[DEBUG] VCS log: https://solvnetplus.synopsys.com)
[DEBUG] VCS log:
[DEBUG] VCS log: Parsing design file '/home/hatta/work/repos/veryl/testcases/sv/48_test.sv'
[DEBUG] VCS log: Top Level Modules:
[DEBUG] VCS log: veryl_testcase_Module48
[DEBUG] VCS log: test1
[DEBUG] VCS log: No TimeScale specified
[DEBUG] VCS log: Starting vcs inline pass...
[DEBUG] VCS log:
[DEBUG] VCS log: 2 modules and 0 UDP read.
[DEBUG] VCS log: recompiling module veryl_testcase_Module48
[DEBUG] VCS log: recompiling module test1
[DEBUG] VCS log: Both modules done.
[DEBUG] VCS log: rm -f _cuarc*.so _csrc*.so pre_vcsobj_*.so share_vcsobj_*.so
[DEBUG] VCS log: if [ -x ../simv ]; then chmod a-x ../simv; fi
[DEBUG] VCS log: g++ -o ../simv -rdynamic -Wl,-rpath='$ORIGIN'/simv.daidir -Wl,-rpath=./simv.daidir -Wl,-rpath=/storage/eda/tools/synopsys/VCS/U-2023.03/linux64/lib -L/storage/eda/tools/synopsys/VCS/U-2023.03/linux64/lib -Wl,-rpath-link=./ /usr/lib64/libnuma.so.1 objs/amcQw_d.o _319973_archive_1.so SIM_l.o rmapats_mop.o rmapats.o rmar.o rmar_nd.o rmar_llvm_0_1.o rmar_llvm_0_0.o -lvirsim -lerrorinf -lsnpsmalloc -lvfs -lvcsnew -lsimprofile -luclinative /storage/eda/tools/synopsys/VCS/U-2023.03/linux64/lib/vcs_tls.o -Wl,-whole-archive -lvcsucli -Wl,-no-whole-archive /storage/eda/tools/synopsys/VCS/U-2023.03/linux64/lib/vcs_save_restore_new.o -ldl -lc -lm -lpthread -ldl
[DEBUG] VCS log: ../simv up to date
[DEBUG] VCS log: CPU time: .252 seconds to compile + .193 seconds to elab + .223 seconds to link
[INFO ] Executing test (test1)
[DEBUG] VCS log: Info: [VCS_SAVE_RESTORE_INFO] ASLR (Address Space Layout Randomization) is detected on the machine. To enable $save functionality, ASLR will be switched off and simv re-executed.
[DEBUG] VCS log: Please use '-no_save' simv switch to avoid this.
[DEBUG] VCS log: Chronologic VCS simulator copyright 1991-2023
[DEBUG] VCS log: Contains Synopsys proprietary information.
[DEBUG] VCS log: Compiler version U-2023.03_Full64; Runtime version U-2023.03_Full64; Apr 11 17:02 2024
[DEBUG] VCS log: hello
[DEBUG] VCS log: "/home/hatta/work/repos/veryl/testcases/sv/48_test.sv", 8: test1.unnamed$$_0: started at 0s failed at 0s
[DEBUG] VCS log: Offending '0'
[DEBUG] VCS log: Error: "/home/hatta/work/repos/veryl/testcases/sv/48_test.sv", 8: test1.unnamed$$_0: at time 0
[DEBUG] VCS log: aaa
[DEBUG] VCS log: $finish called from file "/home/hatta/work/repos/veryl/testcases/sv/48_test.sv", line 9.
[DEBUG] VCS log: $finish at simulation time 0
[DEBUG] VCS log: V C S S i m u l a t i o n R e p o r t
[DEBUG] VCS log: Time: 0
[DEBUG] VCS log: CPU Time: 0.220 seconds; Data structure size: 0.0Mb
[DEBUG] VCS log: Thu Apr 11 17:02:39 2024
[WARN ] Failed test (test1)
[DEBUG] Elapsed time (1353 milliseconds)
I'll close this issue after merging #648, and the remaining issues are moved to #658 and #659.
That is excellent work, look forward to testing this feature with verilator.
I've merged #648. Now Verilator, VCS and Vivado Simulator are supported. I'll release v0.9.0 after document update.
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