Recently I played a bit with SystemVerilog and DPI-C and I thought of sharing the experience with you.
This post shows data types mappings from SystemVerilog to C and how to call C-functions from SV. I also provide a simple SV/C application to facilitate understanding of data types mappings.
Data Mappings
When SystemVerilog interacts with the C environment, a data exchange occurs. Data needs to be interpreted in exactly the same way on both ends, otherwise the communication will fail. Data exchange between SystemVerilog and C is usually done using the DPI-C interface which standardizes the type correspondence and a basic API (see also svdpi.h in the simulator’s installation path).
Most of SystemVerilog data types have a straightforward correspondent in the C language, while others (e.g. 4-value types, arrays) require the DPI-C-defined types and API. Bellow, you can see a full set of data type correspondents in a table format:
SystemVerilog to C data type mapping correspondence
SV type to C equivalent | SV type to DPI-defined equivalent | ||
---|---|---|---|
SystemVerilog | C | SystemVerilog | C |
byte | char | bit | svBit |
shortint | short int | bit[n:0] | svBitVecVal |
int | int | logic | svLogic |
longint | long int | reg | svLogic |
real | double | logic[n:0] | svLogicVecVal* |
string | char* | reg[n:0] | svLogicVecVal* |
string[n] | char* | int[] | svOpenArrayHandle |
chandle | void* | byte[] | svOpenArrayHandle |
shortint[] | svOpenArrayHandle | ||
longint[] | svOpenArrayHandle | ||
real[] | svOpenArrayHandle |
How to pass arguments to methods
There are two ways of passing arguments in both C and SV:
- pass by value: the callee function will use a copy of the argument from the caller
- pass by reference: the callee function will use a pointer/reference of the argument from the caller
If a function is changing the values of its arguments, the change is visible outside of the function only if the arguments are passed by reference. When arguments are passed by value, any change to the arguments done inside the function is NOT visible outside of it.
In SystemVerilog, passing by value or by reference is determined by the argument direction.
In C, passing by value or by reference is determined by whether the argument type is a pointer.
By default both SV and C are passing arguments by value.
Passing arguments by value
//SV - passing by value
function void f1(int a); // implicit direction of a is input
function void f2(input int a); // explicit direction mentioned as input
//C - passing by value
void f1(int a); // argument a is passed by value
Passing arguments by reference
//SV - passing by reference
function void f1(output int a); // direction of a is output, thus it is passing a reference
//C - passing by reference
void f1(int* a); // argument a is passed by reference
The Application
The SV/C application provides usage examples for every data type in the table above. The source code of the application can be downloaded from here.
It also offers a self checking testbench that was successfully run on all 3 major simulators: QuestaSim, VCS, Xcelium.
Example
Let’s assume that we need to send out one bit from SystemVerilog towards a C implementation and return a result back to SystemVerilog. The SystemVerilog code could use two ways for receiving data from the C code:
- via return value – get_bit() example
- via argument – compute_bit() example
Since the library was developed with self-checking in mind, you will notice two assertions for checking the validity of data received from the C counterpart.
The following code snippets will show how to import a function definition and how to use it in SystemVerilog.
// First we must import the functions' declarations whose implementations are done in C
import "DPI-C" function void compute_bit(input bit i_value, output bit result);
import "DPI-C" function bit get_bit(input bit i_value);
//...
rand bit m_bit;
//...
function void test_bit();
bit cres, ares;
bit expected = transform_bit(m_bit);
$display($sformatf("test.test_bit calls compute_bit with %b", m_bit));
compute_bit(m_bit, cres);
ares = get_bit(m_bit);
COMPUTE_BIT_ERR: assert(cres == expected) else begin
$display($sformatf("compute_bit error: expected %b received %b for input %b", expected, cres, m_bit));
$finish();
end
GET_BIT_ERR: assert(ares == expected) else begin
$display($sformatf("get_bit error: expected %b received %b for input %b", expected, ares, m_bit));
$finish();
end
endfunction
function bit transform_bit(bit in);
return !in;
endfunction
This is the piece of code from C, showing the function definition:
//include the SystemVerilog DPI header file
#include "svdpi.h"
// Define the corresponding C functions to be imported in SystemVerilog
//compute function returns the result as argument to the function
void compute_bit(const svBit i_value, svBit* result) {
log_info("dpi_c.compute_bit(): input %u", i_value);
*result = transform_svBit(i_value);
log_info("dpi_c.compute_bit(): result %u", *result);
}
//get function returns the result as return value of the function
svBit get_bit(const svBit i_value) {
svBit result;
log_info("dpi_c.get_bit(): input %u", i_value);
result = transform_svBit(i_value);
log_info("dpi_c.get_bit(): result %u", result);
return result;
}
svBit transform_svBit(const svBit in) {
return !in;
}
Data Types Mappings and The Corresponding API definitions
Next, we’ll list again the data type mappings, but this time together with examples of functions’ signatures from both SystemVerilog side and C side. In this manner you should be able to understand how data types can be used as function arguments or as return values for the functions.
SV byte maps to C char |
|
|
SV shortint maps to C short int |
|
|
SV int maps to C int |
|
|
SV longint maps to C long int |
|
|
SV real maps to C double |
|
|
SV string maps to C char* |
|
|
SV chandle maps to C void* |
|
|
SV bit maps to C bit |
|
|
SV bit[n:0] maps to C svBitVecVal |
|
|
SV logic maps to C svLogic |
|
|
SV reg maps to C svLogic |
|
|
SV logic[n:0] maps to C svLogicVecVal |
|
|
SV reg[n:0] maps to C svLogicVecVal |
|
|
SV int[] maps to C svOpenArrayHandle |
|
|
SV struct maps to C struct |
|
|
All simulators support the examples above…similar to UVM the exceptions are guarded by macros( i.e. `ifdef).
References
If you are looking for more details about how DPI-C works, I recommend reading the following:
- SystemVerilog IEEE Std. 1800™-2013, Section 35 Direct Programming Interface, Annex H DPI-C Layer, Annex I svdpi.h
- VeriPage SystemVerilog DPI Tutorial
- Doulos SystemVerilog DPI Tutorial
Enjoy!
23 Responses
” Passing arguments by reference
//SV – passing by reference
function void f1(output int a); // direction of a is output, thus it is passing a reference
I am not sure where i can find the output indicates “passing a reference”
never mind my previous question. i think you mean “passing reference” in C code if the argument in SV side is output.
Hi, Andrew.
Thank you for paying attention to details.
Technically speaking you are correct. A SystemVerilog output argument is not passed by reference but, as per LRM IEEE-1800-2012, is passed by copy-out.
Still, in my opinion, the effect is similar.
As per LRM IEEE-1800-2012 :
By the way, here is an interesting question on stackOverflow: What does “ ref ” mean in systemverilog?
The answer explains nicely, when to use ref and when to use output.
Great overview of the various mappings, especially how to handle output argument passing. I’ve struggled to find examples of getting values back from C into SystemVerilog via task arguments, and this page confirmed that I was on the right track. One suggestion to add to the material is how to deal with memory allocation. For example if one creates a string in C (char array) it needs to be allocated memory and then subsequently its needs to be freed. Currently I’ve settled on having SystemVerilog invoke a “freeStrMemPtr” C function to free up the given char* array in C memory space, once its assigned/copied the string in SystemVerilog memory space. I haven’t seen any examples of this online (or in the spec), so maybe I’m doing the wrong thing, so it would be a useful addition to this page.
Hi, Carl.
Thanks for the kind words and for suggestions.
Memory management when using both SystemVerilog and C, is an interesting topic. It could make the subject of a different blog post.
I also discussed this with a colleague of mine. Dragos, is developing a library for functional coverage in SystemC. He noticed some kind of a similar approach on Cadence’s website: Sharing memory between SV and DPI-C methods.
Hello,
I am actively investigating the use of a foreign language called Nim[1] to interface with SV instead of using raw C or C++. In the process, I have done some research on using DPI-C with SystemVerilog and am always on the lookout for C examples in the wild used for interfacing with SV.
As I find such C examples (and time), I translate them to Nim and put them on my nim-systemverilog-dpic[2] GitHub repo for others to review and critique.
As my latest exercise, I converted all the C/H files in this blog post to a Nim file over here[3] (that compiles to both C and C++).
Further discussion at https://github.com/amiq-consulting/amiq_blog/issues/2 :). I am looking forward to feedback.
—
[1]: https://nim-lang.org
[2]: https://github.com/kaushalmodi/nim-systemverilog-dpic
[3]: https://github.com/kaushalmodi/nim-systemverilog-dpic/blob/master/amiq_dpi_c_examples/libdpi.nim
Hi Aurelian,
Could you please give any reference to fully working example of import “DPI-C” function void compute_unsized_int_array(input int i_value[], output int result[]);
I am trying to implement something similar and able to pass the dyanmic array from SV to C successfully but when passing dynamic array (result) from C to SV, SV side is not getting the correct value in dynamic array.
BR,
Udit
Hi, Udit.
Here is the C function definition:
void compute_unsized_int_array(const svOpenArrayHandle i_value, svOpenArrayHandle result)
Here is the DPI-C function signature:
import “DPI-C” function void compute_unsized_int_array(input int i_value[], output int result[]);
And here is the SystemVerilog code using the DPI-C:
test_unsized_int_array();
NOTE: Depending on your vendor’s simulator some data types might not be transferred as expected.
Best regards,
Aurelian
Hi Aurelian,
I’m implementing a SV interface, i’ve to return a C struct to SV. What will be the syntax for it?
Hello, Ravi.
For that case, you need a function to convert the struct to svLogicVecVal *. After your conversion, you can send the svLogicVecVal * to a packed struct on the SystemVerilog side using DPI-C.
Hello, how can we run these two files file.sv and file.c means with which command, can you please suggest command if I use VCS simulator? Because I tried but it is giving me an error !
Hello, Shraddha.
Please, see how this is done in our example:
https://github.com/amiq-consulting/amiq_blog/tree/master/amiq_sv_dpi_c_how_to_map_systemverilog_data_types_to_c_using_dpi_c
How do you pass a string from C to a SystemVerilog task?
Thanks for an example.
Hello, Dustin!
A string from C can be passed to a SystemVerilog task using DPI-C.
You can find an example below:
SystemVerilog code:
C code:
When calling the display_string() task, the output is:
My compilation with DPI-C calls pass but I got this run-time error below, any clue why?
— log file —
My source code is below:
—– C code —–
— SV code —
The error indicates that the c file is not being compiled, thus the SV code does not know the implementation of the Basic_Test DPI-C function.
It is interesting to note your comment: “testDir passed from command line”. I assume you mean from the simulator command line.
Have a look at how we add compilation options to the simulator:
https://github.com/amiq-consulting/amiq_blog/tree/master/amiq_sv_dpi_c_how_to_map_systemverilog_data_types_to_c_using_dpi_c/sim
How to pass an unpacked struct from SV to C using an import call in which struct of SV is inout and bring data from C after filling the struct?
Hi Aurelian,
Nice informative article!
Have you tried DPI tasks to access and wait on some signal to achieve certain value using VPI functions?
I have a requirement to wait on a hierarchical DUT signal to attain certain value, but I have the path available only as string (from %m of SV).
1. Do you know if I could add a DPI task which takes the string input, access handle using VPI and wait for the value inside the DPI itself?
2. Or should I use a combination of functions: one to get the VPI handle out of DPI, possibly to a chandle type and use another DPI function to which chandle is passed and get the value back to SV. And add wait statement in SV itself. Can this slow down the simulation, compared to #1 method, if I have to get this done on, say a million different instances in a design?
Please let me know your thoughts, or if you have any such example handy, please share. Thanks
how to send 512 bits of data from c++ to systemverilog functions or tasks using dpi?
Hi
I am new to DPI
I have a .so file , how can I get golden values from c model whereas I do not have c code
I believe you have an error in the section labeled “SV logic[n:0] maps to C svLogicVecVal”. The SV code imports a function named “compute_logic_vector” but the C code declares a function by a different name with a different function prototype (namely, the “result” parameter is missing and it returns a svLogicVecVal pointer instead of void).
extern “C” int32_t* outputgen(int32_t* inputData, int sizearray, int dataWidthIn, int dataWidthOut, int parallelOut, int depth, int32_t max, int32_t min, int32_t stepSize, int32_t lutentry)
Above is C DPI function,where inputData is array and return type is array.THen how to import this function into Systemverilog class to store function output which is in array type?