When we must verify a highly computational RTL, we may deal with complicated mathematical functions and algorithms. Implementing and debugging an RTL model can be tricky and time consuming. In such cases modeling using Octave/Matlab, C/C++ or SystemC can be a good alternative.
In this article we present a “Hello world!” example to illustrate how to use SystemVerilog as the verification language and Octave as the numerical modeling language. This is a simplified version of the Algorithm Verification with SystemVerilog and OpenSource presentation held at DVCon Europe.
First of all, we must install Octave’s main package and it’s development package.
Then we create a testbench and connect it to Octave as shown in the image below:
We need the C++ layer because Octave libraries are C++ libraries.
The SystemVerilog Layer
The SystemVerilog layer contains the implementation of an UVM component and imports a DPI-C function. By using the import DPI-C construct we can call from SystemVerilog a function implemented in C.
import "DPI-C" function void c_hello_world();
class amiq_hello_world extends uvm_component;
...
function void sv_hello_world();
`uvm_info("HELLO_WORLD", "Hello world from SV!", UVM_NONE);
// Call the C layer hello world
c_hello_world();
endfunction
...
endclass
The C Layer
The C layer forwards the call to C++. When mixing C and C++ code the extern “C” construct must be used. In this layer C – C++ type casting may be required.
extern "C" {
void c_hello_world() {
printf("[HELLO_WORLD] Hello world from C!\n");
// Call C++ layer hello world
cpp_hello_world();
}
}
The C++ Layer
The C++ layer forwards the call to Octave. In this layer C++ – Octave type casting may be required.
// In order to access Octave libraries and to use Octave specific data types,
// 3 header files must be included into the C++ layer:
// Main C++ Octave library
#include <octave/octave.h>
// For octave_main()
#include <octave/oct.h>
// For access to virtual terminal support
#include <octave/parse.h>
// Hello world example - C++ function
void cpp_hello_world() {
cout << "[HELLO_WORLD] Hello world from C++!" << endl;
// Input parameters list for octave custom function
octave_value_list oct_in_list;
// Load Octave hello world function
load_fcn_from_file("octave_hello_world.m", "", "", "octave_hello_world", true);
// Call Octave layer hello world
feval("octave_hello_world", oct_in_list, 1);
}
The Octave Layer
The Octave layer contains the mathematical function – in this case only a message will be printed.
% Hello world example
function octave_hello_world ();
disp("[HELLO_WORLD] Hello world from Octave!");
end
Octave Initialization
The Octave interpreter must be initialized at the beginning of the simulation by calling octave_main():
// SystemVerilog
import "DPI-C" function void initialize_octave();
// Hello world environment
class amiq_hello_world extends uvm_component;
...
virtual task run_phase(uvm_phase phase);
...
// Initialize the Octave interpreter
initialize_octave();
...
endtask
...
endclass
// C++
void initialize_octave_cpp() {
// Declare a string vector used to pass arguments to octave_main function
string_vector argv(2);
// Set the first argument to "embedded"
argv(0) = "embedded";
// Set verbosity to quiet
argv(1) = "-q";
// Call octave_main() to initialize the interpreter
octave_main(2, argv.c_str_vec(), 1);
}
Compiling
We must create a shared object that will be passed to the simulator. The mkoctfile utility helps us:
$: mkoctfile -link-stand-alone -v my_cpp_code.cpp
The mkoctfile output is the g++ command that creates the shared object:
g++ -c -fPIC -I/usr/include/octave-3.4.3/octave/.. -I/usr/include/octave-3.4.3/octave -I/usr/include/freetype2 -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic my_ccp_code.cpp -o my_ccp_code.o
g++ -shared -Wl,-Bsymbolic -o my_ccp_code.oct my_ccp_code.o -link-stand-alone -L/usr/lib64/octave/3.4.3 -L/usr/lib64 -loctinterp -loctave -lcruft -L/usr/lib64/atlas -llapack -L/usr/lib64/atlas -lf77blas -latlas -lfftw3 -lfftw3f -lm -L/usr/lib/gcc/x86_64-redhat-linux/4.4.6 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../.. -lgfortranbegin -lgfortran -lm
Running
Simulations can be run using any of the 3 major EDA vendors simulators (irun, vlog/vsim and vcs). For vsim the shared library must be included at run time and for irun and vcs at compile time. You can find some invocation examples here amiq_hello_world_demo.sh.
Note: If you experience issues related to the octave_main() function, remove the macro call “OCTINTERP_API” from “octave.h” header file (usually located in “/usr/include/”). This macro is relevant only for Microsoft’s Visual C compiler. If you create a new file with the macro call removed, you must include it before including the Octave libraries.
Results
The results should look like this:
UVM_INFO @ 0 [HELLO_WORLD] Hello world from SV!
[HELLO_WORLD] Hello world from C!
[HELLO_WORLD] Hello world from C++!
[HELLO_WORLD] Hello world from Octave!
Source Code
The source files for the “Hello world!” example above, as well as more elaborate ones can be found on AMIQ’s github amiq_sv_octave project repository. There are three packages that you can download:
- amiq_hello_world for code and scripts in this tutorial
- amiq_dsp_algorithms for DSP algorithms in both SystemVerilog and Octave, including tests
- amiq_dsp_algorithms_fp for a fixed point implementation of the amiq_dsp_algorithms package using SystemVerilog and SystemC
The slides from this presentation can be viewed here.
12 Responses
Hello.
I downloaded the amiq_hello_world tutorial
Nevertheless, I am getting the following error messages at simulation time:
ncsim: *W,LIBRUN: Could not load the dynamic library: ./INCA_libs/irun.lnx8664.15.10.nc/librunpost
System ERROR: liboctinterp.so.2: cannot open shared object file: No such file or directory or file is not valid ELFCLASS64 library..
ncsim: *F,NOFDPI: Function initialize_octave not found in any of the shared object specified with -SV_LIB switch
As you can see the library librunpost.so has been created:
masia@ctolx646: ls -latr ./INCA_libs/irun.lnx8664.15.10.nc/librunpost.so
-rwxr-x— 1 masia 99xgrp 8196 Jun 22 11:05 ./INCA_libs/irun.lnx8664.15.10.nc/librunpost.so
Can you provide some feedback about my issue ?
Thanks in advance,.
Best Regards.
Andrea
Hi Andrea,
librunpost.so is the name of an internal Cadence shared object not the one you created.
If you are using the scripts from here your shared object should be called libcpp_oct.so. Have you created this shared object?
Best regards,
Daniel.
Hello Daniel.
First of all, thanks for your prompt answer.
I created (using the g++ command provided into the exmaple “amiq_hello_world”) the libcpp_occ.so shared library:
masia@ctolx646: ls -latr libcpp_oct.so
-rwxr-x— 1 masia sp_plus 49380 Jun 23 11:25 libcpp_oct.so
Also the Cadence shared object has been created (invoking “irun” called into your example):
masia@ctolx646: ls -latr INCA_libs/irun.nc/librunpost.so
-rwxr-x— 1 masia sp_plus 8234 Jun 23 11:18 INCA_libs/irun.nc/librunpost.so
Best Regards.
Andrea
Hi Daniel.
Respect to your example, I added the following lines to irun command:
1. -sv_lib /sw/freetools/octave/3.6.1/Linux/rh60/x86_64/lib/octave/3.6.1/liboctinterp.so \
2. -sv_lib ${SCRIPT_DIR_AMIQ_HELLO_WORLD}/INCA_libs/irun.nc/librunpost.so \
So doing, the simulation worked fine.
Best Regards.
Andrea
Hi Andrea,
I’m glad to hear that it works. :)
Best regards,
Daniel.
Thanks Daniel.
Please keep in mind that the example “as it is” did not worked fine at my end .
Best Regards.
Andrea
I was expecting that the compiler arguments would change as new tool versions are released. Unfortunately I don’t have the time to test the scripts for every new version. But the example should be a good starting point for other users.
Best regards,
Daniel.
Hi Daniel.
I completely agree with you.
Best Regards.
Andrea
Hi,
I have been following this post and your DVCon paper.
It has helped me significantly for complex function verification and also in AMS to some extent.
Lately I tried to check performance with SV simulator when I coded required algorithm in octave/matlab only and communicated to SV through file I/O. I found it to be fruitful.
My data may be limited to certain scope.
My question is whether you have checked on performance with purely file I/O. And then calling octave/matlab through SV system function – “$system()”
Regards,
Kunal
Hi Kunal,
We have not tried going through the system API. It could actually be faster than DPI-C calls.
Our goal was to see if we could get better performance and faster implementation using Octave and not System Verilog, for mathematical models. We used DPI-C because we tried to make it system independent and we previously had a fair amount of experience with it. DPI-C also allows you to easily pass complex data between SV and Octave.
Thank you for your input. I will try it in the future if I get to work with similar problems again.
Best regards,
Daniel.
Hi Daniel,
I have looked into the g++ compilation code given above. However, when I run the following, it is throwing an error stating “cannot find -link-stand-alone”
Is there anything that I have missed in here?
Another query is that, when I run the irun simulator, it is launching in gui mode, and once the run is done, when I exit the xcelium, it is throwing an error stating “*** Error in `xmsim’: corrupted double-linked list: 0x000000000b3ff480 ***”. What could be the reason behind that?
Hi, Akhil!
I do expect issues with the exact compilation arguments described in this article. The post is 9 years old and it worked for the simulator, OS, and Octave versions available to me and Andra at that time. A lot has changed in the meantime so the compile arguments should be updated according to your setup.
Please check Andrea Masi’s comment from June 23rd, 2016. While his solution is also old, it might help with updating the compile options.
Best regards,
Daniel.