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

@@ -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.