How to Connect SystemVerilog with Octave

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:

SV_Octave_interconnection

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:

The slides from this presentation can be viewed here.


Comments

Andrea Masi June 23rd, 2016 09:18:12

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


    Daniel Ciupitu June 23rd, 2016 12:34:15

    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.


    Andrea Masi June 23rd, 2016 12:43:31

    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


Andrea Masi June 23rd, 2016 15:03:00

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


    Daniel Ciupitu June 23rd, 2016 15:13:22

    Hi Andrea,

    I’m glad to hear that it works. :)

    Best regards,
    Daniel.


    Andrea Masi June 23rd, 2016 15:26:02

    Thanks Daniel.

    Please keep in mind that the example “as it is” did not worked fine at my end .

    Best Regards.
    Andrea


    Daniel Ciupitu June 28th, 2016 07:43:10

    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.


    Andrea Masi June 28th, 2016 09:49:51

    Hi Daniel.

    I completely agree with you.
    Best Regards.

    Andrea


Kunal Panchal April 5th, 2017 12:11:05

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


    Daniel Ciupitu April 10th, 2017 14:28:55

    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.


Leave a Comment:

Your comment will be visible after approval.

(will not be published)

This site uses Akismet to reduce spam. Learn how your comment data is processed.