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

.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala
   :language: scala
   :start-after: DOC include start: IOBinders
   :end-before: DOC include end: IOBinders


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.

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

.. 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
   :start-after: DOC include start: WithSimAXIMem
   :end-before: DOC include end: WithSimAXIMem

These classes are all ``Config`` objects, which can be mixed into the configs to specify IO connection behaviors.

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.
