Skip to main content

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:

  1. Annotate the targeted signal in the Verilog source(s) file(s) with the attribute (* verime = "probed_sig_name" *).
  2. 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).
  3. Run the Verime tool to build a python package (wheel) and install it as any other python package with pip.
  4. Use the simulation package in python scirpts.

Dependencies

In addition to Python (3.10 tested), Verime has the following native dependencies:

  • Yosys (versions 0.20 and 0.23 known to work)
  • Verilator (version 5.006 known to work)
  • GNU Make

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 (no PATH modification needed). Some versions of Verilator are known to not behave correctly when installed with make 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:

  1. 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);
...
  1. 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();
...
  1. 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 the data 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 the eval() function of the generated Verilator object. This function should be used only if you cannot use sim_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

verime-1.0.1.tar.gz (38.1 kB view details)

Uploaded Source

Built Distribution

verime-1.0.1-py3-none-any.whl (37.5 kB view details)

Uploaded Python 3

File details

Details for the file verime-1.0.1.tar.gz.

File metadata

  • Download URL: verime-1.0.1.tar.gz
  • Upload date:
  • Size: 38.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.6

File hashes

Hashes for verime-1.0.1.tar.gz
Algorithm Hash digest
SHA256 4b678162909d7044e6d5c8b1c1bfdfca9dd83cffd9c9c7b2c49cb1fb780028dd
MD5 4a43f54881a09424d030be5d3882ae93
BLAKE2b-256 6544aa3f9d9dddfa711778e51d5e6e5af2c6e8dbad667e76983f88d16eb9a55f

See more details on using hashes here.

File details

Details for the file verime-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: verime-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 37.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.6

File hashes

Hashes for verime-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 87823b99c8b4a1cc92b76b0738aab3d4e2c4c6b9c148f848d9e31e48d6d24517
MD5 c2cfaf4a2c493f6329bb81cc4bcb0b8c
BLAKE2b-256 76d48c5242d117b296f44894e210c7effb35906544e10dafc1bae5a90cafc065

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page