240 lines
11 KiB
ReStructuredText
240 lines
11 KiB
ReStructuredText
Using FireSim without Chipyard
|
|
==============================
|
|
|
|
FireSim is now standalone allowing (1) FireSim developers to test the repository without
|
|
Chipyard and (2) allowing non-Chipyard top-level projects to integrate FireSim as a
|
|
library. We will discuss option (2) in this section.
|
|
|
|
A non-Chipyard top-level serves as the target which FireSim will simulate. It must
|
|
provide a few items:
|
|
|
|
- A Chisel top-level "harness" to connect FireSim bridges to drive things like the clock
|
|
and reset.
|
|
- A C++ top-level simulation driver to indicate how to progress in the simulation.
|
|
- A series of Make fragments to configure the FireSim build system (otherwise called
|
|
MIDAS or Golden Gate).
|
|
|
|
We name this set of sources a "project".
|
|
|
|
Simple Counter Example Project
|
|
------------------------------
|
|
|
|
By default, FireSim provides a simple example on how to add your own RTL can create a
|
|
simulator with just a clock and reset. This serves as the starting point for users to
|
|
add future bridges or more complicated designs. Throughout this section you will see the
|
|
name ``examples`` at the end of paths, this is the FireSim "project" that we are using.
|
|
|
|
Top-Level Harness
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
:gh-file-ref:`sim/src/main/scala/examples/SimpleCounter.scala` holds the simple
|
|
top-level harness which wraps around a simple counter that increments to 1000.
|
|
|
|
Looking at the counter module, it outputs a ``done`` signal when the counter reaches
|
|
1000.
|
|
|
|
.. literalinclude:: ../../sim/src/main/scala/examples/SimpleCounter.scala
|
|
:language: scala
|
|
:start-after: DOC include start: Counter
|
|
:end-before: DOC include end: Counter
|
|
|
|
To simulate this module, we need to wrap it in test harness that will source/sink it's
|
|
IOs and will also drive the reset and clock of the module. This is shown below:
|
|
|
|
.. literalinclude:: ../../sim/src/main/scala/examples/SimpleCounter.scala
|
|
:language: scala
|
|
:start-after: DOC include start: TestHarness
|
|
:end-before: DOC include end: TestHarness
|
|
|
|
First, we create a top-level ``clock`` and ``reset`` wire that is used for the simple
|
|
counter module. This is shown here:
|
|
|
|
.. literalinclude:: ../../sim/src/main/scala/examples/SimpleCounter.scala
|
|
:language: scala
|
|
:start-after: DOC include start: ClockResetWire
|
|
:end-before: DOC include end: ClockResetWire
|
|
|
|
Next, we connect those ``clock`` and ``reset`` wires to two corresponding bridges that
|
|
can drive the ``clock`` and ``reset`` for the simulation. In this case, we use the
|
|
``RationalClockBridge`` and the ``ResetPulseBridge`` which run the simulation on one
|
|
clock domain and reset the simulation. In more complex cases, these bridges can be used
|
|
to drive multi-clock simulations or drive a reset pulse for a longer period of time.
|
|
This is shown here:
|
|
|
|
.. literalinclude:: ../../sim/src/main/scala/examples/SimpleCounter.scala
|
|
:language: scala
|
|
:start-after: DOC include start: Bridges
|
|
:end-before: DOC include end: Bridges
|
|
|
|
Finally, we need to instantiate our simple counter and wire up it's IOs. This is done
|
|
here:
|
|
|
|
.. literalinclude:: ../../sim/src/main/scala/examples/SimpleCounter.scala
|
|
:language: scala
|
|
:start-after: DOC include start: CL
|
|
:end-before: DOC include end: CL
|
|
|
|
Since we are creating logic within a Chisel ``RawModule`` we need to indicate that the
|
|
``SimpleCounter`` and registers we are using have a default ``clock`` and ``reset``.
|
|
This is done with the ``withClockAndReset`` scope. Also note that this RTL just prints,
|
|
we will use the C++ top-level to terminate the simulation by timing out in the next
|
|
section.
|
|
|
|
C++ Driver Top
|
|
~~~~~~~~~~~~~~
|
|
|
|
:gh-file-ref:`sim/src/main/cc/examples/simple_counter_top.cc` defines the C++ top-level
|
|
simulation driver called ``simple_counter_top_t`` for the simulation. It is in charge of
|
|
adding any extra widgets/bridges, determining how to step the simulation, and
|
|
terminating. Most of this file is boilerplate code (i.e. code that can be copied from
|
|
the example), but two sections are highlighted here.
|
|
|
|
First, we need to define a core simulation loop. This loop is in charge of managing the
|
|
simulation and indicating when bridges need to run their logic. This is shown here:
|
|
|
|
.. literalinclude:: ../../sim/src/main/cc/examples/simple_counter_top.cc
|
|
:language: scala
|
|
:start-after: DOC include start: Loop
|
|
:end-before: DOC include end: Loop
|
|
|
|
You can see things like ``peek_poke.step`` being called to "step" forward in the
|
|
simulation, ``bridge->tick()`` to run bridge logic and more. This loop is terminated
|
|
after N cycles which is given adding ``+max-cycles=N`` to the simulator binary (this is
|
|
defined in the ``systematic_scheduler_t`` class).
|
|
|
|
Second, we need to register the ``simple_counter_top_t`` class as the main simulation
|
|
driver class in the default FireSim main function. This is done here:
|
|
|
|
.. literalinclude:: ../../sim/src/main/cc/examples/simple_counter_top.cc
|
|
:language: scala
|
|
:start-after: DOC include start: RegisterWithMain
|
|
:end-before: DOC include end: RegisterWithMain
|
|
|
|
This code simply creates a unique pointer to the simulation class you want (in this case
|
|
``simple_counter_top_t``) in a function that is called in FireSim's main function.
|
|
|
|
Make fragments
|
|
~~~~~~~~~~~~~~
|
|
|
|
Next, you need to provide a series of Make fragments to configure the FireSim build
|
|
system to generate the RTL to run with Golden Gate. This is located in
|
|
:gh-file-ref:`sim/src/main/makefrag/examples`.
|
|
|
|
This area consists of four Make fragments that indicate how to build/run/configure the
|
|
project:
|
|
|
|
- :gh-file-ref:`sim/src/main/makefrag/examples/build.mk` - Target-RTL generation
|
|
- :gh-file-ref:`sim/src/main/makefrag/examples/config.mk` - Variable defaults
|
|
- :gh-file-ref:`sim/src/main/makefrag/examples/driver.mk` - MIDAS/Golden-Gate sources
|
|
- :gh-file-ref:`sim/src/main/makefrag/examples/metasim.mk` - Top-level meta-simulator
|
|
targets
|
|
|
|
Starting with the :gh-file-ref:`sim/src/main/makefrag/examples/build.mk`, this file
|
|
specifies a rule to build the ``FIRRTL_FILE`` and ``ANNO_FILE`` files needed for
|
|
downstream FireSim Make rules. This FIRRTL file needs to be a Chisel 3.6 (i.e. Scala
|
|
FIRRTL Compiler) compatible FIRRTL file. In this case, we reuse the Chisel generator
|
|
binary (i.e. ``midas.chiselstage.Generator``) for RTL generation since all the Scala
|
|
sources are defined in the FireSim top-level :gh-file-ref:`sim/build.sbt`
|
|
|
|
Next is :gh-file-ref:`sim/src/main/makefrag/examples/config.mk`. This file provides
|
|
capitalized variables used throughout the FireSim Make system. This is set to sensible
|
|
defaults but each of these variables can be overridden on the make command line (i.e.
|
|
``make TARGET_CONFIG=... ...``). In this case, we point to our simulation top-level
|
|
module ``SimpleCounterHarness``.
|
|
|
|
Next is :gh-file-ref:`sim/src/main/makefrag/examples/driver.mk` . This file provides the
|
|
``DRIVER_H``, ``DRIVER_CC``, ``TARGET_CXX_FLAGS``, and ``TARGET_LD_FLAGS`` needed for
|
|
the FireSim Make system to build a C++ driver for the simulation. In this case, we point
|
|
to our ``simple_counter_top.cc`` file that we created earlier.
|
|
|
|
Finally, the :gh-file-ref:`sim/src/main/makefrag/examples/metasim.mk` file provides Make
|
|
targets for running metasimulations through the FireSim MakeFile. You can use this to
|
|
add targets to run your target with any simulator, or whatever else. In this case, we
|
|
define simulation targets for Verilator and VCS that indicate to the simulation that it
|
|
should timeout after 10000 cycles.
|
|
|
|
Running meta-simulations and more
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Once these Make fragments are added, you can then run the FireSim MakeFile or build
|
|
FireSim recipes by invoking the Make system with ``TARGET_PROJECT_MAKEFRAG`` pointing to
|
|
the Make fragments (i.e. ``make TARGET_PROJECT_MAKEFRAG=<PATH/TO/MAKEFRAG/FOLDER>
|
|
...``). In this case, since the files reside within FireSim, the
|
|
``TARGET_PROJECT_MAKEFRAG`` will be properly set to
|
|
``sim/src/main/makefrag/<TARGET_PROJECT>`` so we just need to define the
|
|
``TARGET_PROJECT`` to be ``examples``.
|
|
|
|
The following code runs a metasimulation with VCS for our example RTL test harness, C++
|
|
code, and make fragments.
|
|
|
|
.. code-block:: bash
|
|
|
|
cd $FS_DIR
|
|
source sourceme-manager.sh --skip-ssh-setup
|
|
make TARGET_PROJECT=examples run-vcs
|
|
|
|
Chipyard Example
|
|
----------------
|
|
|
|
For the remainder of this section we will use Chipyard as an example of how to integrate
|
|
FireSim into a top-level project. In the future, we will provide a simplified example
|
|
non-Chipyard top-level setup that users can reference.
|
|
|
|
Top-Level Harness
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
An example of a FireSim top-level harness is in
|
|
:cy-gh-file-ref:`generators/firechip/chip/src/main/scala/FireSim.scala`. While there are
|
|
alot of extra Chipyard specific features, the main focus should be on adding a
|
|
``ResetPulseBridge`` to drive the top-level reset of the system, and adding a
|
|
``RationalClockBridge`` to drive system clocks. Then the harness can choose to
|
|
instantiate any other target-specific bridges (i.e. the FASED DRAM model or a UART
|
|
bridge for example).
|
|
|
|
C++ Driver Top
|
|
~~~~~~~~~~~~~~
|
|
|
|
Next, you need to provide a top-level C++ driver such as
|
|
:cy-gh-file-ref:`generators/firechip/chip/src/main/cc/firesim/firesim_top.cc`. This
|
|
indicates how the bridges should be run, and when.
|
|
|
|
Make fragments
|
|
~~~~~~~~~~~~~~
|
|
|
|
Next, you need to provide a series of Make fragments to configure the FireSim build
|
|
system to generate the RTL to run with Golden Gate. Chipyard's example is here:
|
|
:cy-gh-file-ref:`generators/firechip/chip/src/main/makefrag/firesim`.
|
|
|
|
Starting with the ``build.mk``, this file specifies a rule to build the ``FIRRTL_FILE``
|
|
and ``ANNO_FILE`` files needed for downstream FireSim Make rules. This FIRRTL file needs
|
|
to be a Chisel 3.6 (i.e. Scala FIRRTL Compiler) compatible FIRRTL file. In Chipyard's
|
|
case, the Chipyard MakeFile is invoked always (due to the dependency on a ``.PHONY``
|
|
target) to create the two files. However, Chipyard's MakeFile doesn't update the files
|
|
if nothing has changed preventing downstream FireSim Make rules from re-running.
|
|
Additionally, the file has a variable ``TARGET_COPY_TO_MIDAS_SCALA_DIRS`` this allows
|
|
the ``firesim_target_symlink_hook`` target to symlink target-specific bridge directories
|
|
into MIDAS to compile (in this case, ``bridgeinterfaces`` and
|
|
``golgengateimplementations``). If you are using the default bridges (i.e.
|
|
``ResetPulseBridge`` and ``RationalClockBridge``) then this variable and target
|
|
shouldn't be needed.
|
|
|
|
Next is ``config.mk``. This file provides capitalized variables used throughout the
|
|
FireSim Make system. This also provides Make variables that can be visible from the
|
|
other ``*.mk`` files in this directory (like ``chipyard_dir``).
|
|
|
|
Next is ``driver.mk``. This file provides the ``DRIVER_H``, ``DRIVER_CC``,
|
|
``TARGET_CXX_FLAGS``, and ``TARGET_LD_FLAGS`` needed for the FireSim Make system to
|
|
build a C++ driver for the simulation. When using default bridges, you should just need
|
|
to add your ``firesim_top.cc`` file to the ``DRIVER_CC``. Additionally, you might need
|
|
to add to ``TARGET_CXX_FLAGS`` an include path to the generated directory (i.e.
|
|
``-I$(GENERATED_DIR)``).
|
|
|
|
Finally, the ``metasim.mk`` file provides Make targets for running metasimulations
|
|
through the FireSim MakeFile. You can use this to add targets to run your target with
|
|
any simulator, or whatever else.
|
|
|
|
Once these Make fragments are added, you can then run the FireSim MakeFile or build
|
|
FireSim recipes by invoking the Make system with ``TARGET_PROJECT_MAKEFRAG`` pointing to
|
|
the Make fragments (like
|
|
:cy-gh-file-ref:`generators/firechip/chip/src/main/makefrag/firesim`).
|