From ab21c53a42a4bd313c86a84b9c67abe7c77ee69d Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Fri, 4 Sep 2020 23:49:40 -0700 Subject: [PATCH] Add documentation on HarnessBinders --- docs/Advanced-Concepts/Debugging-BOOM.rst | 13 +++-- docs/Advanced-Concepts/Top-Testharness.rst | 6 +-- docs/Customization/IOBinders.rst | 54 ++++++++++--------- .../src/main/scala/HarnessBinders.scala | 2 + .../chipyard/src/main/scala/IOBinders.scala | 13 +---- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/Advanced-Concepts/Debugging-BOOM.rst b/docs/Advanced-Concepts/Debugging-BOOM.rst index 1e61804c58..fd41a9d7ce 100644 --- a/docs/Advanced-Concepts/Debugging-BOOM.rst +++ b/docs/Advanced-Concepts/Debugging-BOOM.rst @@ -12,11 +12,14 @@ to verify functionality. Setting up Dromajo Co-simulation -------------------------------------- -Dromajo co-simulation is setup to work when two config fragments are added to a BOOM config. -First, a ``chipyard.config.WithTraceIO`` config fragment must be added so that BOOM's traceport is enabled. -Second, a ``chipyard.iobinders.WithSimDromajoBridge`` config fragment must be added to -connect the Dromajo co-simulator to the traceport. -Once both config fragments are added Dromajo should be enabled. +Dromajo co-simulation is setup to work when three config fragments are added to a BOOM config. + + * A ``chipyard.config.WithTraceIO`` config fragment must be added so that BOOM's traceport is enabled. + * A ``chipyard.iobinders.WithTraceIOPunchthrough`` config fragment must be added to add the ``TraceIO`` to the ``ChipTop`` + * A ``chipyard.harness.WithSimDromajoBridge`` config fragment must be added to instantiate a Dromajo cosimulator in the ``TestHarness`` and connect it to the ``ChipTop``'s ``TraceIO`` + + +Once all config fragments are added Dromajo should be enabled. To build/run Dromajo with a BOOM design, run your configuration the following make commands: diff --git a/docs/Advanced-Concepts/Top-Testharness.rst b/docs/Advanced-Concepts/Top-Testharness.rst index ebd5b3702f..43a8a33813 100644 --- a/docs/Advanced-Concepts/Top-Testharness.rst +++ b/docs/Advanced-Concepts/Top-Testharness.rst @@ -14,9 +14,9 @@ ChipTop/DUT ``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. +The ``IOBinders`` are responsible for instantiating the IO cells for ``ChipTop`` IO that correspond to IO of the ``System``. +The ``HarnessBinders`` are responsible for instantiating test harness collateral that connects to the ``ChipTop`` ports. +Most types of devices and testing collateral can be instantiated using custom ``IOBinders`` and ``HarnessBinders``. System/DigitalTop diff --git a/docs/Customization/IOBinders.rst b/docs/Customization/IOBinders.rst index 6332d07a97..55920cd3fb 100644 --- a/docs/Customization/IOBinders.rst +++ b/docs/Customization/IOBinders.rst @@ -1,41 +1,45 @@ +IOBinders and HarnessBinders +============================ + +In Chipyard we use special ``Parameters`` keys, ``IOBinders`` and ``HarnessBinders`` to bridge the gap between digital system IOs and TestHarness collateral. + IOBinders ========= -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``. +The ``IOBinder`` functions are responsible for instantiating IO cells and IOPorts in the ``ChipTop`` layer. + +``IOBinders`` are typically defined using the ``OverrideIOBinder`` or ``ComposeIOBinder`` macros. An ``IOBInder`` consists of a function matching ``Systems`` with a given trait that generates IO ports and IOCells, and returns a list of generated ports and cells. + +For example, the ``WithUARTIOCells`` IOBinder specifies will, for any ``System`` that might have UART ports (``HasPeripheryUARTModuleIMP``, generate ports within the ``ChipTop`` (``ports``) as well as IOCells with the appropriate type and direction (``cells2d``). This function returns a the list of generated ports, and the list of generate IOCells. The list of generated ports is passed to the ``HarnessBinders`` such that they can be connected to ``TestHarness`` devices. + .. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala :language: scala - :start-after: DOC include start: IOBinders - :end-before: DOC include end: IOBinders + :start-after: DOC include start: WithUARTIOCells + :end-before: DOC include end: WithUARTIOCells +HarnessBinders +============== -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. +The ``HarnessBinder`` functions determine what modules to bind to the IOs of a ``ChipTop`` in the ``TestHarness``. The ``HarnessBinder`` interface is designed to be reused across various simulation modes, enabling decoupling of the target design from simulation and testing concerns. -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 SW RTL simulations, the default set of ``HarnessBinders`` instantiate software-simulated models of various devices, for example external memory or UART, and connect those models to the IOs of the ``ChipTop``. + * For FireSim simulations, FireSim-specific ``HarnessBinders`` instantiate ``Bridges``, which faciliate cycle-accurate simulation across the simulated chip's IOs. See the FireSim documentation for more details. + * In the future, a Chipyard FPGA prototyping flow may use ``HarnessBinders`` to connect ``ChipTop`` IOs to other devices or IOs in the FPGA harness. -.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala - :language: scala - :start-after: DOC include start: WithGPIOTiedOff - :end-before: DOC include end: WithGPIOTiedOff +For FireSim simulations, the ``HarnessBinder`` attach ``Bridge`` modules (See the FireSim documentation for more details). +Like ``IOBinders``, ``HarnessBinders`` are defined using macros (``OverrideHarnessBinder, ComposeHarnessBinder``), and matches ``Systems`` with a given trait. However, ``HarnessBinders`` are also passed a reference to the ``TestHarness`` (``th: HasHarnessSignalReferences``) and the list of ports generated by the corresponding ``IOBinder`` (``ports: Seq[Data]``). -``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``. +For exmaple, the ``WithUARTAdapter`` will connect the UART SW display adapter to the ports generated by the ``WithUARTIOCells`` described earlier, if those ports are present. -.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/HarnessBinders.scala :language: scala - :start-after: DOC include start: WithSimAXIMem - :end-before: DOC include end: WithSimAXIMem + :start-after: DOC include start: WithUARTAdapter + :end-before: DOC include end: WithUARTAdapter + +The ``IOBinder`` and ``HarnesBinder`` system is designed to enable decoupling of concerns between target design and simulation ssystem. -These classes are all ``Config`` objects, which can be mixed into the configs to specify IO connection behaviors. +For a given set of chip IOs, there may be not only multiple simulation platforms ("harnesses", so-to-speak), but also multiple simulation strategies. For example, the choice of whether to connect the backing AXI4 memory port to a accurate DRAM model (``SimDRAM``) or a simple simulated memory model (``SimAXIMem``) is isolated in ``HarnessBinders``, and does not affect target RTL generation. -There are two macros for generating these ``Config``s. ``OverrideIOBinder`` overrides any existing behaviors set for a particular IO in the ``Config`` object. This macro is frequently used because typically top-level IOs drive or are driven by only one source, so a composition of ``IOBinders`` does not make sense. The ``ComposeIOBinder`` macro provides the functionality of not overriding existing behaviors. +Similarly, for a given simulation platform and strategy, there may be multiple strategies for generating the chip IOs. This target-design configuration is isolated in the ``IOBinders``. diff --git a/generators/chipyard/src/main/scala/HarnessBinders.scala b/generators/chipyard/src/main/scala/HarnessBinders.scala index 8901717264..a26b51045f 100644 --- a/generators/chipyard/src/main/scala/HarnessBinders.scala +++ b/generators/chipyard/src/main/scala/HarnessBinders.scala @@ -66,12 +66,14 @@ class WithGPIOTiedOff extends OverrideHarnessBinder({ } }) +// DOC include start: WithUARTAdapter class WithUARTAdapter extends OverrideHarnessBinder({ (system: HasPeripheryUARTModuleImp, th: HasHarnessSignalReferences, ports: Seq[Data]) => { UARTAdapter.connect(ports.map(_.asInstanceOf[UARTPortIO]))(system.p) Nil } }) +// DOC include end: WithUARTAdapter class WithSimSPIFlashModel(rdOnly: Boolean = true) extends OverrideHarnessBinder({ (system: HasPeripherySPIFlashModuleImp, th: HasHarnessSignalReferences, ports: Seq[Data]) => { diff --git a/generators/chipyard/src/main/scala/IOBinders.scala b/generators/chipyard/src/main/scala/IOBinders.scala index acd5720747..db5c7008c6 100644 --- a/generators/chipyard/src/main/scala/IOBinders.scala +++ b/generators/chipyard/src/main/scala/IOBinders.scala @@ -40,13 +40,6 @@ import scala.reflect.{ClassTag} // You can add your own binder by adding a new (key, fn) pair, typically by using // the OverrideIOBinder or ComposeIOBinder macros - - -// DOC include start: IOBinders -// This type describes a function callable on the TestHarness instance. Its return type is unused. - - - case object IOBinders extends Field[Map[String, (Any) => (Seq[Data], Seq[IOCell])]]( Map[String, (Any) => (Seq[Data], Seq[IOCell])]().withDefaultValue((Any) => (Nil, Nil)) ) @@ -59,7 +52,6 @@ object ApplyIOBinders { } } - // Note: The parameters instance is accessible only through LazyModule // or LazyModuleImpLike. The self-type requirement in traits like // CanHaveMasterAXI4MemPort is insufficient to make it accessible to the IOBinder @@ -116,8 +108,6 @@ object BoreHelper { } } -// DOC include end: IOBinders - case object IOCellKey extends Field[IOCellTypeParams](GenericIOCellParams()) @@ -140,7 +130,7 @@ class WithGPIOCells extends OverrideIOBinder({ } }) - +// DOC include start: WithUARTIOCells class WithUARTIOCells extends OverrideIOBinder({ (system: HasPeripheryUARTModuleImp) => { val (ports, cells2d) = system.uart.zipWithIndex.map({ case (u, i) => @@ -151,6 +141,7 @@ class WithUARTIOCells extends OverrideIOBinder({ (ports, cells2d.flatten) } }) +// DOC include end: WithUARTIOCells class WithSPIIOCells extends OverrideIOBinder({ (system: HasPeripherySPIFlashModuleImp) => {