BoringUtils
Utilities for generating synthesizable cross module references that "bore" through the hierarchy. The underlying cross module connects are handled by FIRRTL's Wiring Transform.
Consider the following example where you want to connect a component in one module to a component in another. Module Constant has a wire tied to 42 and Expect will assert unless connected to 42:
class Constant extends Module {
val io = IO(new Bundle{})
val x = Wire(UInt(6.W))
x := 42.U
}
class Expect extends Module {
val io = IO(new Bundle{})
val y = Wire(UInt(6.W))
y := 0.U
// This assertion will fail unless we bore!
chisel3.assert(y === 42.U, "y should be 42 in module Expect")
}
We can then connect x to y using BoringUtils without modifiying the Chisel IO of Constant, Expect, or modules that may instantiate them. There are two approaches to do this:
-
Hierarchical boring using BoringUtils.bore
-
Non-hierarchical boring using BoringUtils.addSink/BoringUtils.addSource
===Hierarchical Boring===
Hierarchical boring involves connecting one sink instance to another source instance in a parent module. Below, module Top contains an instance of Constant and Expect. Using BoringUtils.bore, we can connect constant.x to expect.y.
class Top extends Module {
val io = IO(new Bundle{})
val constant = Module(new Constant)
val expect = Module(new Expect)
BoringUtils.bore(constant.x, Seq(expect.y))
}
Bottom-up boring involves boring a sink in a child instance to the current module, where it can be assigned from. Using BoringUtils.bore, we can connect from constant.x to mywire.
class Top extends Module {
val io = IO(new Bundle { val foo = UInt(3.W) })
val constant = Module(new Constant)
io.foo := BoringUtils.bore(constant.x)
}
===Non-hierarchical Boring===
Non-hierarchical boring involves connections from sources to sinks that cannot see each other. Here, x is described as a source and given a name, uniqueId, and y is described as a sink with the same name. This is equivalent to the hierarchical boring example above, but requires no modifications to Top.
class Constant extends Module {
val io = IO(new Bundle{})
val x = Wire(UInt(6.W))
x := 42.U
BoringUtils.addSource(x, "uniqueId")
}
class Expect extends Module {
val io = IO(new Bundle{})
val y = Wire(UInt(6.W))
y := 0.U
// This assertion will fail unless we bore!
chisel3.assert(y === 42.U, "y should be 42 in module Expect")
BoringUtils.addSink(y, "uniqueId")
}
class Top extends Module {
val io = IO(new Bundle{})
val constant = Module(new Constant)
val expect = Module(new Expect)
}
==Comments==
Both hierarchical and non-hierarchical boring emit FIRRTL annotations that describe sources and sinks. These are matched by a name key that indicates they should be wired together. Hierarchical boring safely generates this name automatically. Non-hierarchical boring unsafely relies on user input to generate this name. Use of non-hierarchical naming may result in naming conflicts that the user must handle.
The automatic generation of hierarchical names relies on a global, mutable namespace. This is currently persistent across circuit elaborations.
Attributes
- Source
- BoringUtils.scala
- Graph
-
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
BoringUtils.type