summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt29
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala30
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Module.scala6
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala12
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala4
-rw-r--r--src/main/scala/chisel3/Driver.scala12
-rw-r--r--src/main/scala/chisel3/package.scala3
-rw-r--r--src/test/scala/chiselTests/AnnotatingDiamondSpec.scala164
-rw-r--r--src/test/scala/chiselTests/AnnotatingExample.scala145
9 files changed, 238 insertions, 167 deletions
diff --git a/build.sbt b/build.sbt
index 2ce1f523..cb3b9d7e 100644
--- a/build.sbt
+++ b/build.sbt
@@ -13,17 +13,28 @@ lazy val customUnidocSettings = unidocSettings ++ Seq (
target in unidoc in ScalaUnidoc := crossTarget.value / "api"
)
+val defaultVersions = Map("firrtl" -> "1.1-SNAPSHOT")
+
lazy val commonSettings = Seq (
organization := "edu.berkeley.cs",
version := "3.1-SNAPSHOT",
git.remoteRepo := "git@github.com:ucb-bar/chisel3.git",
autoAPIMappings := true,
scalaVersion := "2.11.7",
- scalacOptions := Seq("-deprecation", "-feature")
+ scalacOptions := Seq("-deprecation", "-feature"),
+ // Since we want to examine the classpath to determine if a dependency on firrtl is required,
+ // this has to be a Task setting.
+ // Fortunately, allDependencies is a Task Setting, so we can modify that.
+ allDependencies := {
+ allDependencies.value ++ Seq("firrtl").collect {
+ // If we have an unmanaged jar file on the classpath, assume we're to use that,
+ case dep: String if !(unmanagedClasspath in Compile).value.toString.contains(s"$dep.jar") =>
+ // otherwise let sbt fetch the appropriate version.
+ "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
+ }
+ }
)
-val defaultVersions = Map("firrtl" -> "1.1-SNAPSHOT")
-
lazy val chiselSettings = Seq (
name := "chisel3",
@@ -72,18 +83,6 @@ lazy val chiselSettings = Seq (
"com.github.scopt" %% "scopt" % "3.4.0"
),
- // Since we want to examine the classpath to determine if a dependency on firrtl is required,
- // this has to be a Task setting.
- // Fortunately, allDependencies is a Task Setting, so we can modify that.
- allDependencies := {
- allDependencies.value ++ Seq("firrtl").collect {
- // If we have an unmanaged jar file on the classpath, assume we're to use that,
- case dep: String if !(unmanagedClasspath in Compile).value.toString.contains(s"$dep.jar") =>
- // otherwise let sbt fetch the appropriate version.
- "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
- }
- },
-
// Tests from other projects may still run concurrently.
parallelExecution in Test := true,
diff --git a/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala b/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala
new file mode 100644
index 00000000..73573bb1
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala
@@ -0,0 +1,30 @@
+// See LICENSE for license details.
+
+package chisel3.core
+
+import chisel3.internal.InstanceId
+import firrtl.Transform
+import firrtl.annotations.{Annotation, CircuitName, ComponentName, ModuleName}
+
+/**
+ * This is a stand-in for the firrtl.Annotations.Annotation because at the time this annotation
+ * is created the component cannot be resolved, into a targetString. Resolution can only
+ * happen after the circuit is elaborated
+ * @param component A chisel thingy to be annotated, could be module, wire, reg, etc.
+ * @param transformClass A fully-qualified class name of the transformation pass
+ * @param value A string value to be used by the transformation pass
+ */
+case class ChiselAnnotation(component: InstanceId, transformClass: Class[_ <: Transform], value: String) {
+ def toFirrtl: Annotation = {
+ val circuitName = CircuitName(component.pathName.split("""\.""").head)
+ component match {
+ case m: Module =>
+ Annotation(
+ ModuleName(m.name, circuitName), transformClass, value)
+ case _ =>
+ Annotation(
+ ComponentName(
+ component.instanceName, ModuleName(component.parentModName, circuitName)), transformClass, value)
+ }
+ }
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala
index bd406529..76a3b240 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala
@@ -14,7 +14,7 @@ object Module {
/** A wrapper method that all Module instantiations must be wrapped in
* (necessary to help Chisel track internal state).
*
- * @param m the Module being created
+ * @param bc the Module being created
*
* @return the input module `m` with Chisel metadata properly set
*/
@@ -85,6 +85,10 @@ extends HasId {
iodef
}
+ def annotate(annotation: ChiselAnnotation): Unit = {
+ Builder.annotations += annotation
+ }
+
private[core] var ioDefined: Boolean = false
/**
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
index cf86b0e7..7a77763b 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
@@ -108,22 +108,22 @@ private[chisel3] trait HasId extends InstanceId {
private[chisel3] def getRef: Arg = _ref.get
// Implementation of public methods.
- def instanceName = _parent match {
+ def instanceName: String = _parent match {
case Some(p) => p._component match {
case Some(c) => getRef fullName c
case None => throwException("signalName/pathName should be called after circuit elaboration")
}
case None => throwException("this cannot happen")
}
- def pathName = _parent match {
+ def pathName: String = _parent match {
case None => instanceName
case Some(p) => s"${p.pathName}.$instanceName"
}
- def parentPathName = _parent match {
+ def parentPathName: String = _parent match {
case Some(p) => p.pathName
case None => throwException(s"$instanceName doesn't have a parent")
}
- def parentModName = _parent match {
+ def parentModName: String = _parent match {
case Some(p) => p.name
case None => throwException(s"$instanceName doesn't have a parent")
}
@@ -145,6 +145,7 @@ private[chisel3] class DynamicContext() {
val idGen = new IdGen
val globalNamespace = new Namespace(None, Set())
val components = ArrayBuffer[Component]()
+ val annotations = ArrayBuffer[ChiselAnnotation]()
var currentModule: Option[Module] = None
// Set by object Module.apply before calling class Module constructor
// Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping
@@ -161,6 +162,7 @@ private[chisel3] object Builder {
def idGen: IdGen = dynamicContext.idGen
def globalNamespace: Namespace = dynamicContext.globalNamespace
def components: ArrayBuffer[Component] = dynamicContext.components
+ def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
def currentModule: Option[Module] = dynamicContext.currentModule
def currentModule_=(target: Option[Module]): Unit = {
@@ -206,7 +208,7 @@ private[chisel3] object Builder {
errors.checkpoint()
errors.info("Done elaborating.")
- Circuit(components.last.name, components)
+ Circuit(components.last.name, components, annotations.map(_.toFirrtl))
}
}
}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
index 699cc13c..50400034 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -7,6 +7,8 @@ import core._
import chisel3.internal._
import chisel3.internal.sourceinfo.{SourceInfo, NoSourceInfo}
+import _root_.firrtl.annotations.Annotation
+
case class PrimOp(val name: String) {
override def toString: String = name
}
@@ -273,4 +275,4 @@ abstract class Component extends Arg {
case class DefModule(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component
case class DefBlackBox(id: Module, name: String, ports: Seq[Port], params: Map[String, Param]) extends Component
-case class Circuit(name: String, components: Seq[Component])
+case class Circuit(name: String, components: Seq[Component], annotations: Seq[Annotation] = Seq.empty)
diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala
index ab51ad25..646702c3 100644
--- a/src/main/scala/chisel3/Driver.scala
+++ b/src/main/scala/chisel3/Driver.scala
@@ -6,10 +6,13 @@ import chisel3.internal.firrtl.Emitter
import scala.sys.process._
import java.io._
+import net.jcazevedo.moultingyaml._
import internal.firrtl._
import firrtl._
+import _root_.firrtl.annotations.AnnotationYamlProtocol._
+
/**
* The Driver provides methods to invoke the chisel3 compiler and the firrtl compiler.
* By default firrtl is automatically run after chisel. an [[ExecutionOptionsManager]]
@@ -239,6 +242,15 @@ object Driver extends BackendCompilationUtilities {
w.write(firrtlString)
w.close()
+ val annotationFile = new File(optionsManager.getBuildFileName("anno"))
+ val af = new FileWriter(annotationFile)
+ af.write(circuit.annotations.toArray.toYaml.prettyPrint)
+ af.close()
+
+ /* This passes the firrtl source and annotations directly to firrtl */
+ optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
+ firrtlSource = Some(firrtlString), annotations = circuit.annotations.toList)
+
val firrtlExecutionResult = if(chiselOptions.runFirrtlCompiler) {
Some(firrtl.Driver.execute(optionsManager))
}
diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala
index e4e64b89..25d3ec3a 100644
--- a/src/main/scala/chisel3/package.scala
+++ b/src/main/scala/chisel3/package.scala
@@ -32,6 +32,9 @@ package object chisel3 { // scalastyle:ignore package.object.name
type Element = chisel3.core.Element
type Bits = chisel3.core.Bits
+ type ChiselAnnotation = chisel3.core.ChiselAnnotation
+ val ChiselAnnotation = chisel3.core.ChiselAnnotation
+
// Some possible regex replacements for the literal specifier deprecation:
// (note: these are not guaranteed to handle all edge cases! check all replacements!)
// Bool\((true|false)\)
diff --git a/src/test/scala/chiselTests/AnnotatingDiamondSpec.scala b/src/test/scala/chiselTests/AnnotatingDiamondSpec.scala
new file mode 100644
index 00000000..3886ddd6
--- /dev/null
+++ b/src/test/scala/chiselTests/AnnotatingDiamondSpec.scala
@@ -0,0 +1,164 @@
+// See LICENSE for license details.
+
+package chiselTests
+
+import chisel3._
+import chisel3.internal.InstanceId
+import chisel3.testers.BasicTester
+import firrtl.{CircuitForm, CircuitState, LowForm, Transform}
+import firrtl.annotations.{Annotation, ModuleName, Named}
+import org.scalatest._
+
+//scalastyle:off magic.number
+/**
+ * This and the Identity transform class are a highly schematic implementation of a
+ * library implementation of (i.e. code outside of firrtl itself)
+ */
+object IdentityAnnotation {
+ def apply(target: Named, value: String): Annotation = Annotation(target, classOf[IdentityTransform], value)
+
+ def unapply(a: Annotation): Option[(Named, String)] = a match {
+ case Annotation(named, t, value) if t == classOf[IdentityTransform] => Some((named, value))
+ case _ => None
+ }
+}
+
+class IdentityTransform extends Transform {
+ override def inputForm: CircuitForm = LowForm
+
+ override def outputForm: CircuitForm = LowForm
+
+ override def execute(state: CircuitState): CircuitState = {
+ getMyAnnotations(state) match {
+ case Nil => state
+ case myAnnotations =>
+ /* Do something useful with annotations here */
+ state
+ }
+ }
+}
+
+trait IdentityAnnotator {
+ self: Module =>
+ def identify(component: InstanceId, value: String): Unit = {
+ annotate(ChiselAnnotation(component, classOf[IdentityTransform], value))
+ }
+}
+/** A diamond circuit Top instantiates A and B and both A and B instantiate C
+ * Illustrations of annotations of various components and modules in both
+ * relative and absolute cases
+ *
+ * This is currently not much of a test, read the printout to see what annotations look like
+ */
+/**
+ * This class has parameterizable widths, it will generate different hardware
+ * @param widthC io width
+ */
+class ModC(widthC: Int) extends Module with IdentityAnnotator {
+ val io = IO(new Bundle {
+ val in = Input(UInt(widthC.W))
+ val out = Output(UInt(widthC.W))
+ })
+ io.out := io.in
+
+ identify(this, s"ModC($widthC)")
+
+ identify(io.out, s"ModC(ignore param)")
+}
+
+/**
+ * instantiates a C of a particular size, ModA does not generate different hardware
+ * based on it's parameter
+ * @param annoParam parameter is only used in annotation not in circuit
+ */
+class ModA(annoParam: Int) extends Module with IdentityAnnotator {
+ val io = IO(new Bundle {
+ val in = Input(UInt())
+ val out = Output(UInt())
+ })
+ val modC = Module(new ModC(16))
+ modC.io.in := io.in
+ io.out := modC.io.out
+
+ identify(this, s"ModA(ignore param)")
+
+ identify(io.out, s"ModA.io.out($annoParam)")
+ identify(io.out, s"ModA.io.out(ignore_param)")
+}
+
+class ModB(widthB: Int) extends Module with IdentityAnnotator{
+ val io = IO(new Bundle {
+ val in = Input(UInt(widthB.W))
+ val out = Output(UInt(widthB.W))
+ })
+ val modC = Module(new ModC(widthB))
+ modC.io.in := io.in
+ io.out := modC.io.out
+
+ identify(io.in, s"modB.io.in annotated from inside modB")
+}
+
+class TopOfDiamond extends Module with IdentityAnnotator {
+ val io = IO(new Bundle {
+ val in = Input(UInt(32.W))
+ val out = Output(UInt(32.W))
+ })
+ val x = Reg(UInt(32.W))
+ val y = Reg(UInt(32.W))
+
+ val modA = Module(new ModA(64))
+ val modB = Module(new ModB(32))
+
+ x := io.in
+ modA.io.in := x
+ modB.io.in := x
+
+ y := modA.io.out + modB.io.out
+ io.out := y
+
+ identify(this, s"TopOfDiamond\nWith\nSome new lines")
+
+ identify(modB.io.in, s"modB.io.in annotated from outside modB")
+}
+
+class DiamondTester extends BasicTester {
+ val dut = Module(new TopOfDiamond)
+
+ stop()
+}
+
+class AnnotatingDiamondSpec extends FreeSpec with Matchers {
+ def findAnno(as: Seq[Annotation], name: String): Option[Annotation] = {
+ as.find { a => a.targetString == name }
+ }
+
+ """
+ |Diamond is an example of a module that has two sub-modules A and B who both instantiate their
+ |own instances of module C. This highlights the difference between specific and general
+ |annotation scopes
+ """.stripMargin - {
+
+ """
+ |annotations are not resolved at after circuit elaboration,
+ |that happens only after emit has been called on circuit""".stripMargin in {
+
+ Driver.execute(Array.empty[String], () => new TopOfDiamond) match {
+ case ChiselExecutionSucccess(Some(circuit), emitted, _) =>
+ val annos = circuit.annotations
+ annos.length should be (10)
+
+ annos.count {
+ case Annotation(ModuleName(name, _), _, annoValue) => name == "ModC" && annoValue == "ModC(16)"
+ case _ => false
+ } should be (1)
+
+ annos.count {
+ case Annotation(ModuleName(name, _), _, annoValue) => name == "ModC_1" && annoValue == "ModC(32)"
+ case _ => false
+ } should be (1)
+ case _ =>
+ assert(false)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/test/scala/chiselTests/AnnotatingExample.scala b/src/test/scala/chiselTests/AnnotatingExample.scala
deleted file mode 100644
index 0be3ba59..00000000
--- a/src/test/scala/chiselTests/AnnotatingExample.scala
+++ /dev/null
@@ -1,145 +0,0 @@
-// See LICENSE for license details.
-
-package chiselTests
-
-import chisel3._
-import chisel3.core.Module
-import chisel3.internal.InstanceId
-import chisel3.testers.BasicTester
-import org.scalatest._
-
-import scala.util.DynamicVariable
-
-//scalastyle:off magic.number
-
-/**
- * This Spec file illustrates use of Donggyu's component name API, it currently only
- * uses three methods .signalName, .parentModName and .pathName
- *
- * This is also an illustration of how to implement an annotation system in chisel3
- * A local (my) Driver and Builder are created to provide thread-local access to
- * an annotation map, and then a post elaboration annotation processor can resolve
- * the keys and could serialize the annotations to a file for use by firrtl passes
- */
-
-class SomeSubMod(param1: Int, param2: Int) extends Module {
- val io = new Bundle {
- val in = Input(UInt(16.W))
- val out = Output(SInt(32.W))
- }
- val annotate = MyBuilder.myDynamicContext.annotationMap
-
- annotate(AnnotationKey(this, JustThisRef)) = s"SomeSubMod($param1, $param2)"
- annotate(AnnotationKey(io.in, AllRefs)) = "sub mod io.in"
- annotate(AnnotationKey(io.out, JustThisRef)) = "sub mod io.out"
-}
-
-class AnnotatingExample extends Module {
- val io = new Bundle {
- val a = Input(UInt(32.W))
- val b = Input(UInt(32.W))
- val e = Input(Bool())
- val z = Output(UInt(32.W))
- val v = Output(Bool())
- val bun = new Bundle {
- val nested_1 = Input(UInt(12.W))
- val nested_2 = Output(Bool())
- }
- }
- val x = Reg(UInt(32.W))
- val y = Reg(UInt(32.W))
-
- val subModule1 = Module(new SomeSubMod(1, 2))
- val subModule2 = Module(new SomeSubMod(3, 4))
-
-
- val annotate = MyBuilder.myDynamicContext.annotationMap
-
- annotate(AnnotationKey(subModule2, AllRefs)) = s"SomeSubMod was used"
-
- annotate(AnnotationKey(x, JustThisRef)) = "I am register X"
- annotate(AnnotationKey(y, AllRefs)) = "I am register Y"
- annotate(AnnotationKey(io.a, JustThisRef)) = "I am io.a"
- annotate(AnnotationKey(io.bun.nested_1, AllRefs)) = "I am io.bun.nested_1"
- annotate(AnnotationKey(io.bun.nested_2, JustThisRef)) = "I am io.bun.nested_2"
-}
-
-class AnnotatingExampleTester extends BasicTester {
- val dut = Module(new AnnotatingExample)
-
- stop()
-}
-
-class AnnotatingExampleSpec extends FlatSpec with Matchers {
- behavior of "Annotating components of a circuit"
-
- it should "contain the following relative keys" in {
- val annotationMap = MyDriver.buildAnnotatedCircuit { () => new AnnotatingExampleTester }
-
- annotationMap.contains("SomeSubMod.io.in") should be(true)
- annotationMap.contains("AnnotatingExample.y") should be(true)
-
- annotationMap("SomeSubMod.io.in") should be("sub mod io.in")
- }
- it should "contain the following absolute keys" in {
- val annotationMap = MyDriver.buildAnnotatedCircuit { () => new AnnotatingExampleTester }
-
- annotationMap.contains("AnnotatingExampleTester.dut.subModule2.io.out") should be (true)
- annotationMap.contains("AnnotatingExampleTester.dut.x") should be (true)
-
- annotationMap("AnnotatingExampleTester.dut.subModule2.io.out") should be ("sub mod io.out")
- }
-}
-
-trait AnnotationScope
-case object AllRefs extends AnnotationScope
-case object JustThisRef extends AnnotationScope
-
-object AnnotationKey {
- def apply(component: InstanceId): AnnotationKey = {
- AnnotationKey(component, AllRefs)
- }
-}
-case class AnnotationKey(val component: InstanceId, scope: AnnotationScope) {
- override def toString: String = {
- scope match {
- case JustThisRef =>
- s"${component.pathName}"
- case AllRefs =>
- s"${component.parentModName}.${component.instanceName}"
- case _ =>
- s"${component.toString}_unknown_scope"
- }
- }
-}
-
-class AnnotationMap extends scala.collection.mutable.HashMap[AnnotationKey, String]
-
-class MyDynamicContext {
- val annotationMap = new AnnotationMap
-}
-
-object MyBuilder {
- private val myDynamicContextVar = new DynamicVariable[Option[MyDynamicContext]](None)
-
- def myDynamicContext: MyDynamicContext =
- myDynamicContextVar.value getOrElse new MyDynamicContext
-
- def processAnnotations(annotationMap: AnnotationMap): Map[String, String] = {
- annotationMap.map { case (k,v) => k.toString -> v}.toMap
- }
-
- def build[T <: Module](f: => T): Map[String, String] = {
- myDynamicContextVar.withValue(Some(new MyDynamicContext)) {
- Driver.emit(() => f)
- processAnnotations(myDynamicContextVar.value.get.annotationMap)
- }
- }
-}
-
-object MyDriver extends BackendCompilationUtilities {
- /**
- * illustrates a chisel3 style driver that, annotations can only processed within this structure
- */
- def buildAnnotatedCircuit[T <: Module](gen: () => T): Map[String, String] = MyBuilder.build(gen())
-}