Skip to content

Netlist Simulator and Waveform Viewer

joern274 edited this page Jun 2, 2022 · 3 revisions
Attention: 

Note: netlist simulator is currently experiencing a major makeover. The code has not been merged into master yet but can be obtained in branch v4.0.0

The **netlist simulator** enables the reverse engineer to simulate selected parts of the netlist in a cycle-accurate fashion. It operates on a user-defined set of [gates](Gate) and allows for all kinds of customization. The netlist simulator is not build automatically and needs to be enabled before starting the build process using the `-DPL_NETLIST_SIMULATOR=1` flag when calling `cmake`.

  1. Loading the Plugin
Similar to all other plugins, the netlist simulator can be loaded using the following Python snippet:
from hal_plugins import netlist_simulator

pl_sim = hal_py.plugin_manager.get_plugin_instance("netlist_simulator")
  1. Setting up the Simulator
Before the simulation starts, a simulator instance needs to be created by calling 'create_simulator' on the plugin instance as follows:
sim = pl_sim.create_simulator()

Now the user must specify the subset of the netlist he wants to simulate. For this purpose, a list of gates must be specified and passed to the simulator instance `sim` such that it can automatically determine input and output [nets](Net) of the subgraph. This can be done using the `add_gates` function as shown below.

gate_list = [g1, g2, g3, g4]   # collect gates to simulate in a list
sim.add_gates(gate_list)        # add the gates to the simulation instance

As stated before, the simulator will automatically detect all inputs and outputs to the simulated subgraph. Using `get_input_nets` and `get_output_nets`, the user can retrieve those nets and may use them later on to, e.g., set input values during simulation.

sim.get_input_nets()    # get all input nets
sim.get_output_nets()   # get all output nets

Next, the clock signal needs to be specified. This can be done using either `add_clock_frequency` or `add_clock_period`, which take the net connected to the clock and a clock frequency or period as input. Hence, the function assignes a specific clock frequency to the given net and thus allows for different clock domains.




sim.add_clock_frequency(clk, 1000000)   # assign a clock frequency of 1 MHz to input net 'clk'

To avoid getting stuck in infinite loops, it may be helpful to set a iteration timeout that stops the simulation after a pre-defined number of iterations. The timeout is given in number of iterations and can be controlled using `set_iteration_timeout` and `get_iteration_timeout`. It defaults to `10000000` iterations and assigning a value of `0` disables the timeout.

sim.set_iteration_timeout(1000)   # set the timeout to 1000 iterations

In some cases, sequential gates may come with an initial value that is assigned to their internal state during device startup. For example, this is the case for flip-flops within FPGA netlists, which may retrieve their initial state from the netlist or rather bitstream file. To accommodate this during simulation, these initial values can be loaded into the internal state of such sequential elements using `load_initial_values_from_netlist`. Further, `load_initial_values` may be used to specify an initial value to load into all sequential elements before running the simulation.

sim.load_initial_values_from_netlist()                        # load initial values into sequential gates if available
sim.load_initial_values(hal_py.BooleanFunction.Value.ZERO)   # load logical 0 into all FFs before running simulation
  1. Running the Simulation
To finally run the simulation, logical values should be assigned to all of the input nets of the simulated subgraph. The function `set_input` provides exactly this functionality and takes a net as well as a value as input. It may also be used during simulation to change input values between simulation cycles. The simulation itself is started using `simulate` and proceeds for the specified number of picoseconds. An example is given below.
sim.set_input(in1, hal_py.BooleanFunction.Value.ONE)    # assign a logical 1 to input net in1
sim.set_input(in2, hal_py.BooleanFunction.Value.ZERO)   # assign a logical 0 to input net in2
sim.simulate(3000)                                       # simulate for 3000 ps
sim.set_input(in1, hal_py.BooleanFunction.Value.ZERO)   # change the value of in1 to a logical 0
sim.simulate(2000)                                       # simulate for another 2000 ps
  1. Retrieving Simulation Results
To get the results of the simulation, the function `get_simulation_state` may be called to retrieve the entire simulation result. It returns a simulation state containing detailed information of the simulation including all incurred events. By calling `get_net_value` on that instance, the user can query the value of each net included in the subgraph at every point in time during simulation.
state = sim.get_simulation_state()     # retrieve the simulation state
val = state.get_net_value(net, 1000)   # get the value of net 'net' after 1000 ps

Furthermore, the simulation state holds a list of all events incurred during simulation that can be obtained using `get_events`. This list may also be expanded by the user by creating and adding a new event using `add_event`. The current simulation state of the simulator instance may be overwritten using `set_simulation_state`. More on these operations can be found in the official Python API documentation.

  1. Generating a Waveform File
To assist the simulation process, HAL can generate waveform files using the VCD file format. For this purpose, the function `generate_vcd` can be called to write the current simulation instance to a waveform file. The user may specify a start and an end time (in ps) to avoid the generation of unnecessarily large traces. The VCD file can then be viewed using popular open-source tools like [gtkwave](http://gtkwave.sourceforge.net). An example looks like this:
sim.generate_vcd("trace.vcd", 0, 300000)
  1. Example: Simulating a simple counter
In our tests we provide a netlist for a [simple](https://github.com/emsec/hal/blob/master/plugins/netlist_simulator/test/netlists/counter/counternetlist_flattened_by_hal.vhd). The following code simulates the netlist:
from hal_plugins import netlist_simulator
pl_sim = hal_py.plugin_manager.get_plugin_instance("netlist_simulator")

sim = pl_sim.create_simulator()

sim.add_gates(netlist.get_gates())
sim.load_initial_values(hal_py.BooleanFunction.Value.ZERO)

clock_enable_B = netlist.get_net_by_id(8)
clock = netlist.get_net_by_id(9)
reset = netlist.get_net_by_id(7)
output_0 = netlist.get_net_by_id(6)
output_1 = netlist.get_net_by_id(5)
output_2 = netlist.get_net_by_id(4)
output_3 = netlist.get_net_by_id(3)

sim.add_clock_period(clock, 10000)

sim.set_input(clock_enable_B, hal_py.BooleanFunction.Value.ONE)
sim.set_input(reset, hal_py.BooleanFunction.Value.ZERO)
sim.simulate(40 * 1000)

sim.set_input(clock_enable_B, hal_py.BooleanFunction.Value.ZERO)
sim.simulate(110 * 1000)

sim.set_input(reset, hal_py.BooleanFunction.Value.ONE)
sim.simulate(20 * 1000)

sim.set_input(reset, hal_py.BooleanFunction.Value.ZERO)
sim.simulate(70 * 1000)

sim.set_input(clock_enable_B, hal_py.BooleanFunction.Value.ONE)
sim.simulate(23 * 1000)

sim.set_input(reset, hal_py.BooleanFunction.Value.ONE)
sim.simulate(20 * 1000)

sim.simulate(20 * 1000)

sim.simulate(5 * 1000)

sim.generate_vcd("counter.vcd", 0, 300000)

The generated vcd can be viewed using gtkwave.

![Waveform](https://github.com/emsec/hal/blob/master/wiki_images/simulator/counter_gtkwave.png)

Clone this wiki locally