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

case object IOBinders extends Field[Map[String, (Clock, Bool, Bool, Any) => Seq[Any]]](
  Map[String, (Clock, Bool, Bool, Any) => Seq[Any]]().withDefaultValue((c: Clock, r: Bool, s: Bool, t: Any) => Nil)
)

// This macro overrides previous matches on some Top mixin. This is useful for
// binders which drive IO, since those typically cannot be composed
class OverrideIOBinder[T](fn: => (Clock, Bool, Bool, T) => Seq[Any])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
  case IOBinders => up(IOBinders, site) + (tag.runtimeClass.toString ->
      ((clock: Clock, reset: Bool, success: Bool, t: Any) => {
        t match {
          case top: T => fn(clock, reset, success, top)
          case _ => Nil
        }
      })
  )
})

// This macro composes with previous matches on some Top mixin. This is useful for
// annotation-like binders, since those can typically be composed
class ComposeIOBinder[T](fn: => (Clock, Bool, Bool, T) => Seq[Any])(implicit tag: ClassTag[T]) extends Config((site, here, up) => {
  case IOBinders => up(IOBinders, site) + (tag.runtimeClass.toString ->
      ((clock: Clock, reset: Bool, success: Bool, t: Any) => {
        t match {
          case top: T => (up(IOBinders, site)(tag.runtimeClass.toString)(clock, reset, success, top)
            ++ fn(clock, reset, success, top))
          case _ => Nil
        }
      })
  )
})

This special key solves the problem of duplicating test-harnesses for each different Top 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.

For example, the WithSimAXIMemTiedOff IOBinder specifies that any Top which matches CanHaveMasterAXI4MemPortModuleImp will have a SimAXIMem connected.

class WithSimAXIMem extends OverrideIOBinder({
  (c, r, s, top: CanHaveMasterAXI4MemPortModuleImp) => top.connectSimAXIMem(); Nil
})

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.