Skip to content

Commit 32a6f6f

Browse files
seldridgechiselbot
authored andcommitted
[core] Add knownLayers API to BlackBox, ExtModule (#4983)
Add an API to add `knownlayers` specifications to FIRRTL `extmodule`s. This is a mechanism to declare that an external module was compiled with support for specific layers. This then allows a FIRRTL comopiler compiler to know that it should automatically enable these layers if needed. In order to make this work, a number of other changes had to be made. First, move `atModuleBodyEnd` from `RawModule` to `BaseModule`. This allows capturing of the `Builder` state when the module closes. Here, this is used to capture the layers that are known to the `Builder` if a module is later converted to a `Definition`. Second, the default layers are now added to the `Builder` when it starts as opposed to when it ends. This allows for the default layers to be captured by module or external modules and then emitted correctly if needed (in the `Definition` or as FIRRTL `knownlayers`). Third, when an `Instance` is created, any layers that it has need to be added to the `Builder`. This can arise if a `Definition` is imported via an annotation and used to create a blackbox which will define additional `knownlayers`. If these layers are not added to the FIRRTL circuit, then this is illegal FIRRTL. Signed-off-by: Schuyler Eldridge <[email protected]>
1 parent 887d6bd commit 32a6f6f

File tree

22 files changed

+346
-93
lines changed

22 files changed

+346
-93
lines changed

core/src/main/scala/chisel3/BlackBox.scala

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
package chisel3
44

55
import chisel3.experimental.{BaseModule, Param}
6+
import chisel3.layer.Layer
67
import chisel3.internal.BaseBlackBox
78
import chisel3.internal.Builder.pushCommand
89
import chisel3.internal.firrtl.ir._
910
import chisel3.internal.throwException
1011
import chisel3.experimental.{SourceInfo, UnlocatableSourceInfo}
1112

13+
import scala.collection.mutable
14+
1215
package internal {
1316

1417
private[chisel3] abstract class BaseBlackBox extends BaseModule {
@@ -17,6 +20,45 @@ package internal {
1720
// is not possible to properly name EmptyExtModule created from
1821
// Defintions. See unit test SeparateElaborationSpec #4.a
1922
private[chisel3] def _isImportedDefinition: Boolean = false
23+
24+
/** User-provided information about what layers are known to this `BlackBox`.
25+
*
26+
* E.g., if this was a [[BlackBox]] that points at Verilog built from
27+
* _another_ Chisel elaboration, then this would be the layers that were
28+
* defined in that circuit.
29+
*
30+
* @note This will cause the emitted FIRRTL to include the `knownlayer`
31+
* keyword on the `extmodule` declaration.
32+
*/
33+
protected def knownLayers: Seq[Layer]
34+
35+
// Internal tracking of _knownLayers. This can be appended to with
36+
// `addKnownLayer` which happens if you use `addLayer` inside an external
37+
// module.
38+
private val _knownLayers: mutable.LinkedHashSet[Layer] = mutable.LinkedHashSet.empty[Layer]
39+
40+
/** Add a layer to list of knownLayers for this module. */
41+
private[chisel3] def addKnownLayer(layer: Layer) = {
42+
var currentLayer: Layer = layer
43+
while (currentLayer != Layer.Root && !_knownLayers.contains(currentLayer)) {
44+
val layer = currentLayer
45+
val parent = layer.parent
46+
47+
_knownLayers += layer
48+
currentLayer = parent
49+
}
50+
}
51+
52+
/** Get the known layers.
53+
*
54+
* @throw IllegalArgumentException if the module is not closed
55+
*/
56+
private[chisel3] def getKnownLayers: Seq[Layer] = {
57+
require(isClosed, "Can't get layers before module is closed")
58+
_knownLayers.toSeq
59+
}
60+
61+
knownLayers.foreach(layer.addLayer)
2062
}
2163
}
2264

@@ -70,9 +112,15 @@ package experimental {
70112
* }}}
71113
* @note The parameters API is experimental and may change
72114
*/
73-
abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox {
115+
abstract class ExtModule(
116+
val params: Map[String, Param] = Map.empty[String, Param],
117+
override protected final val knownLayers: Seq[Layer] = Seq.empty[Layer]
118+
) extends BaseBlackBox {
74119
private[chisel3] override def generateComponent(): Option[Component] = {
75120
require(!_closed, "Can't generate module more than once")
121+
122+
evaluateAtModuleBodyEnd()
123+
76124
_closed = true
77125

78126
// Ports are named in the same way as regular Modules
@@ -81,7 +129,7 @@ package experimental {
81129
val firrtlPorts = getModulePorts.map { case port =>
82130
Port(port, port.specifiedDirection, UnlocatableSourceInfo)
83131
}
84-
val component = DefBlackBox(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params)
132+
val component = DefBlackBox(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params, getKnownLayers)
85133
_component = Some(component)
86134
_component
87135
}
@@ -127,7 +175,10 @@ package experimental {
127175
* }}}
128176
* @note The parameters API is experimental and may change
129177
*/
130-
abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox {
178+
abstract class BlackBox(
179+
val params: Map[String, Param] = Map.empty[String, Param],
180+
override protected final val knownLayers: Seq[Layer] = Seq.empty[Layer]
181+
) extends BaseBlackBox {
131182

132183
// Find a Record port named "io" for purposes of stripping the prefix
133184
private[chisel3] lazy val _io: Option[Record] =
@@ -147,6 +198,9 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param
147198
require(portsSize == 1, "BlackBox must only have one IO, called `io`")
148199

149200
require(!_closed, "Can't generate module more than once")
201+
202+
evaluateAtModuleBodyEnd()
203+
150204
_closed = true
151205

152206
val io = _io.get
@@ -166,7 +220,7 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param
166220
val firrtlPorts = namedPorts.map { namedPort =>
167221
Port(namedPort._2, namedPort._2.specifiedDirection, UnlocatableSourceInfo)
168222
}
169-
val component = DefBlackBox(this, name, firrtlPorts, io.specifiedDirection, params)
223+
val component = DefBlackBox(this, name, firrtlPorts, io.specifiedDirection, params, getKnownLayers)
170224
_component = Some(component)
171225
_component
172226
}

core/src/main/scala/chisel3/Layer.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ object layer {
196196
Builder.layers += layer
197197
currentLayer = parent
198198
}
199+
200+
// If this API is used in a BlackBox, then modify it's `knownLayers` member.
201+
Builder.currentModule.map {
202+
case module: internal.BaseBlackBox => module.addKnownLayer(layer)
203+
case _ =>
204+
}
199205
}
200206

201207
/** A type class that describes how to post-process the return value from a layer block. */

core/src/main/scala/chisel3/Module.scala

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import chisel3.internal._
1010
import chisel3.internal.binding._
1111
import chisel3.internal.Builder._
1212
import chisel3.internal.firrtl.ir._
13+
import chisel3.layer.Layer
1314
import chisel3.experimental.{requireIsChiselType, BaseModule, SourceInfo, Targetable, UnlocatableSourceInfo}
1415
import chisel3.properties.{Class, Property}
1516
import chisel3.reflect.DataMirror
@@ -953,6 +954,90 @@ package experimental {
953954
localModulePrefix.foreach { prefix =>
954955
Builder.pushModulePrefix(prefix, localModulePrefixUseSeparator)
955956
}
957+
958+
/** Hook to invoke hardware generators after the rest of the Module is constructed.
959+
*
960+
* This is a power-user API, and should not normally be needed.
961+
*
962+
* In rare cases, it is necessary to run hardware generators at a late stage, but still within the scope of the
963+
* Module. In these situations, atModuleBodyEnd may be used to register such generators. For example:
964+
*
965+
* {{{
966+
* class Example extends RawModule {
967+
* atModuleBodyEnd {
968+
* val extraPort0 = IO(Output(Bool()))
969+
* extraPort0 := 0.B
970+
* }
971+
* }
972+
* }}}
973+
*
974+
* Any generators registered with atModuleBodyEnd are the last code to execute when the Module is constructed. The
975+
* execution order is:
976+
*
977+
* - The constructors of any super classes or traits the Module extends
978+
* - The constructor of the Module itself
979+
* - The atModuleBodyEnd generators
980+
*
981+
* The atModuleBodyEnd generators execute in the lexical order they appear in the Module constructor.
982+
*
983+
* For example:
984+
*
985+
* {{{
986+
* trait Parent {
987+
* // Executes first.
988+
* val foo = ...
989+
* }
990+
*
991+
* class Example extends Parent {
992+
* // Executes second.
993+
* val bar = ...
994+
*
995+
* atModuleBodyEnd {
996+
* // Executes fourth.
997+
* val qux = ...
998+
* }
999+
*
1000+
* atModuleBodyEnd {
1001+
* // Executes fifth.
1002+
* val quux = ...
1003+
* }
1004+
*
1005+
* // Executes third..
1006+
* val baz = ...
1007+
* }
1008+
* }}}
1009+
*
1010+
* If atModuleBodyEnd is used in a Definition, any generated hardware will be included in the Definition. However, it
1011+
* is currently not possible to annotate any val within atModuleBodyEnd as @public.
1012+
*/
1013+
protected def atModuleBodyEnd(gen: => Unit): Unit = {
1014+
_atModuleBodyEnd += { () => gen }
1015+
}
1016+
private val _atModuleBodyEnd = new ArrayBuffer[() => Unit]
1017+
1018+
protected[chisel3] def evaluateAtModuleBodyEnd(): Unit = _atModuleBodyEnd.foreach(_())
1019+
1020+
/** Record the layers in the circuit when this module was created. */
1021+
private var _layers: Seq[Layer] = null
1022+
1023+
atModuleBodyEnd {
1024+
_layers = Builder.layers.toSeq
1025+
}
1026+
1027+
/** Return the layers for this module after.
1028+
*
1029+
* This requires that the module is closed.
1030+
*/
1031+
private[chisel3] def layers: Seq[Layer] = {
1032+
require(isClosed, "Can't get layers before module is closed")
1033+
if (_layers == null) {
1034+
throw new InternalErrorException(
1035+
s"a closed BaseModule '$desiredName' has null '_layers': this should be impossible"
1036+
)
1037+
}
1038+
_layers
1039+
}
1040+
9561041
}
9571042
}
9581043

