No project description provided
Project description
Verime
In the context of the side-channel analysis of hardware implementations, an evaluator usually requires to have access to the internal values of a target circuitry. For this purpose, a circuit oracle simulating these values is implemented, which may turn out to be time consuming to do manually (e.g., each probed internal value should be modeled and any modification of the original circuitry implies to rewrite the circuit oracle) and/or achieve poor performances (e.g., spawning a simulator process for each simulation).
The Verime tool is proposed to tackle these issues. In particular, it automatically generates a prediction library of a circuit (described in Verilog). More precisely, it performs behavioral verilog simulations during an arbitrary amount of clock cycles thanks to a Verilator backend. Verilator is a powerful tool, but requires some expertise and predicting certain internal states using the \* verilator public *\
pragma can be challenging and time consuming to setup for a non-experienced user. Based on that, the Verime tools acts as a wrapper for verilator and aims to (significantly) reduce the evaluator work: it automatically generates the C++ Verilator backend code and generates a user friendly python package thant can be easily integrated and used.
The advantages over "raw" Verilator usage are:
- Easy integration with python: no bindings to write.
- Nultiple simulations in a single process, that can even run in parallel, achieving excellent performance (no process startup overhead).
- No need to handle complicated Verilator model interface to probe the value of wires.
In short, the workflow is as follows:
- Annotate the targeted signal in the Verilog source(s) file(s) with the attribute
(* verime = "probed_sig_name" *)
. - Write a C++ simple simulation wrapper for the top-level module that indicates:
- How the top-level module should be interfaced and how the input data is routed.
- When the probed signals values should be stored (e.g., all clock cycles or some specific ones).
- Run the Verime tool to build a python package (wheel) and install it as any other python package with pip.
- Use the simulation package in python scirpts.
Dependencies
In addition to Python (3.10 tested), Verime has the following native dependencies:
CAUTION: Verilator dependency:
- If a recent-enough version is available, we recommand to install Verilator using the packet manager of your OS.
- If you build from source, we
highly recommend using an in-place version of Verilator, as recommended
in the official
documentation.
Then, set the
VERILATOR_ROOT
environmnent variable appropriately (noPATH
modification needed). Some versions of Verilator are known to not behave correctly when installed withmake install
.
Installation
python3 -m pip install verime
You can also build it locally (it is a pure-python package).
Usage example
This section demonstrates how Verime can be used for a simple example provided under the tests directory. In particular, the later contains the Verilog file implementing a programmable delay counter (i.e., a module that counts up to an arbitrary value and indicates when it finishes). In particular, the following files can be found:
These modules have been coded to use different coding styles (e.g., generate loop, multiple depth levels, generics and localparam, ... ). For the provided top level, cnt_bound
is used to specify a delay (in terms of clock cycles), start
is a control signal used to start a new count and busy
is asserted when a count is in progress. An execution begins when start
is asserted (and that busy
is not). Then, the core will assert busy
during cnt_bound
+1 cycles.
1. Annotation of the HDL
In this simple example, we use Verime to probe some internals signals accross the hierarchy of the counter. To do so, we first annotate the internal signals that we want to probe with the verime
attribute. In particular, we annotate the signals reg [N-1:0] counter_state
(in counter.v), input a
(in FA1bit.v) and input b
(in FA1bit.v) as depicted in the following code snippets
...
// Register to hold the value
(* verime = "counter_state" *)
reg [N-1:0] counter_state;
wire [N-1:0] counter_nextstate;
...
and
...
(* verime = "FA1_ina" *)
input a;
(* verime = "FA1_inb" *)
input b;
input cin;
...
2. Implementation of the C++ simulation wrapper
Once the HW design annotated, the file test_counter.cpp implements the C++ simulation wrapper of the top-level module. This code follows the structure:
// MUST be included, as is.
#include "verime_lib.h"
// The simulation function. Only function that MUST be implemented
void run_simu(SimModel *sm, Prober *p, char* data, size_t data_size) {
// See below for the content of this function.
}
For our simple example, the run_simu
function only perfoms four basic steps:
- First, the core is reset
...
// Reset the top module core
sm->vtop->rst = 1;
sim_clock_cycle(sm);
sm->vtop->rst = 0;
sim_clock_cycle(sm);
...
- Then, the programmable delay is fetched from the input buffer and the dedicated input bus of the core is set accordingly
...
// Prepare the run with input data
// Set the cnt_bound value
memcpy(&sm->vtop->cnt_bound,data,BYTES_BOUND);
...
Where BYTES_BOUND
is a macro derived from GENERIC_N
, which is defined by
Verime as the value of the N
generic parameter of the top-level module.
3. Afterwards, a core execution is started
...
// Start the run
sm->vtop->start = 1;
sim_clock_cycle(sm);
sm->vtop->start = 0;
sm->vtop->eval();
...
- Finally, we wait for the end of the execution. While waiting for the counter to reach the configuration, the value of the probed states are saved at every clock cycles. Their values are also saved the cycle after the completion of the counting process.
...
// Run until the end of the computation
while(sm->vtop->busy==1){
// Save all the probed values for the current clock cycle.
save_state(p);
// Simulate a single clock cycle
sim_clock_cycle(sm);
}
// Save the probed value once the operation is over
save_state(p);
...
3. Building the python3 library package.
It is now time to compile everything together in order to have our 'easy-to-use' simulation python package.
This is done in two steps: first generate the source files for the package using Verime, then running make
with the generated Makefile
.
The example Makefile follows this procedure to follow, you can simply run make
in tests/example
.
If the build is successful (otherwise check if you have installed all the required dependencies in correct versions), a wheel package should be created under the directory named after the PACK_NAME
variable of the Makefile.
4. Use the simulation python package.
Now that the package has been built for our simple design, the file example_simu.py demonstrates how the it can be used. In the example, we use it to validate the (functional) behavior of our HW module (i.e., by verifying the value of the internal counter after an execution). The following commands (under tests/example
) can be used to verify that everything went well (here, we rely on a virtual environment which is recommanded but not stricly required):
python3 -m venv ve
source ve/bin/activate # change according to your OS and shell
pip install --upgrade pip
pip install counter_example_16_lib/*.whl # default value
python3 example_simu.py
At the end, a message similar to the following should be displayed
...
Check the prediction results for the 100 cases:
All simulated cases where successfully verified!
[0:00:00.583404 elapsed (100 pred. cases ; 3387683 clock cycles simulated)]
Verime API and detailed usage
This section explains with more details the functions and parameters that can be used at the different steps of the flow when using the Verime tool.
C++ Wrapper
The simulation wrapper is the only file that need to be written by as user. In in this file, the user only needs to implement to function run_simu
:
void run_simu(SimModel *sm, Prober *p, char *data, size_t data_size);
The following parameters are used:
SimModel *sm
: A Verime-specific structure holding the circuit model generated by the Verilator backend. It is typically used to simulate the stimuli at the top-level of the simulated hardware architecture.Prober *p
: A Verime-specific structure used to easily to save the value of the probed (i.e., annotaded) signals at a given simulation time.char *data
: An array of bytes, provided to the python API as the input data of a single simulation. These are the data required to perform a simulation.size_t data_size
: The length of thedata
array.
The definitions of the structures SimModel
and Prober
depend on the hardware architecture as well as the annotated signals and are automatically generated by Verime. The directive #include "verime_lib.h"
must be used in order to properly include the generated library code in the compilation flow. Besides, the Verilog top-level generic values can be recovered in the wrapper: Verime will define the macro GENERIC_${PARAM}
for each generic defined at the Verilog top-level (where PARAM
is the generic name).
To implement the top-level stimuli, the input/output of the core can be accessed through the SimModel
. In particular sm->vtop
references to an instance of the top module simulation object generated with Verilator. As an example, the statement sm->vtop->sig_name = value;
can be used to set the value of the hardware top level I/O bus sig_name
. The exact types of each data bus depends on its practical size, as summarized by the following table
size $s $ (bits) |
C++ type |
---|---|
$s\leq 8 $ |
uint8_t |
$8< s \leq 16 $ |
uint16_t |
$16< s \leq 32 $ |
uint32_t |
$32< s \leq 64 $ |
uint64_t |
$64<s $ |
uint32_t [$\lceil s/32 \rceil $] |
In addition, the following functions are provided when including verime_lib.h
:
sim_clock_cycle(SimModel * sm)
: simulates a posedge clock cycle. This function only works for a design that is synchronous to the positive edge of a top-level input clock.save_state(Prober * p)
: save the value of the probed signal at the current simulation time.sm->vtop->eval()
: evaluates the circuit internal signals values at the current simulation time. This is a call to theeval()
function of the generated Verilator object. This function should be used only if you cannot usesim_clock_cycle
.
Verime usage
The verime tool is used to build the front-end python package based on the HDL Verilog source and the C++ simulation wrapper. As shown by the tool helper, several parameter can be used.
verime -h
usage: verime [-h] [-y YDIR [YDIR ...]] [-g GENERICS [GENERICS ...]] -t TOP [--yosys-exec YOSYS_EXEC] --pack PACK --simu SIMU [--build-dir BUILD_DIR] [--clock CLOCK]
options:
-h, --help show this help message and exit
-y YDIR [YDIR ...], --ydir YDIR [YDIR ...]
Directory for the module search. (default: [])
-g GENERICS [GENERICS ...], --generics GENERICS [GENERICS ...]
Verilog generic value, as -g<Id>=<Value>. (default: None)
-t TOP, --top TOP Path to the top module file, e.g. /home/user/top.v. (default: None)
--yosys-exec YOSYS_EXEC
Yosys executable. (default: yosys)
--pack PACK The Verilator-me package name. (default: None)
--simu SIMU Path to the C++ file defining run_simu (default: None)
--build-dir BUILD_DIR
The build directory. (default: .)
--clock CLOCK The clock signal to use. (default: clk)
The only required parameters are -t
, --pack
and --simu
.
Python Library
Once installed, the simulation library package can be used to simulate the probed internal state value by creating a Simu
object, as shown next
import generated_verime_lib as prediction_lib
...
predictions = prediction_lib.Simu(
cases_inputs,
am_probed_states
)
where cases_inputs
is a 2D numpy array of bytes (i.e., np.uint8
) that holds the input bytes for each simulation (each row will be used as the data
argument of run_simu
in the C++ code) and am_probed_states
specifies the maximum amount of time that the probed states will be saved (i.e., the number of calls to save_state
in run_simu
).
The Simu
object can be index with keys which are the Verime probed signals names, returning the corresponding simulation results. These are 3D numpy arrays, where the first dimension is the simulation index, the second dimension is the saving index (i.e., result of the i-th call to save_state()
in run_simu()
) and the third dimension is the byte index of the probed signal value. Putting all together, the following code snippet represents a generic template for the usage of the generated prediction library.
# Import the generated lib
import generated_verime_lib as plib
# Import numpy, used to generate the input byte for each simulated cases
import numpy as numpy
# Amount of cases to simulate and amount of input bytes per cases
Ncases = 100
Nbytes_per_case = 7
# Generate the input bytes value. In practice, the cases are represented as a
# numpy array where each row contains the input bytes to be used as `data` in
# C++ simulation wrapper.
cases = np.random.randint(0,256,[Ncases,Nbytes_per_case],dtype=np.uint8)
# Use Simu to predict the probed signals values. Here, we assume that no more
# than 100 saving of the probed state will be performed
predictions = plib.Simu(
cases,
100
)
# Use the predicted state for the signal "probed_state" for the "cid"-th case. Here, the
# "sid"-th saving of the simulation is recovered
useful_state = predictions["probed_state"][cid,sid,:]
... # do some stuff
Finally, some metadata are embedded in the library package and can be easily accessed for further usage. These are summarized here (use help($VERIME_LIB)
to get detailled information):
field | data |
---|---|
GENERICS |
A dictionary holding the value of the Verilog top-level generics used. |
PROBED_STATE_BYTES |
Amount of bytes required to encode all the probed internal state values |
SIGNALS |
List containing the names of the signal generated by Verime |
SIG_BITS |
A dictionary holding the width (in bits) for each probed signals |
SIG_BYTES |
A dictionary holding the amount of bytes used to encode each probe signals |
Parallelization
The generated simulation library releases the python global interpreter lock
(GIL) when running the simulations, hence simulations can be parallelized on
multi-core CPUs using python's thread-based concurrency such as
concurrent.futures.ThreadPoolExecutor
.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file verime-1.0.0.tar.gz
.
File metadata
- Download URL: verime-1.0.0.tar.gz
- Upload date:
- Size: 37.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b77ad655efbfb53ef42812252cde69fb6a54dcae3231df0b4988f5f3a69948af |
|
MD5 | 7ad4aa0aee59f59d5190cad21ecc3721 |
|
BLAKE2b-256 | 79c95e59b8651f339a112ee1d894b8341ebf31d94be4abedc988bba6278778df |
File details
Details for the file verime-1.0.0-py3-none-any.whl
.
File metadata
- Download URL: verime-1.0.0-py3-none-any.whl
- Upload date:
- Size: 36.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0e651a5ab86408a1e90e08a715377ee8bb106f047a0ad258a45f0b1cc0ca38f8 |
|
MD5 | 279111b7419ef51aabeac3ea64b79f4b |
|
BLAKE2b-256 | bbadb7fb8357815463ee358057f1bb5be01b6166e5bb41d50d2fcf0d08c13615 |