Skip to content

Commit

Permalink
Add optional group API
Browse files Browse the repository at this point in the history
Add support for optional groups.  These are groups of statements with
optional functionality.  The primary use case is intended for verification
code to "extract" such code into bound modules.

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
  • Loading branch information
seldridge committed Sep 27, 2023
1 parent 600218d commit d70540f
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 15 deletions.
61 changes: 61 additions & 0 deletions core/src/main/scala/chisel3/Group.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3

import chisel3.internal.{Builder, HasId}
import chisel3.internal.firrtl.{GroupDefBegin, GroupDefEnd, Node}
import chisel3.experimental.SourceInfo
import scala.collection.mutable.LinkedHashSet

object group {

object Convention {
sealed trait Type
private[chisel3] case object Root extends Type
case object Bind extends Type
}

// TODO: All this mutable state needs to be cleaned up and moved into the
// Builder. The naming needs to be handled more inteligently.
abstract class Declaration(val convention: Convention.Type)(implicit _parent: Declaration) { self: Singleton =>
protected implicit val thiz: Declaration = self

private[chisel3] def parent: Declaration = _parent

private[chisel3] def name: String = Utils.goodName(this)
}

object Declaration {
case object Root extends Declaration(Convention.Root)(null)
implicit val rootDeclaration: Declaration = Root
}

/** Add a declaration and all of its parents to the Builder. This lets the
* Builder know that this group was used and should be emitted in the FIRRTL.
*/
private[chisel3] def addDeclarations(declaration: Declaration) = {
var currentDeclaration: Declaration = declaration
while (currentDeclaration != Declaration.Root && !Builder.groups.contains(currentDeclaration)) {
val decl = currentDeclaration
val parent = decl.parent

Builder.groups += decl
currentDeclaration = parent
}
}

def apply[A](
declaration: Declaration
)(block: => A
)(
implicit sourceInfo: SourceInfo
): Unit = {
Builder.pushCommand(GroupDefBegin(sourceInfo, declaration))
addDeclarations(declaration)
Builder.groupStack = declaration :: Builder.groupStack
block
Builder.pushCommand(GroupDefEnd(sourceInfo))
Builder.groupStack = Builder.groupStack.tail
}

}
40 changes: 39 additions & 1 deletion core/src/main/scala/chisel3/internal/Builder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ private[chisel3] class DynamicContext(
val components = ArrayBuffer[Component]()
val annotations = ArrayBuffer[ChiselAnnotation]()
val newAnnotations = ArrayBuffer[ChiselMultiAnnotation]()
val groups = mutable.LinkedHashSet[group.Declaration]()
var currentModule: Option[BaseModule] = None

/** Contains a mapping from a elaborated module to their aspect
Expand All @@ -492,6 +493,7 @@ private[chisel3] class DynamicContext(
var currentClock: Option[Clock] = None
var currentReset: Option[Reset] = None
var currentDisable: Disable.Type = Disable.BeforeReset
var groupStack: List[group.Declaration] = Nil
val errors = new ErrorLog(warningFilters, sourceRoots, throwOnFirstError)
val namingStack = new NamingStack

Expand Down Expand Up @@ -551,6 +553,8 @@ private[chisel3] object Builder extends LazyLogging {
def components: ArrayBuffer[Component] = dynamicContext.components
def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations

def groups: mutable.LinkedHashSet[group.Declaration] = dynamicContext.groups

def contextCache: BuilderContextCache = dynamicContext.contextCache

// TODO : Unify this with annotations in the future - done this way for backward compatability
Expand Down Expand Up @@ -760,6 +764,11 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContext.currentDisable = newDisable
}

def groupStack: List[group.Declaration] = dynamicContext.groupStack
def groupStack_=(s: List[group.Declaration]): Unit = {
dynamicContext.groupStack = s
}

def inDefinition: Boolean = {
dynamicContextVar.value
.map(_.inDefinition)
Expand Down Expand Up @@ -949,14 +958,43 @@ private[chisel3] object Builder extends LazyLogging {
case _ => None
}.toSeq

/** Stores an adjacency list representation of groups. Connections indicating children. */
val groupAdjacencyList = mutable
.LinkedHashMap[group.Declaration, mutable.LinkedHashSet[group.Declaration]]()
.withDefault(_ => mutable.LinkedHashSet[group.Declaration]())

// Populate the adjacency list.
groups.foreach { group =>
groupAdjacencyList(group.parent) = groupAdjacencyList(group.parent) += group
}

/** For a `group.Declaration`, walk all its children and fold them into a
* `GroupDecl`. This "folding" creates one `GroupDecl` for each child
* nested under each parent `GroupDecl`.
*/
def foldGroupDecls(decl: group.Declaration): GroupDecl = {
val children = groupAdjacencyList(decl)
val convention = decl.convention match {
case group.Convention.Bind => GroupConvention.Bind
case _ => ???
}
decl match {
case leaf if children.isEmpty => GroupDecl(UnlocatableSourceInfo, leaf.name, convention, Seq.empty)
case node => GroupDecl(UnlocatableSourceInfo, node.name, convention, children.map(foldGroupDecls).toSeq)
}
}

val rootGroups = groupAdjacencyList(group.Declaration.Root)

(
Circuit(
components.last.name,
components.toSeq,
annotations.toSeq,
makeViewRenameMap,
newAnnotations.toSeq,
typeAliases
typeAliases,
rootGroups.map(foldGroupDecls).toSeq
),
mod
)
Expand Down
37 changes: 33 additions & 4 deletions core/src/main/scala/chisel3/internal/firrtl/Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ private[chisel3] object Converter {
case _ => None
}

/** Trait used for tracking when or group regions. */
private sealed trait RegionFrame

/** Internal datastructure to help translate Chisel's flat Command structure to FIRRTL's AST
*
* In particular, when scoping is translated from flat with begin end to a nested datastructure
Expand All @@ -241,6 +244,10 @@ private[chisel3] object Converter {
*/
// TODO we should probably have a different structure in the IR to close elses
private case class WhenFrame(when: fir.Conditionally, outer: VectorBuilder[fir.Statement], alt: Boolean)
extends RegionFrame

/** Internal datastructure to help convert optional groups to FIRRTL. */
private case class GroupFrame(group: fir.GroupDefine, outer: VectorBuilder[fir.Statement]) extends RegionFrame

/** Convert Chisel IR Commands into FIRRTL Statements
*
Expand All @@ -253,7 +260,7 @@ private[chisel3] object Converter {
*/
def convert(cmds: Seq[Command], ctx: Component, typeAliases: Seq[String]): fir.Statement = {
var stmts = new VectorBuilder[fir.Statement]()
var scope: List[WhenFrame] = Nil
var scope: List[RegionFrame] = Nil
var cmdsIt = cmds.iterator.buffered
// Extra var because sometimes we want to push a Command to the head of cmdsIt
// This is more efficient than changing the iterator
Expand All @@ -280,7 +287,7 @@ private[chisel3] object Converter {
stmts = new VectorBuilder[fir.Statement]
scope = frame :: scope
case WhenEnd(info, depth, _) =>
val frame = scope.head
val frame = scope.head.asInstanceOf[WhenFrame]
val when =
if (frame.alt) frame.when.copy(alt = fir.Block(stmts.result()))
else frame.when.copy(conseq = fir.Block(stmts.result()))
Expand All @@ -303,7 +310,7 @@ private[chisel3] object Converter {
scope = scope.tail
}
case OtherwiseEnd(info, depth) =>
val frame = scope.head
val frame = scope.head.asInstanceOf[WhenFrame]
val when = frame.when.copy(alt = fir.Block(stmts.result()))
// TODO For some reason depth == 1 indicates the last closing otherwise whereas
// depth == 0 indicates last closing when
Expand All @@ -313,6 +320,17 @@ private[chisel3] object Converter {
stmts = frame.outer
stmts += when
scope = scope.tail
case GroupDefBegin(info, declaration) =>
val groupDefine = fir.GroupDefine(convert(info), declaration.name, fir.EmptyStmt)
val frame = GroupFrame(groupDefine, stmts)
stmts = new VectorBuilder[fir.Statement]
scope = frame :: scope
case GroupDefEnd(info) =>
val frame = scope.head.asInstanceOf[GroupFrame]
val groupDefine = frame.group.copy(body = fir.Block(stmts.result()))
stmts = frame.outer
stmts += groupDefine
scope = scope.tail
}
}
}
Expand Down Expand Up @@ -454,13 +472,24 @@ private[chisel3] object Converter {
)
}

