Add ChipTop to enable real chip configs with IO cells, etc. (#480)

This adds an additional layer (ChipTop) between the System module and the TestHarness. The IOBinder API is now changed to take only a single parameter (an Any) and return a 3 things: The IO port(s), the IO cell(s), and a function to call inside the test harness, which is analogous to the old IOBinder function, except that it takes a TestHarness object as an argument instead of (clock, reset, success).
* A new Top-level module, ChipTop, has been created. ChipTop instantiates a "system" module specified by BuildSystem.
* BuildTop now builds a ChipTop dut module in the TestHarness by default
* A new BuildSystem key has been added, which by default builds DigitalTop (previously just called Top)
* The IOBinders API has changed. IOBinders are now called inside of ChipTop and return a tuple3 of (IO ports, IO cells, harness functions). The harness functions are now called inside the TestHarness (this is analogous to the previous IOBinder functions).
* IO cell models have been included in ChipTop. These can be replaced with real IO cells for tapeout, or used as-is for simulation.
* The default for the TOP make variable is now ChipTop (was Top)
This commit is contained in:
John Wright
2020-04-01 14:03:56 -07:00
committed by GitHub
parent 3d253c0f67
commit 1f98c84210
23 changed files with 444 additions and 166 deletions

View File

@@ -2,17 +2,28 @@ Tops, Test-Harnesses, and the Test-Driver
===========================================
The three highest levels of hierarchy in a Chipyard
SoC are the Top (DUT), ``TestHarness``, and the ``TestDriver``.
The Top and ``TestHarness`` are both emitted by Chisel generators.
SoC are the ``ChipTop`` (DUT), ``TestHarness``, and the ``TestDriver``.
The ``ChipTop`` and ``TestHarness`` are both emitted by Chisel generators.
The ``TestDriver`` serves as our testbench, and is a Verilog
file in Rocket Chip.
Top/DUT
ChipTop/DUT
-------------------------
The top-level module of a Rocket Chip SoC is composed via cake-pattern.
Specifically, "Tops" extend a ``System``, which extends a ``Subsystem``, which extends a ``BaseSubsystem``.
``ChipTop`` is the top-level module that instantiates the ``System`` submodule, usually an instance of the concrete class ``DigitalTop``.
The vast majority of the design resides in the ``System``.
Other components that exist inside the ``ChipTop`` layer are generally IO cells, clock receivers and multiplexers, reset synchronizers, and other analog IP that needs to exist outside of the ``System``.
The ``IOBinders`` are responsible for instantiating the IO cells and defining the test harness collateral that connects to the top-level ports.
Most of these types of devices can be instantiated using custom ``IOBinders``, so the provided ``ChipTop`` and ``ChipTopCaughtReset`` classes are sufficient.
However, if needed, the ``BaseChipTop`` abstract class can be extended for building more custom ``ChipTop`` designs.
System/DigitalTop
-------------------------
The system module of a Rocket Chip SoC is composed via cake-pattern.
Specifically, ``DigitalTop`` extends a ``System``, which extends a ``Subsystem``, which extends a ``BaseSubsystem``.
BaseSubsystem

View File

@@ -77,10 +77,10 @@ It is used in the Rocket Chip SoC library and Chipyard framework in merging mult
This example shows the Chipyard default top that composes multiple traits together into a fully-featured SoC with many optional components.
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
:language: scala
:start-after: DOC include start: Top
:end-before: DOC include end: Top
:start-after: DOC include start: DigitalTop
:end-before: DOC include end: DigitalTop
There are two "cakes" or mixins here. One for the lazy module (ex. ``CanHavePeripherySerial``) and one for the lazy module
@@ -88,8 +88,8 @@ implementation (ex. ``CanHavePeripherySerialModuleImp`` where ``Imp`` refers to
all the logical connections between generators and exchanges configuration information among them, while the
lazy module implementation performs the actual Chisel RTL elaboration.
In the ``Top`` example class, the "outer" ``Top`` instantiates the "inner"
``TopModule`` as a lazy module implementation. This delays immediate elaboration
In the ``DigitalTop`` example class, the "outer" ``DigitalTop`` instantiates the "inner"
``DigitalTopModule`` as a lazy module implementation. This delays immediate elaboration
of the module until all logical connections are determined and all configuration information is exchanged.
The ``System`` outer base class, as well as the
``CanHavePeriphery<X>`` outer traits contain code to perform high-level logical
@@ -102,8 +102,9 @@ For example, the ``CanHavePeripherySerialModuleImp`` trait optionally physically
the ``SerialAdapter`` module, and instantiates queues.
In the test harness, the SoC is elaborated with
``val dut = Module(LazyModule(Top))``.
After elaboration, the result will be a ``Top`` module, which contains a
``val dut = p(BuildTop)(p)``.
After elaboration, the system submodule of ``ChipTop`` will be a ``DigitalTop`` module, which contains a
``SerialAdapter`` module (among others), if the config specified for that block to be instantiated.
From a high level, classes which extend ``LazyModule`` *must* reference

View File

@@ -15,10 +15,10 @@ that writes zeros to the memory at a configured address.
.. literalinclude:: ../../generators/chipyard/src/main/scala/example/InitZero.scala
:language: scala
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
:language: scala
:start-after: DOC include start: Top
:end-before: DOC include end: Top
:start-after: DOC include start: DigitalTop
:end-before: DOC include end: DigitalTop
We use ``TLHelper.makeClientNode`` to create a TileLink client node for us.
We then connect the client node to the memory system through the front bus (fbus).

View File

@@ -1,7 +1,7 @@
IOBinders
=========
In Chipyard we use a special ``Parameters`` key, ``IOBinders`` to determine what modules to bind to the IOs of a ``Top`` in the ``TestHarness``.
In Chipyard we use a special ``Parameters`` key, ``IOBinders`` to instantiate IO cells in the ``ChipTop`` layer and determine what modules to bind to the IOs of a ``ChipTop`` in the ``TestHarness``.
.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
:language: scala
@@ -9,11 +9,27 @@ In Chipyard we use a special ``Parameters`` key, ``IOBinders`` to determine what
:end-before: DOC include end: IOBinders
This special key solves the problem of duplicating test-harnesses for each different ``Top`` type.
This special key solves the problem of duplicating test-harnesses for each different ``System`` type.
You could just as well create a custom harness module that attaches IOs explicitly.
Instead, the ``IOBinders`` key provides a map from Scala traits to attachment behaviors.
Each ``IOBinder`` returns a tuple of three values: the list of ``ChipTop`` ports created by the ``IOBinder``, the list of all IO cell modules instantiated by the ``IOBinder``, and an optional function to be called inside the test harness.
This function is responsible for instantiating logic inside the ``TestHarness`` to appropriately drive the ``ChipTop`` IO ports created by the ``IOBinder``.
Conveniently, because the ``IOBinder`` is generating the port, it may also use the port inside this function, which prevents the ``BaseChipTop`` code from ever needing to access the port ``val``, thus having the ``IOBinder`` house all port specific code.
This scheme prevents the need to have two separate binder functions for each ``System`` trait.
When creating custom ``IOBinders`` it is important to use ``suggestName`` to name ports; otherwise Chisel will raise an exception trying to name the IOs.
The example ``IOBinders`` demonstrate this.
You could just as well create a custom harness module that attaches IOs explicitly. Instead, the IOBinders key provides a map from Scala traits to attachment behaviors.
As an example, the ``WithGPIOTiedOff`` IOBinder creates IO cells for the GPIO module(s) instantiated in the ``System``, then punches out new ``Analog`` ports for each one.
The test harness simply ties these off, but additional logic could be inserted to perform some kind of test in the ``TestHarness``.
For example, the ``WithSimAXIMemTiedOff`` IOBinder specifies that any ``Top`` which matches ``CanHaveMasterAXI4MemPortModuleImp`` will have a ``SimAXIMem`` connected.
.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
:language: scala
:start-after: DOC include start: WithGPIOTiedOff
:end-before: DOC include end: WithGPIOTiedOff
``IOBinders`` also do not need to create ports. Some ``IOBinders`` can simply insert circuitry inside the ``ChipTop`` layer.
For example, the ``WithSimAXIMemTiedOff`` IOBinder specifies that any ``System`` which matches ``CanHaveMasterAXI4MemPortModuleImp`` will have a ``SimAXIMem`` connected inside ``ChipTop``.
.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
:language: scala

View File

@@ -34,25 +34,25 @@ Accessing the value stored in the key is easy in Chisel, as long as the ``implic
Traits
------
Typically, most custom blocks will need to modify the behavior of some pre-existing block. For example, the GCD widget needs the ``Top`` module to instantiate and connect the widget via Tilelink, generate a top-level ``gcd_busy`` port, and connect that to the module as well. Traits let us do this without modifying the existing code for the ``Top``, and enables compartmentalization of code for different custom blocks.
Typically, most custom blocks will need to modify the behavior of some pre-existing block. For example, the GCD widget needs the ``DigitalTop`` module to instantiate and connect the widget via Tilelink, generate a top-level ``gcd_busy`` port, and connect that to the module as well. Traits let us do this without modifying the existing code for the ``DigitalTop``, and enables compartmentalization of code for different custom blocks.
Top-level traits specify that the ``Top`` has been parameterized to read some custom key and optionally instantiate and connect a widget defined by that key. Traits **should not** mandate the instantiation of custom logic. In other words, traits should be written with ``CanHave`` semantics, where the default behavior when the key is unset is a no-op.
Top-level traits specify that the ``DigitalTop`` has been parameterized to read some custom key and optionally instantiate and connect a widget defined by that key. Traits **should not** mandate the instantiation of custom logic. In other words, traits should be written with ``CanHave`` semantics, where the default behavior when the key is unset is a no-op.
Top-level traits should be defined and documented in subprojects, alongside their corresponding keys. The traits should then be added to the ``Top`` being used by Chipyard.
Top-level traits should be defined and documented in subprojects, alongside their corresponding keys. The traits should then be added to the ``DigitalTop`` being used by Chipyard.
Below we see the traits for the GCD example. The Lazy trait connects the GCD module to the Diplomacy graph, while the Implementation trait causes the ``Top`` to instantiate an additional port and concretely connect it to the GCD module.
Below we see the traits for the GCD example. The Lazy trait connects the GCD module to the Diplomacy graph, while the Implementation trait causes the ``DigitalTop`` to instantiate an additional port and concretely connect it to the GCD module.
.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
:language: scala
:start-after: DOC include start: GCD lazy trait
:end-before: DOC include end: GCD imp trait
These traits are added to the default ``Top`` in Chipyard.
These traits are added to the default ``DigitalTop`` in Chipyard.
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
:language: scala
:start-after: DOC include start: Top
:end-before: DOC include end: Top
:start-after: DOC include start: DigitalTop
:end-before: DOC include end: DigitalTop
Config Fragments
----------------

View File

@@ -87,21 +87,21 @@ For peripherals which instantiate a concrete module, or which need to be connect
:start-after: DOC include start: GCD imp trait
:end-before: DOC include end: GCD imp trait
Constructing the Top and Config
-------------------------------
Constructing the DigitalTop and Config
--------------------------------------
Now we want to mix our traits into the system as a whole.
This code is from ``generators/chipyard/src/main/scala/Top.scala``.
This code is from ``generators/chipyard/src/main/scala/DigitalTop.scala``.
.. literalinclude:: ../../generators/chipyard/src/main/scala/Top.scala
.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala
:language: scala
:start-after: DOC include start: Top
:end-before: DOC include end: Top
:start-after: DOC include start: DigitalTop
:end-before: DOC include end: DigitalTop
Just as we need separate traits for ``LazyModule`` and module implementation, we need two classes to build the system.
The ``Top`` class contains the set of traits which parameterize and define the ``Top``. Typically these traits will optionally add IOs or peripherals to the ``Top``.
The ``Top`` class includes the pre-elaboration code and also a ``lazy val`` to produce the module implementation (hence ``LazyModule``).
The ``TopModule`` class is the actual RTL that gets synthesized.
The ``DigitalTop`` class contains the set of traits which parameterize and define the ``DigitalTop``. Typically these traits will optionally add IOs or peripherals to the ``DigitalTop``.
The ``DigitalTop`` class includes the pre-elaboration code and also a ``lazy val`` to produce the module implementation (hence ``LazyModule``).
The ``DigitalTopModule`` class is the actual RTL that gets synthesized.