core/src/main/scala/chisel3/RawModule.scala

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,66 +22,6 @@ import scala.collection.mutable.ArrayBuffer
2222
*/
2323
abstract class RawModule extends BaseModule {
2424

25-
/** Hook to invoke hardware generators after the rest of the Module is constructed.
26-
*
27-
* This is a power-user API, and should not normally be needed.
28-
*
29-
* In rare cases, it is necessary to run hardware generators at a late stage, but still within the scope of the
30-
* Module. In these situations, atModuleBodyEnd may be used to register such generators. For example:
31-
*
32-
* {{{
33-
* class Example extends RawModule {
34-
* atModuleBodyEnd {
35-
* val extraPort0 = IO(Output(Bool()))
36-
* extraPort0 := 0.B
37-
* }
38-
* }
39-
* }}}
40-
*
41-
* Any generators registered with atModuleBodyEnd are the last code to execute when the Module is constructed. The
42-
* execution order is:
43-
*
44-
* - The constructors of any super classes or traits the Module extends
45-
* - The constructor of the Module itself
46-
* - The atModuleBodyEnd generators
47-
*
48-
* The atModuleBodyEnd generators execute in the lexical order they appear in the Module constructor.
49-
*
50-
* For example:
51-
*
52-
* {{{
53-
* trait Parent {
54-
* // Executes first.
55-
* val foo = ...
56-
* }
57-
*
58-
* class Example extends Parent {
59-
* // Executes second.
60-
* val bar = ...
61-
*
62-
* atModuleBodyEnd {
63-
* // Executes fourth.
64-
* val qux = ...
65-
* }
66-
*
67-
* atModuleBodyEnd {
68-
* // Executes fifth.
69-
* val quux = ...
70-
* }
71-
*
72-
* // Executes third..
73-
* val baz = ...
74-
* }
75-
* }}}
76-
*
77-
* If atModuleBodyEnd is used in a Definition, any generated hardware will be included in the Definition. However, it
78-
* is currently not possible to annotate any val within atModuleBodyEnd as @public.
79-
*/
80-
protected def atModuleBodyEnd(gen: => Unit): Unit = {
81-
_atModuleBodyEnd += { () => gen }
82-
}
83-
private val _atModuleBodyEnd = new ArrayBuffer[() => Unit]
84-
8525
/** Hook to invoke hardware generators after a Module has been constructed and closed.
8626
*
8727
* This is useful for running hardware generators after a Module's constructor has run and its Definition is available, while still having access to arguments and definitions in the constructor. The Module itself can no longer be modified at this point.
@@ -223,9 +163,7 @@ abstract class RawModule extends BaseModule {
223163
}
224164

225165
// Evaluate any atModuleBodyEnd generators.
226-
_atModuleBodyEnd.foreach { gen =>
227-
gen()
228-
}
166+
evaluateAtModuleBodyEnd()
229167

230168
_closed = true
231169

core/src/main/scala/chisel3/experimental/IntrinsicModule.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import chisel3.SpecifiedDirection
66
import chisel3.experimental.{BaseModule, Param}
77
import chisel3.internal.Builder.pushCommand
88
import chisel3.internal.firrtl.ir._
9+
import chisel3.layer.Layer
910

1011
private[chisel3] abstract class BaseIntrinsicModule(intrinsicName: String) extends BaseModule {
1112
val intrinsic = intrinsicName
@@ -16,6 +17,7 @@ abstract class IntrinsicModule(intrinsicName: String, val params: Map[String, Pa
1617
extends BaseIntrinsicModule(intrinsicName) {
1718
private[chisel3] override def generateComponent(): Option[Component] = {
1819
require(!_closed, "Can't generate intmodule more than once")
20+
evaluateAtModuleBodyEnd()
1921
_closed = true
2022

2123
// Ports are named in the same way as regular Modules

core/src/main/scala/chisel3/experimental/hierarchy/DefinitionClone.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package chisel3.experimental.hierarchy
55
import chisel3.experimental.BaseModule
66
import chisel3.internal.{HasId, PseudoModule}
77
import chisel3.internal.firrtl.ir.{Component, Ref}
8+
import chisel3.layer.Layer
89

910
/** Represents a Definition root module, when accessing something from a definition
1011
*
@@ -25,4 +26,5 @@ private[chisel3] class DefinitionClone[T <: BaseModule](val getProto: T) extends
2526
private[chisel3] def initializeInParent(): Unit = ()
2627
// Module name is the same as proto's module name
2728
override def desiredName: String = getProto.name
29+
override def layers: Seq[Layer] = getProto.layers
2830
}

core/src/main/scala/chisel3/experimental/hierarchy/InstanceClone.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package chisel3.experimental.hierarchy
55
import chisel3.experimental.BaseModule
66
import chisel3.internal.PseudoModule
77
import chisel3.internal.firrtl.ir.{Component, Ref}
8+
import chisel3.layer.Layer
89

910
/** Represents a module viewed from a different instance context.
1011
*
@@ -30,4 +31,5 @@ private[chisel3] final class InstanceClone[T <: BaseModule](val getProto: T, val
3031
override def instanceName = instName()
3132
// Module name is the same as proto's module name
3233
override def desiredName: String = getProto.name
34+
override def layers: Seq[Layer] = getProto.layers
3335
}

core/src/main/scala/chisel3/experimental/hierarchy/ModuleClone.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import chisel3.experimental.{BaseModule, SourceInfo}
66
import chisel3.internal.{HasId, PseudoModule}
77
import chisel3.internal.firrtl.ir.{Component, ModuleCloneIO, Ref}
88
import chisel3.internal.{throwException, Namespace}
9+
import chisel3.layer.Layer
910
import chisel3._
1011

1112
// Private internal class to serve as a _parent for Data in cloned ports
@@ -26,6 +27,7 @@ private[chisel3] class ModuleClone[T <: BaseModule](val getProto: T)(implicit si
2627
// Don't generate a component, but point to the one for the cloned Module
2728
private[chisel3] def generateComponent(): Option[Component] = {
2829
require(!_closed, "Can't generate module more than once")
30+
evaluateAtModuleBodyEnd()
2931
_closed = true
3032
_component = getProto._component
3133
None
@@ -77,4 +79,6 @@ private[chisel3] class ModuleClone[T <: BaseModule](val getProto: T)(implicit si
7779
_portsRecord.suggestName(seed)
7880
this
7981
}
82+
83+
override def layers: Seq[Layer] = getProto.layers
8084
}

0 commit comments

Comments
 (0)