def rec(decl: GroupDecl): fir.GroupDeclare = {
val convention = decl.convention match {
case GroupConvention.Bind => fir.GroupConvention.Bind
}
decl match {
case leaf if leaf.children.isEmpty => fir.GroupDeclare(convert(leaf.sourceInfo), leaf.name, convention, Seq.empty)
case node => fir.GroupDeclare(convert(node.sourceInfo), node.name, convention, node.children.map(rec))
}
}

def convert(circuit: Circuit): fir.Circuit = {
val typeAliases: Seq[String] = circuit.typeAliases.map(_.name)
fir.Circuit(
fir.NoInfo,
circuit.components.map(c => convert(c, typeAliases)),
circuit.name,
circuit.typeAliases.map(ta => fir.DefTypeAlias(convert(ta.sourceInfo), ta.name, ta.underlying))
circuit.typeAliases.map(ta => fir.DefTypeAlias(convert(ta.sourceInfo), ta.name, ta.underlying)),
circuit.groups.map(rec)
)
}

Expand Down
37 changes: 28 additions & 9 deletions core/src/main/scala/chisel3/internal/firrtl/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,21 @@ case class Attach(sourceInfo: SourceInfo, locs: Seq[Node]) extends Command
case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command
@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
case class Stop(id: stop.Stop, sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Definition

private[chisel3] object GroupConvention {
sealed trait Type
case object Bind extends Type
}

private[chisel3] case class GroupDecl(
sourceInfo: SourceInfo,
name: String,
convention: GroupConvention.Type,
children: Seq[GroupDecl])

private[chisel3] case class GroupDefBegin(sourceInfo: SourceInfo, declaration: group.Declaration) extends Command
private[chisel3] case class GroupDefEnd(sourceInfo: SourceInfo) extends Command

// Note this is just deprecated which will cause deprecation warnings, use @nowarn
@deprecated(
"This API should never have been public, for Module port reflection, use DataMirror.modulePorts",
Expand Down Expand Up @@ -452,16 +467,18 @@ case class Circuit(
"Chisel 3.5")

newAnnotations: Seq[ChiselMultiAnnotation],
typeAliases: Seq[DefTypeAlias]) {
typeAliases: Seq[DefTypeAlias],
groups: Seq[GroupDecl]) {

def this(
name: String,
components: Seq[Component],
annotations: Seq[ChiselAnnotation],
renames: RenameMap,
typeAliases: Seq[DefTypeAlias]
typeAliases: Seq[DefTypeAlias],
groups: Seq[GroupDecl]
) =
this(name, components, annotations, renames, Seq.empty, typeAliases)
this(name, components, annotations, renames, Seq.empty, typeAliases, groups)

def firrtlAnnotations: Iterable[Annotation] =
annotations.flatMap(_.toFirrtl.update(renames)) ++ newAnnotations.flatMap(
Expand All @@ -473,16 +490,17 @@ case class Circuit(
components: Seq[Component] = components,
annotations: Seq[ChiselAnnotation] = annotations,
renames: RenameMap = renames,
typeAliases: Seq[DefTypeAlias] = typeAliases
) = Circuit(name, components, annotations, renames, newAnnotations, typeAliases)
typeAliases: Seq[DefTypeAlias] = typeAliases,
groups: Seq[GroupDecl] = groups
) = Circuit(name, components, annotations, renames, newAnnotations, typeAliases, groups)

}

@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
object Circuit
extends scala.runtime.AbstractFunction5[String, Seq[Component], Seq[ChiselAnnotation], RenameMap, Seq[
extends scala.runtime.AbstractFunction6[String, Seq[Component], Seq[ChiselAnnotation], RenameMap, Seq[
DefTypeAlias
], Circuit] {
], Seq[GroupDecl], Circuit] {
def unapply(c: Circuit): Option[(String, Seq[Component], Seq[ChiselAnnotation], RenameMap, Seq[DefTypeAlias])] = {
Some((c.name, c.components, c.annotations, c.renames, c.typeAliases))
}
Expand All @@ -492,7 +510,8 @@ object Circuit
components: Seq[Component],
annotations: Seq[ChiselAnnotation],
renames: RenameMap,
typeAliases: Seq[DefTypeAlias] = Seq.empty
typeAliases: Seq[DefTypeAlias] = Seq.empty,
groups: Seq[GroupDecl] = Seq.empty
): Circuit =
new Circuit(name, components, annotations, renames, typeAliases)
new Circuit(name, components, annotations, renames, typeAliases, groups)
}
20 changes: 19 additions & 1 deletion firrtl/src/main/scala/firrtl/ir/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,19 @@ case class ProbeRelease(info: Info, clock: Expression, cond: Expression, probe:
extends Statement
with UseSerializer

object GroupConvention {
sealed trait Type
case object Bind extends Type {
override def toString: String = "bind"
}
}

case class GroupDeclare(info: Info, name: String, convention: GroupConvention.Type, body: Seq[GroupDeclare])
extends FirrtlNode
with IsDeclaration
with UseSerializer
case class GroupDefine(info: Info, declaration: String, body: Statement) extends Statement with UseSerializer

// formal
object Formal extends Enumeration {
val Assert = Value("assert")
Expand Down Expand Up @@ -650,7 +663,12 @@ case class IntModule(
*/
case class DefClass(info: Info, name: String, ports: Seq[Port], body: Statement) extends DefModule with UseSerializer

case class Circuit(info: Info, modules: Seq[DefModule], main: String, typeAliases: Seq[DefTypeAlias] = Seq.empty)
case class Circuit(
info: Info,
modules: Seq[DefModule],
main: String,
typeAliases: Seq[DefTypeAlias] = Seq.empty,
groups: Seq[GroupDeclare] = Seq.empty)
extends FirrtlNode
with HasInfo
with UseSerializer
Expand Down
22 changes: 22 additions & 0 deletions firrtl/src/main/scala/firrtl/ir/Serializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ object Serializer {
private case object AltBegin extends PseudoStatement
private case object WhenEnd extends PseudoStatement

private case class GroupDefineBegin(info: Info, declaration: String) extends PseudoStatement
private case object GroupDefineEnd extends PseudoStatement

// This does not extend Iterator[Statement] because
// 1. It is extended by StmtsSerializer which extends Iterator[String]
// 2. Flattening out whens introduces fake Statements needed for [un]indenting
Expand Down Expand Up @@ -206,6 +209,9 @@ object Serializer {
}
val last = underlying
underlying = stmts ++ last
case GroupDefine(info, declaration, body) =>
val begin = GroupDefineBegin(info, declaration)
underlying = Iterator(begin, body, GroupDefineEnd)
case other =>
next = other
}
Expand Down Expand Up @@ -246,6 +252,12 @@ object Serializer {
indent += 1
case WhenEnd =>
indent -= 1
case GroupDefineBegin(info, declaration) =>
doIndent()
b ++= s"group $declaration :"
indent += 1
case GroupDefineEnd =>
indent -= 1
case other =>
doIndent()
s(other)
Expand Down Expand Up @@ -513,8 +525,18 @@ object Serializer {
b ++= s"${NewLine}"
Iterator(b.toString)
} else Iterator.empty
val groups = if (circuit.groups.nonEmpty) {
implicit val b = new StringBuilder
def groupIt(groupDecl: GroupDeclare)(implicit indent: Int): Unit = {
b ++= s"${NewLine}"; doIndent(); b ++= s"declgroup ${groupDecl.name}, ${groupDecl.convention} :"
groupDecl.body.foreach(groupIt(_)(indent + 1))
}
circuit.groups.foreach(groupIt(_)(1))
Iterator(b.toString)
} else Iterator.empty
prelude ++
typeAliases ++
groups ++
circuit.modules.iterator.zipWithIndex.flatMap {
case (m, i) =>
val newline = Iterator(if (i == 0) s"$NewLine" else s"${NewLine}${NewLine}")
Expand Down
Loading

0 comments on commit d70540f

Please sign in to comment.