Systemverilog DPI example with AES-Openssl C-model
When creating scoreboard, there is a high possibility that we’ll need to integrate the golden model written in other programming languages into our testbench. Systemverilog support this with the DPI (Direct Programming Interface).
This post is an example of using DPI-C to make C model generate the expected data of AES encryption. This AES model is built with Openssl.
AES C-model built with Openssl library
AES encryption with Openssl library
We’re not gonna explain how to use Openssl library in this post, since it is pretty common and well explained in the Openssl man page already.
Let use this c file which already implement the aes encryption using Openssl library. To perform aes encryption, we need to call aes_encrypt
with suitable input of array of data.
You can access to the full version here: aes.c
int aes_encrypt(enum AES_OP_MODE aes_mode,
unsigned char *plaintext,
int plaintext_len,
unsigned char *key,
int key_len,
unsigned char *ivec,
unsigned char *ciphertext)
{
...
return ciphertext_len;
}
- The inputs are the
plaintext
to encrypt, thekey
andivec
. All of these inputs are passed to the function by using the pointer to the array of those data in memory. Also, we need to supply the encryption mode, the size of the plaintext (in unit of byte) and key (in unit of bit). - The output is
ciphertext
, it’s an pointer to and array of data, and we passed that pointer to this function to store the output data. Also, the function will return the size of ciphertext in unit of byte.
C wrapper to interract with Systemverilog
Now, C and Systemverilog are two different programming languages, with different set of data types.
So, to be able to use this aes_encrypt
, we need to make sure C and SV code understand each other by making a C wrapper file below.
- We notice that the type of the plaintext, key, ivec, ciphertext array of bytes are not
unsigned char *
anymore. Instead, their type is nowsvOpenArrayHandle
. This type is pre-defined C type and it’s equivalent to the array of int, byte, shortint,… type in Systemverilog. Read this post for other type mapping correspondence. - Then we just need to type cast to normal C data type and call the
aes_encrypt()
function. - The pre-defined C type
svOpenArrayHandle
and the functionsvGetArrayPtr
is defined in the"svdpi.h"
. This file is provided by your EDA simulation tool.
Building .so files
LDFLAGS = -Wl,-rpath,$(OPENSSL_PATH)/lib -lssl -lcrypto
C_INCLUDE = -I$(QUESTA_SIM_INS_DIR)/questasim/include
all: aes_sv_c_dpi.so aes
aes_sv_c_dpi.so: aes_sv_c_dpi.c
gcc -m64 -fPIC -g -W -shared -fdiagnostics-color=never -std=c99 $(C_INCLUDE) $(LDFLAGS) -o aes_sv_c_dpi.so aes_sv_c_dpi.c
aes: aes.c
gcc -m64 -std=c99 $(C_INCLUDE) $(LDFLAGS) aes.c -o aes
- Finally we create a Makefile to generate the
aes_sv_c_dpi.so
file as above. This file will be load when running simulation so that we can call it in the Systemverilog testbench. - We must add
-I$(QUESTA_SIM_INS_DIR)/questasim/include
so that the gcc compiler can find the"svdpi.h"
file discussed above. - Also, we should compile a
aes
executable file so that we can run and debug the encryption if necessary.
Calling C-model function in Systemverilog
Loading C libary .so file
-sv_lib ${C_MODEL_DIR}/aes_sv_c_dpi
// The Questasim will load the ${C_MODEL_DIR}/aes_sv_c_dpi.so when run simulation
- This is depend on the EDA tool that you’re using for simulation. You need read your EDA tool User Guide to know suitable option to load the .so file as well as the location of the
"svdpi.h"
file.
Importing the C function to SV testbench
When the C-model is loaded and ready to use, we just need to import it into our testbench as below.
import "DPI-C" function int cAesEncrypt(
input aes_enc_mode_e m_aes_mode,
input byte m_plaintext[],
input int m_plaintext_len,
input byte m_key[],
input int m_key_len,
input byte m_ivec[],
output byte m_ciphertext[]);
- The
cAesEncrypt()
is the C-wrapper function that we created in this. The argument list in theimport
statement must have the same order as the C code function. - The
input
in each argument means that that argument will be pass from SV to C code. For example, we will pass the value ofm_plaintext_len
to thesvplaintext_len
input argument of thecAesEncrypt()
in C code. - For the
output
argument, in the other hand, the data will be passed from C code to SV code. We actually pass the reference/pointer to the array in memory that will store them_ciphertext
, the C code will write data to the memory pointed by that pointer.
Calling C function in Systemverilog
byte m_aes_plaintext[$];
byte m_aes_key[$];
byte m_aes_ivec[$]
byte m_aes_ciphertext[4096];
int m_ciphertext_len;
// generate the input data
m_aes_plaintext = '{'hab, 'h02, 'h33, 'hff, 'haa, 'h22, 'h45};
...
// Calling C function for encryption
m_ciphertext_len = cAesEncrypt(
get_aes_enc_mode(), //enc mode
m_aes_plaintext, //enc data array
m_aes_plaintext.size(), //enc data size
m_aes_key, //key array
m_aes_key.size(), //key size
m_aes_ivec, //ivec array
m_aes_ciphertext); //outputed cipher byte array
- Finally, we are able to call the C-function from Systemverilog testbench.
- Only 1 point to remember is that for all the
output
array, in this example ism_aes_ciphertext
array, we must define a size of an array (here is 4096), otherwise it will create runtime error.
Finding more information
To have more understanding as well as more examples, check below references:
- The IEEE Standard for SystemVerilog, Chapter.35. Direct programming interface, Annex H DPI C layer.
- EDA User guide (Questasim).
- How to Call C-functions from SystemVerilog Using DPI-C
- Openssl man page with examples
[Tags
systemverilog
]