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!
andrew ming
February 1st, 2019 02:16:19