doc: simulation

This commit is contained in:
Sebastien Bourdeauducq 2012-03-09 21:17:21 +01:00
parent 0165d23295
commit 7b1101ab99
1 changed files with 153 additions and 3 deletions

View File

@ -365,7 +365,7 @@ The control signals are used to issue requests.
* Hub-to-Master
* ``tag_issue`` is an integer representing the transaction ("tag") attributed by the hub. The width of this signal is determined by the maximum number of in-flight transactions that the hub port can handle.
* ``ack`` is asserted when ``tag_issue`` is valid and the transaction has been registered by the hub. A hub may assert ``ack'' even when ``stb`` is low, which means it is ready to accept any new transaction and will do as soon as ``stb`` goes high.
* ``ack`` is asserted when ``tag_issue`` is valid and the transaction has been registered by the hub. A hub may assert ``ack`` even when ``stb`` is low, which means it is ready to accept any new transaction and will do as soon as ``stb`` goes high.
The data signals are used to complete requests.
@ -404,8 +404,8 @@ Using ASMI with Migen
=====================
TODO: please document me!
Dataflow high-level synthesis
#############################
Dataflow synthesis
##################
.. WARNING::
This is experimental and incomplete.
@ -438,8 +438,158 @@ Actor graphs are managed using the NetworkX [networkx]_ library.
Simulating a Migen design
#########################
Migen allows you to easily simulate your FHDL design and interface it with arbitrary Python code.
To interpret the design, the FHDL structure is simply converted into Verilog and then simulated using an external program (e.g. Icarus Verilog). This is is intrinsically compatible with VHDL/Verilog instantiations from Migen and maximizes software reuse.
To interface the external simulator to Python, a VPI task is called at each clock cycle and implement the test bench functionality proper - which can be fully written in Python.
Signals inside the simulator can be read and written using VPI as well. This is how the Python test bench generates stimulus and obtains the values of signals for processing.
.. _vpisetup:
Installing the VPI module
*************************
To communicate with the external simulator, Migen uses a UNIX domain socket and a custom protocol which is handled by a VPI plug-in (written in C) on the simulator side.
To build and install this plug-in, run the following commands from the ``vpi`` directory: ::
make [INCDIRS=-I/usr/...]
make install [INSTDIR=/usr/...]
The variable ``INCDIRS`` (default: empty) can be used to give a list of paths where to search for the include files. This is useful considering that different Linux distributions put the ``vpi_user.h`` file in various locations.
The variable ``INSTDIR`` (default: ``/usr/lib/ivl``) specifies where the ``migensim.vpi`` file is to be installed.
This plug-in is designed for Icarus Verilog, but can probably be used with most Verilog simulators with minor modifications.
The generic simulator object
****************************
The generic simulator object (``migen.sim.generic.Simulator``) is the central component of the simulation.
Creating a simulator object
===========================
The constructor of the ``Simulator`` object takes the following parameters:
#. The fragment to simulate. The fragment can (and generally does) contain both synthesizable code and a non-synthesizable list of simulation functions.
#. A simulator runner object (see :ref:`simrunner`).
#. A top-level object (see :ref:`toplevel`). With the default value of ``None``, the simulator creates a default top-level object itself.
#. The name of the UNIX domain socket used to communicate with the external simulator through the VPI plug-in (default: "simsocket").
Running the simulation
======================
Running the simulation is achieved by calling the ``run`` method of the ``Simulator`` object.
It takes an optional parameter that defines the maximum number of clock cycles that this call simulates. The default value of -1 sets no cycle limit.
The simulation runs until the maximum number of cycles is reached, or a simulation function sets the property ``interrupt`` to ``True`` in the ``Simulator`` object.
At each clock cycle, the ``Simulator`` object runs in turn all simulation functions listed in the fragment. Simulation functions must take exactly one parameter which is used by the instance of the ``Simulator`` object to pass a reference to itself.
Simulation functions can read the current simulator cycle by reading the ``cycle_counter`` property of the ``Simulator``. The cycle counter's value is 0 for the cycle immediately following the reset cycle.
Reading and writing signals
===========================
Reading and writing signals is done by calling the ``Simulator`` object's methods ``rd`` and ``wr`` (respectively) from simulation functions.
The ``rd`` method takes the FHDL ``Signal`` object to read and returns its value as a Python integer. The returned integer is the value of the signal immediately before the clock edge.
The ``wr`` method takes a ``Signal`` object and the value to write as a Python integer. The signal takes the new value immediately after the clock edge.
The semantics of reads and writes (respectively immediately before and after the clock edge) match those of the non-blocking assignment in Verilog. Note that because of Verilog's design, reading "variable" signals (i.e. written to using blocking assignment) directly may give unexpected and non-deterministic results and is not supported. You should instead read the values of variables after they have gone through a non-blocking assignment in the same ``always`` block.
Reading and writing memories
============================
References to FHDL ``Memory`` objects can also be passed to the ``rd`` and ``wr`` methods. In this case, they take an additional parameter for the memory address.
Initializing signals and memories
=================================
A simulation function can access (and typically initialize) signals and memories during the reset cycle if it has its property ``initialize`` set to ``True``.
In this case, it will be run once at the beginning of the simulation with a cycle counter value of -1 indicating the reset cycle.
.. _simrunner:
The external simulator runner
*****************************
Role
====
The runner object is responsible for starting the external simulator, loading the VPI module, and feeding the generated Verilog into the simulator.
It must implement a ``start`` method, called by the ``Simulator``, which takes two strings as parameters. They contain respectively the Verilog source of the top-level design and the converted fragment.
Icarus Verilog support
======================
Migen comes with a ``migen.sim.icarus.Runner`` object that supports Icarus Verilog.
Its constructor has the following optional parameters:
#. ``extra_files`` (default: ``None``): lists additional Verilog files to simulate.
#. ``top_file`` (default: "migensim_top.v"): name of the temporary file containing the top-level.
#. ``dut_file`` (default: "migensim_dut.v"): name of the temporary file containing the converted fragment.
#. ``vvp_file`` (default: ``None``): name of the temporary file compiled by Icarus Verilog. When ``None``, becomes ``dut_file + "vp"``.
#. ``keep_files`` (default: ``False``): do not delete temporary files. Useful for debugging.
.. _toplevel:
The top-level object
********************
Role of the top-level object
============================
The top-level object is responsible for generating the Verilog source for the top-level test bench.
It must implement a method ``get`` that takes as parameter the name of the UNIX socket the VPI plugin should connect to, and returns the full Verilog source as a string.
It must have the following attributes (which are read by the ``Simulator`` object):
* ``clk_name``: name of the clock signal.
* ``rst_name``: name of the reset signal.
* ``dut_type``: module type of the converted fragment.
* ``dut_name``: name used for instantiating the converted fragment.
* ``top_name``: name/module type of the top-level design.
Role of the generated Verilog
=============================
The generated Verilog must:
#. instantiate the converted fragment and connect its clock and reset ports.
#. produce a running clock signal.
#. assert the reset signal for the first cycle and deassert it immediately after.
#. at the beginning, call the task ``$migensim_connect`` with the UNIX socket name as parameter.
#. at each rising clock edge, call the task ``$migensim_tick``. It is an error to call ``$migensim_tick`` before a call to ``$migensim_connect``.
#. set up the optional VCD output file.
The generic top-level object
============================
Migen comes with a ``migen.sim.generic.TopLevel`` object that implements the above behaviour. It should be usable in the majority of cases.
The main parameters of its constructor are the output VCD file (default: ``None``) and the levels of hierarchy that must be present in the VCD (default: 1).
Basic simulation example
========================
::
from migen.fhdl.structure import *
from migen.sim.generic import Simulator
from migen.sim.icarus import Runner
class Counter:
def __init__(self):
self.count = Signal(BV(4))
def do_simulation(self, s):
print("Count: " + str(s.rd(self.count)))
def get_fragment(self):
sync = [self.count.eq(self.count + 1)]
sim = [self.do_simulation]
return Fragment(sync=sync, sim=sim)
def main():
dut = Counter()
sim = Simulator(dut.get_fragment(), Runner())
sim.run(20)
main()