From 67de39e9579867574734de154ae1672d27976ee5 Mon Sep 17 00:00:00 2001 From: chick Date: Fri, 11 Sep 2020 17:06:19 -0700 Subject: [PATCH] Refactor tapeout for Chisel 3.4, Firrtl 1.4 - Remove clk package based on discussion with Colin - Annotations need to be refactored to using latest API - Generally that involves making annos generated by a anonymous ChiselAnnotation - The chisel annotations will use RunFirrtlTransform to queue up its associated transform - Chisel annotation provieds toFirrtl to generate Firrtl form of annotation - Usages of unapply on firrtl annotations cannot use generic unapply(target, transform, data) which has been eliminated - Have transforms use with DependencyAPIMigration to avoid deprecated `form`s - Added some 'see License comments - TechnologyLocation section of AddIOPadsSpec does not currently run because there is no content for it. - Added some tests for annotation serialization here --- .../transforms/.pads/AddIOPadsTransform.scala | 9 +- .../transforms/.pads/ChiselTopModule.scala | 18 +- .../transforms/.pads/FoundryPadsYaml.scala | 7 +- .../transforms/.pads/PadAnnotations.scala | 56 ++--- .../transforms/AddSuffixToModuleNames.scala | 5 +- .../scala/transforms/EnumerateModules.scala | 8 +- .../src/main/scala/transforms/Generate.scala | 9 +- .../main/scala/transforms/ResetInverter.scala | 26 +-- .../main/scala/transforms/retime/Retime.scala | 28 +-- .../scala/transforms/utils/FileUtils.scala | 45 ++-- .../transforms/.pads/AddIOPadsSpec.scala | 203 +++++++++++------- .../scala/transforms/ResetInverterSpec.scala | 25 +-- .../scala/transforms/retime/RetimeSpec.scala | 54 +++-- 13 files changed, 275 insertions(+), 218 deletions(-) diff --git a/tapeout/src/main/scala/transforms/.pads/AddIOPadsTransform.scala b/tapeout/src/main/scala/transforms/.pads/AddIOPadsTransform.scala index b586e8be..1d12adb8 100644 --- a/tapeout/src/main/scala/transforms/.pads/AddIOPadsTransform.scala +++ b/tapeout/src/main/scala/transforms/.pads/AddIOPadsTransform.scala @@ -3,24 +3,19 @@ package barstools.tapeout.transforms.pads import firrtl._ -import firrtl.annotations._ import firrtl.passes._ -import firrtl.ir._ import barstools.tapeout.transforms._ import scala.collection.mutable // Main Add IO Pad transform operates on low Firrtl -class AddIOPadsTransform extends Transform with SeqTransformBased { - - override def inputForm: CircuitForm = LowForm - override def outputForm: CircuitForm = LowForm +class AddIOPadsTransform extends Transform with SeqTransformBased with DependencyAPIMigration { val transformList = new mutable.ArrayBuffer[Transform] def transforms: Seq[Transform] = transformList override def execute(state: CircuitState): CircuitState = { - val collectedAnnos = HasPadAnnotation(getMyAnnotations(state)) + val collectedAnnos = HasPadAnnotation(state.annotations) collectedAnnos match { // Transform not used case None => state diff --git a/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala b/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala index a348ff72..5b2ed28a 100644 --- a/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala +++ b/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala @@ -77,7 +77,12 @@ abstract class TopModule( // Annotate IO with side + pad name def annotatePad(sig: Element, side: PadSide = defaultPadSide, name: String = ""): Unit = if (usePads) { val anno = IOPadAnnotation(side.serialize, name) - annotate(TargetIOPadAnnoC(sig, anno)) + annotate(new ChiselAnnotation with RunFirrtlTransform { + override def toFirrtl: Annotation = { + TargetIOPadAnnoF(sig.toTarget, anno) + } + def transformClass: Class[_ <: Transform] = classOf[AddIOPadsTransform] + }) } def annotatePad(sig: Aggregate, name: String): Unit = annotatePad(sig, side = defaultPadSide, name) def annotatePad(sig: Aggregate, side: PadSide): Unit = annotatePad(sig, side, name = "") @@ -86,7 +91,16 @@ abstract class TopModule( // There may be cases where pads were inserted elsewhere. If that's the case, allow certain IO to // not have pads auto added. Note that annotatePad and noPad are mutually exclusive! - def noPad(sig: Element): Unit = if (usePads) annotate(TargetIOPadAnnoC(sig, NoIOPadAnnotation())) + def noPad(sig: Element): Unit = { + if (usePads) { + annotate(new ChiselAnnotation with RunFirrtlTransform { + override def toFirrtl: Annotation = { + TargetIOPadAnnoF(sig.toTarget, NoIOPadAnnotation()) + } + def transformClass: Class[_ <: Transform] = classOf[AddIOPadsTransform] + }) + } + } def noPad(sig: Aggregate): Unit = extractElements(sig) foreach { x => noPad(x) } // Since this is a super class, this should be the first thing that gets run diff --git a/tapeout/src/main/scala/transforms/.pads/FoundryPadsYaml.scala b/tapeout/src/main/scala/transforms/.pads/FoundryPadsYaml.scala index 2d372a51..ef6fdde7 100644 --- a/tapeout/src/main/scala/transforms/.pads/FoundryPadsYaml.scala +++ b/tapeout/src/main/scala/transforms/.pads/FoundryPadsYaml.scala @@ -1,3 +1,5 @@ +// See LICENSE for license details. + package barstools.tapeout.transforms.pads import net.jcazevedo.moultingyaml._ @@ -85,8 +87,9 @@ object FoundryPadsYaml extends DefaultYamlProtocol { implicit val _pad = yamlFormat6(FoundryPad) def parse(techDir: String): Seq[FoundryPad] = { val file = techDir + exampleResource - if(techDir != "" && !(new java.io.File(file)).exists()) - throw new Exception("Technology directory must contain FoundryPads.yaml!") + if(techDir != "" && !(new java.io.File(file)).exists()) { + throw new Exception(s"Technology directory $techDir must contain FoundryPads.yaml!") + } val out = (new YamlFileReader(exampleResource)).parse[FoundryPad](if (techDir == "") "" else file) val padNames = out.map(x => x.correctedName) require(padNames.distinct.length == padNames.length, "Pad names must be unique!") diff --git a/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala b/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala index c1f2d783..7ca49799 100644 --- a/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala +++ b/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala @@ -3,9 +3,6 @@ package barstools.tapeout.transforms.pads import firrtl.annotations._ -import chisel3.experimental._ -import chisel3._ - import net.jcazevedo.moultingyaml._ object PadAnnotationsYaml extends DefaultYamlProtocol { @@ -13,6 +10,17 @@ object PadAnnotationsYaml extends DefaultYamlProtocol { implicit val _noiopad = yamlFormat1(NoIOPadAnnotation) implicit val _supplyanno = yamlFormat5(SupplyAnnotation) implicit val _modulepadanno = yamlFormat4(ModulePadAnnotation) + + // Putting these serialize methods here seems to fix warnings about missing implicits for the toYaml + def serialize(noIOPad: NoIOPadAnnotation): String = { + noIOPad.toYaml.prettyPrint + } + def serialize(ioPadAnnotation: IOPadAnnotation): String = { + ioPadAnnotation.toYaml.prettyPrint + } + def serialize(modulePadAnnotation: ModulePadAnnotation): String = { + modulePadAnnotation.toYaml.prettyPrint + } } abstract class FirrtlPadTransformAnnotation { @@ -25,14 +33,12 @@ abstract class IOAnnotation { } case class IOPadAnnotation(padSide: String, padName: String) extends IOAnnotation { - import PadAnnotationsYaml._ - def serialize: String = this.toYaml.prettyPrint + def serialize: String = PadAnnotationsYaml.serialize(this) def getPadSide: PadSide = HasPadAnnotation.getSide(padSide) } case class NoIOPadAnnotation(noPad: String = "") extends IOAnnotation { - import PadAnnotationsYaml._ - def serialize: String = this.toYaml.prettyPrint + def serialize: String = PadAnnotationsYaml.serialize(this) def field: String = "noPad:" } @@ -44,12 +50,6 @@ case class TargetIOPadAnnoF(target: ComponentName, anno: IOAnnotation) def targetName: String = target.name } -//TODO: PORT-1.4: Remove commented code -// Chisel version -//case class TargetIOPadAnnoC(target: Element, anno: IOAnnotation) extends ChiselAnnotation { -// def toFirrtl = TargetIOPadAnnoF(target.toNamed, anno) -//} - // A bunch of supply pads (designated by name, # on each chip side) can be associated with the top module case class SupplyAnnotation( padName: String, @@ -64,9 +64,9 @@ case class ModulePadAnnotation( coreWidth: Int = 0, coreHeight: Int = 0, supplyAnnos: Seq[SupplyAnnotation] = Seq.empty) { - import PadAnnotationsYaml._ - def serialize: String = this.toYaml.prettyPrint - val supplyPadNames = supplyAnnos.map(_.padName) + + def serialize: String = PadAnnotationsYaml.serialize(this) + def supplyPadNames: Seq[String] = supplyAnnos.map(_.padName) require(supplyPadNames.distinct.length == supplyPadNames.length, "Supply pads should only be specified once!") def getDefaultPadSide: PadSide = HasPadAnnotation.getSide(defaultPadSide) } @@ -91,7 +91,6 @@ case class CollectedAnnos( } object HasPadAnnotation { - import PadAnnotationsYaml._ def getSide(a: String): PadSide = a match { case i if i == Left.serialize => Left @@ -115,26 +114,13 @@ object HasPadAnnotation { // case _ => None // } - //scalastyle:off cyclomatic.complexity - def unapply(a: Annotation): Option[FirrtlPadTransformAnnotation] = a match { - case hasTransform: RunFirrtlTransform if hasTransform.transformClass == classOf[AddIOPadsTransform] => - hasTransform match { - case hasTarget: SingleTargetAnnotation[_] => - hasTarget.target match { - case m: ModuleName => - Some(TargetModulePadAnnoF(m, s.parseYaml.convertTo[ModulePadAnnotation])) - hasTarget match { - case _ => None - } - - - } - case _ => None - } - def apply(annos: Seq[Annotation]): Option[CollectedAnnos] = { // Get all pad-related annotations (config files, pad sides, pad names, etc.) - val padAnnos = annos.map(x => unapply(x)).flatten + val padAnnos = annos.flatMap { + case a: TargetModulePadAnnoF => Some(a) + case a: TargetIOPadAnnoF => Some(a) + case _ => None + } val targets = padAnnos.map(x => x.targetName) require(targets.distinct.length == targets.length, "Only 1 pad related annotation is allowed per component/module") if (padAnnos.length == 0) { diff --git a/tapeout/src/main/scala/transforms/AddSuffixToModuleNames.scala b/tapeout/src/main/scala/transforms/AddSuffixToModuleNames.scala index 0e1a3739..ff8c1857 100644 --- a/tapeout/src/main/scala/transforms/AddSuffixToModuleNames.scala +++ b/tapeout/src/main/scala/transforms/AddSuffixToModuleNames.scala @@ -7,7 +7,6 @@ import firrtl.ir._ import firrtl.annotations._ import firrtl.Mappers._ - case class KeepNameAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] { def duplicate(n: ModuleTarget) = this.copy(n) @@ -22,9 +21,7 @@ case class ModuleNameSuffixAnnotation(target: CircuitTarget, suffix: String) // Verilog black box and therefore can't be renamed. Since the point is to // allow FIRRTL to be linked together using "cat" and ExtModules don't get // emitted, this should be safe. -class AddSuffixToModuleNames extends Transform { - def inputForm = LowForm - def outputForm = LowForm +class AddSuffixToModuleNames extends Transform with DependencyAPIMigration { def processAnnos(annos: AnnotationSeq): (AnnotationSeq, (String) => String) = { val whitelist = annos.collect({ case KeepNameAnnotation(tgt) => tgt.module }).toSet diff --git a/tapeout/src/main/scala/transforms/EnumerateModules.scala b/tapeout/src/main/scala/transforms/EnumerateModules.scala index 11da911e..4bd2855f 100644 --- a/tapeout/src/main/scala/transforms/EnumerateModules.scala +++ b/tapeout/src/main/scala/transforms/EnumerateModules.scala @@ -20,10 +20,10 @@ class EnumerateModulesPass(enumerate: (Module) => Unit) extends Pass { } } -class EnumerateModules(enumerate: (Module) => Unit) extends Transform with SeqTransformBased { - def inputForm = LowForm - def outputForm = LowForm - def transforms = Seq(new EnumerateModulesPass(enumerate)) +class EnumerateModules(enumerate: (Module) => Unit) + extends Transform with SeqTransformBased with DependencyAPIMigration { + + def transforms: Seq[Transform] = Seq(new EnumerateModulesPass(enumerate)) def execute(state: CircuitState): CircuitState = { val ret = runTransforms(state) diff --git a/tapeout/src/main/scala/transforms/Generate.scala b/tapeout/src/main/scala/transforms/Generate.scala index 89df8b55..b48f47e1 100644 --- a/tapeout/src/main/scala/transforms/Generate.scala +++ b/tapeout/src/main/scala/transforms/Generate.scala @@ -1,16 +1,11 @@ package barstools.tapeout.transforms import firrtl._ -import firrtl.ir._ import firrtl.annotations._ -import firrtl.stage.FirrtlCircuitAnnotation -import firrtl.passes.Pass - -import java.io.File -import firrtl.annotations.AnnotationYamlProtocol._ +import firrtl.ir._ import firrtl.passes.memlib.ReplSeqMemAnnotation +import firrtl.stage.FirrtlCircuitAnnotation import firrtl.transforms.BlackBoxResourceFileNameAnno -import net.jcazevedo.moultingyaml._ import logger.LazyLogging trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions => diff --git a/tapeout/src/main/scala/transforms/ResetInverter.scala b/tapeout/src/main/scala/transforms/ResetInverter.scala index 08d84983..e6bdad45 100644 --- a/tapeout/src/main/scala/transforms/ResetInverter.scala +++ b/tapeout/src/main/scala/transforms/ResetInverter.scala @@ -2,19 +2,15 @@ package barstools.tapeout.transforms -import chisel3.internal.InstanceId +import chisel3.experimental.RunFirrtlTransform import firrtl.PrimOps.Not -import firrtl.annotations.{Annotation, CircuitName, ModuleName, Named} -import firrtl.ir.{Input, UIntType, IntWidth, Module, Port, DefNode, NoInfo, Reference, DoPrim, Block, Circuit} +import firrtl.annotations.{Annotation, CircuitName, ModuleName, SingleTargetAnnotation} +import firrtl.ir._ import firrtl.passes.Pass -import firrtl.{CircuitForm, CircuitState, LowForm, Transform} +import firrtl.{CircuitState, DependencyAPIMigration, Transform} -object ResetInverterAnnotation { - def apply(target: ModuleName): Annotation = Annotation(target, classOf[ResetInverterTransform], "invert") - def unapply(a: Annotation): Option[Named] = a match { - case Annotation(m, t, "invert") if t == classOf[ResetInverterTransform] => Some(m) - case _ => None - } +case class ResetInverterAnnotation(target: ModuleName) extends SingleTargetAnnotation[ModuleName] { + override def duplicate(n: ModuleName): Annotation = ResetInverterAnnotation(n) } object ResetN extends Pass { @@ -42,12 +38,9 @@ object ResetN extends Pass { } } -class ResetInverterTransform extends Transform { - override def inputForm: CircuitForm = LowForm - override def outputForm: CircuitForm = LowForm - +class ResetInverterTransform extends Transform with DependencyAPIMigration { override def execute(state: CircuitState): CircuitState = { - getMyAnnotations(state) match { + state.annotations.filter(_.isInstanceOf[ResetInverterAnnotation]) match { case Nil => state case Seq(ResetInverterAnnotation(ModuleName(state.circuit.main, CircuitName(_)))) => state.copy(circuit = ResetN.run(state.circuit)) @@ -60,7 +53,8 @@ class ResetInverterTransform extends Transform { trait ResetInverter { self: chisel3.Module => def invert[T <: chisel3.internal.LegacyModule](module: T): Unit = { - chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation{ + chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform { + def transformClass: Class[_ <: Transform] = classOf[ResetInverterTransform] def toFirrtl: Annotation = ResetInverterAnnotation(module.toNamed) }) } diff --git a/tapeout/src/main/scala/transforms/retime/Retime.scala b/tapeout/src/main/scala/transforms/retime/Retime.scala index 231687bf..f790f7b3 100644 --- a/tapeout/src/main/scala/transforms/retime/Retime.scala +++ b/tapeout/src/main/scala/transforms/retime/Retime.scala @@ -2,27 +2,17 @@ package barstools.tapeout.transforms.retime -import chisel3.internal.InstanceId -import firrtl.PrimOps.Not -import firrtl.annotations.{Annotation, CircuitName, ModuleName, Named, ComponentName} -import firrtl.ir.{Input, UIntType, IntWidth, Module, Port, DefNode, NoInfo, Reference, DoPrim, Block, Circuit} -import firrtl.passes.Pass -import firrtl.{CircuitForm, CircuitState, LowForm, Transform} +import chisel3.experimental.RunFirrtlTransform +import firrtl.annotations._ +import firrtl.{CircuitState, DependencyAPIMigration, Transform} -object RetimeAnnotation { - def apply(target: ModuleName): Annotation = Annotation(target, classOf[RetimeTransform], "retime") - def unapply(a: Annotation): Option[Named] = a match { - case Annotation(m, t, "retime") if t == classOf[RetimeTransform] => Some(m) - case _ => None - } +case class RetimeAnnotation(target: Named) extends SingleTargetAnnotation[Named] { + override def duplicate(n: Named): Annotation = RetimeAnnotation(n) } -class RetimeTransform extends Transform { - override def inputForm: CircuitForm = LowForm - override def outputForm: CircuitForm = LowForm - +class RetimeTransform extends Transform with DependencyAPIMigration { override def execute(state: CircuitState): CircuitState = { - getMyAnnotations(state) match { + state.annotations.filter(_.isInstanceOf[RetimeAnnotation]) match { case Nil => state case seq => seq.foreach { case RetimeAnnotation(ModuleName(module, CircuitName(_))) => @@ -39,8 +29,10 @@ class RetimeTransform extends Transform { trait RetimeLib { self: chisel3.Module => + def retime[T <: chisel3.internal.LegacyModule](module: T): Unit = { - chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation{ + chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform { + def transformClass: Class[_ <: Transform] = classOf[RetimeTransform] def toFirrtl: Annotation = RetimeAnnotation(module.toNamed) }) } diff --git a/tapeout/src/main/scala/transforms/utils/FileUtils.scala b/tapeout/src/main/scala/transforms/utils/FileUtils.scala index 7baf3e14..76ee45ec 100644 --- a/tapeout/src/main/scala/transforms/utils/FileUtils.scala +++ b/tapeout/src/main/scala/transforms/utils/FileUtils.scala @@ -1,9 +1,11 @@ +// See LICENSE for license details. + package barstools.tapeout.transforms +import chisel3.experimental.{ChiselAnnotation, annotate} import firrtl._ import firrtl.annotations._ -import firrtl.passes._ -import firrtl.ir._ +import firrtl.transforms.BlackBoxTargetDirAnno object WriteConfig { def apply(dir: String, file: String, contents: String): Unit = { @@ -17,8 +19,7 @@ object GetTargetDir { def apply(state: CircuitState): String = { val annos = state.annotations val destDir = annos.map { - case Annotation(f, t, s) if t == classOf[firrtl.transforms.BlackBoxTargetDirAnno] => - Some(s) + case BlackBoxTargetDirAnno(s) => Some(s) case _ => None }.flatten val loc = { @@ -31,27 +32,39 @@ object GetTargetDir { } } -// Fake transform just to track Technology information directory -object TechnologyLocation { - def apply(dir: String): Annotation = { - Annotation(CircuitName("All"), classOf[TechnologyLocation], dir) +trait HasSetTechnologyLocation { + self: chisel3.Module => + + def setTechnologyLocation(dir: String) { + annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = { + TechnologyLocationAnnotation(dir) + } + }) } } -class TechnologyLocation extends Transform { - def inputForm: CircuitForm = LowForm - def outputForm: CircuitForm = LowForm - def execute(state: CircuitState) = throw new Exception("Technology Location transform execution doesn't work!") + +case class TechnologyLocationAnnotation(dir: String) extends SingleTargetAnnotation[CircuitName] { + val target: CircuitName = CircuitName("All") + override def duplicate(n: CircuitName): Annotation = TechnologyLocationAnnotation(dir) +} + +class TechnologyLocation extends Transform with DependencyAPIMigration { + def execute(state: CircuitState): CircuitState = { + throw new Exception("Technology Location transform execution doesn't work!") + } + def get(state: CircuitState): String = { val annos = state.annotations - val dir = annos.map { - case Annotation(f, t, s) if t == classOf[TechnologyLocation] => Some(s) + val dir = annos.flatMap { + case TechnologyLocationAnnotation(dir) => Some(dir) case _ => None - }.flatten + } dir.length match { case 0 => "" case 1 => val targetDir = new java.io.File(dir.head) - if(!targetDir.exists()) throw new Exception("Technology yaml directory doesn't exist!") + if(!targetDir.exists()) throw new Exception(s"Technology yaml directory $targetDir doesn't exist!") dir.head case _ => throw new Exception("Only 1 tech directory annotation allowed!") } diff --git a/tapeout/src/test/scala/transforms/.pads/AddIOPadsSpec.scala b/tapeout/src/test/scala/transforms/.pads/AddIOPadsSpec.scala index b578be97..43889539 100644 --- a/tapeout/src/test/scala/transforms/.pads/AddIOPadsSpec.scala +++ b/tapeout/src/test/scala/transforms/.pads/AddIOPadsSpec.scala @@ -2,64 +2,74 @@ package barstools.tapeout.transforms.pads +import java.io.File + +import barstools.tapeout.transforms.HasSetTechnologyLocation import chisel3._ +import chisel3.experimental._ +import chisel3.iotesters._ +import chisel3.util.HasBlackBoxInline import firrtl._ import org.scalatest.{FlatSpec, Matchers} -import chisel3.experimental._ -import chisel3.util.HasBlackBoxInline -import chisel3.iotesters._ class BB extends BlackBox with HasBlackBoxInline { val io = IO(new Bundle { val c = Input(SInt(14.W)) val z = Output(SInt(16.W)) val analog1 = Analog(3.W) - val analog2 = analog1.chiselCloneType + val analog2 = Analog(3.W) }) // Generates a "FakeBB.v" file with the following Verilog module - setInline("FakeBB.v", + setInline( + "FakeBB.v", s""" - |module BB( - | input [15:0] c, - | output [15:0] z, - | inout [2:0] analog1, - | inout [2:0] analog2 - |); - | always @* begin - | z = 2 * c; - | analog2 = analog1 + 1; - | end - |endmodule - """.stripMargin) + |module BB( + | input [15:0] c, + | output [15:0] z, + | inout [2:0] analog1, + | inout [2:0] analog2 + |); + | always @* begin + | z = 2 * c; + | analog2 = analog1 + 1; + | end + |endmodule + """.stripMargin + ) } // If no template file is provided, it'll use the default one (example) in the resource folder // Default pad side is Top if no side is specified for a given IO // You can designate the number of different supply pads on each chip side -class ExampleTopModuleWithBB extends TopModule( - supplyAnnos = Seq( - SupplyAnnotation(padName = "vdd", leftSide = 3, bottomSide = 2), - SupplyAnnotation(padName = "vss", rightSide = 1) - )) { +class ExampleTopModuleWithBB + extends TopModule( + supplyAnnos = Seq( + SupplyAnnotation(padName = "vdd", leftSide = 3, bottomSide = 2), + SupplyAnnotation(padName = "vss", rightSide = 1) + ) + ) + with HasSetTechnologyLocation { val io = IO(new Bundle { val a = Input(UInt(15.W)) - val b = a.chiselCloneType + val b = Input(a.cloneType) val c = Input(SInt(14.W)) val x = Output(UInt(16.W)) - val y = x.chiselCloneType + val y = Output(x.cloneType) val z = Output(SInt(16.W)) val analog1 = Analog(3.W) - val analog2 = analog1.chiselCloneType + val analog2 = analog1.cloneType val v = Output(Vec(3, UInt(5.W))) }) + setTechnologyLocation("./RealTech") + // Can annotate aggregates with pad side location + pad name (should be a name in the yaml template) annotatePad(io.v, Right, "from_tristate_foundry") // Can annotate individual elements annotatePad(io.analog1, Left, "fast_custom") annotatePad(io.analog2, Bottom, "slow_foundry") // Looks for a pad that matches the IO type (digital in, digital out, analog) if no name is specified - Seq(io.a, io.b, io.c, io.x) foreach { x => annotatePad(x, Left) } + Seq(io.a, io.b, io.c, io.x).foreach { x => annotatePad(x, Left) } // Some signals might not want pads associated with them noPad(io.y) // Clk might come directly from bump @@ -74,8 +84,8 @@ class ExampleTopModuleWithBB extends TopModule( io.x := io.a + 1.U io.y := io.b - 1.U - io.v foreach { lhs => lhs := io.a } - + io.v.foreach { lhs => lhs := io.a } + } class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c) { @@ -89,10 +99,10 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c) expect(c.io.x, ax(i) + 1) expect(c.io.y, bx(i) - 1) expect(c.io.z, 2 * cx(i)) - c.io.v foreach { out => expect(out, ax(i)) } + c.io.v.foreach { out => expect(out, ax(i)) } } - // Analog can't be peeked + poked -} + // Analog can't be peeked + poked +} // Notes: Annotations // a in 15: left, default digital @@ -110,7 +120,7 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c) // vdd, bottom: 2, group of 1 // vss, right: 1, group of 2 // Notes: Used pads -// digital horizontal (from_tristate_foundry) +// digital horizontal (from_tristate_foundry) // in + out // analog fast_custom horizontal // analog slow_foundry vertical @@ -122,72 +132,111 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c) class IOPadSpec extends FlatSpec with Matchers { - def readOutputFile(dir: String, f: String): String = - scala.io.Source.fromFile(Seq(dir, f).mkString("/")).getLines.mkString("\n") + def readOutputFile(dir: String, f: String): String = { + FileUtils.getText(dir + File.separator + f) + } def readResource(resource: String): String = { val stream = getClass.getResourceAsStream(resource) scala.io.Source.fromInputStream(stream).mkString } - def checkOutputs(dir: String) = { + def checkOutputs(dir: String): Unit = { // Show that black box source helper is run //readOutputFile(dir, "black_box_verilog_files.f") should include ("pad_supply_vdd_horizontal.v") val padBBEx = s"""// Digital Pad Example - |// Signal Direction: Input - |// Pad Orientation: Horizontal - |// Call your instance PAD - |module pad_digital_from_tristate_foundry_horizontal_input( - | input in, - | output reg out - |); - | // Where you would normally dump your pad instance - | always @* begin - | out = in; - | end - |endmodule - | - |module pad_digital_from_tristate_foundry_horizontal_input_array #( - | parameter int WIDTH=1 - |)( - | input [WIDTH-1:0] in, - | output reg [WIDTH-1:0] out - |); - | pad_digital_from_tristate_foundry_horizontal_input pad_digital_from_tristate_foundry_horizontal_input[WIDTH-1:0]( - | .in(in), - | .out(out) - | );""".stripMargin + |// Signal Direction: Input + |// Pad Orientation: Horizontal + |// Call your instance PAD + |module pad_digital_from_tristate_foundry_horizontal_input( + | input in, + | output reg out + |); + | // Where you would normally dump your pad instance + | always @* begin + | out = in; + | end + |endmodule + | + |module pad_digital_from_tristate_foundry_horizontal_input_array #( + | parameter int WIDTH=1 + |)( + | input [WIDTH-1:0] in, + | output reg [WIDTH-1:0] out + |); + | pad_digital_from_tristate_foundry_horizontal_input pad_digital_from_tristate_foundry_horizontal_input[WIDTH-1:0]( + | .in(in), + | .out(out) + | );""".stripMargin // Make sure black box templating is OK - readOutputFile(dir, "pad_digital_from_tristate_foundry_horizontal_input_array.v") should include (padBBEx) + readOutputFile(dir, "pad_digital_from_tristate_foundry_horizontal_input_array.v") should include(padBBEx) - val verilog = readOutputFile(dir, "ExampleTopModuleWithBB.v") + val verilog = readOutputFile(dir, "ExampleTopModuleWithBB.v") // Pad frame + top should be exact - verilog should include (readResource("/PadAnnotationVerilogPart.v")) + verilog should include(readResource("/PadAnnotationVerilogPart.v")) // Pad Placement IO file should be exact val padIO = readOutputFile(dir, "pads.io") padIO should include(readResource("/PadPlacement.io")) } - behavior of "top module with blackbox" + behavior.of("Pad Annotations") - import barstools.tapeout.transforms._ + it should "serialize pad annotations" in { + val noIOPadAnnotation = NoIOPadAnnotation("dog") + noIOPadAnnotation.serialize should include("noPad: dog") + + val ioPadAnnotation = IOPadAnnotation("left", "oliver") + ioPadAnnotation.serialize should include( + """padSide: left + |padName: oliver + |""".stripMargin) + + val modulePadAnnotation = ModulePadAnnotation( + "top", + 11, + 42, + Seq( + SupplyAnnotation("mypad, 1, 2 ,3 , 4"), + SupplyAnnotation("yourpad, 9, 8, 7, 6") + ) + ) + + modulePadAnnotation.serialize should be( + """defaultPadSide: top + |coreWidth: 11 + |coreHeight: 42 + |supplyAnnos: + |- rightSide: 0 + | padName: mypad, 1, 2 ,3 , 4 + | leftSide: 0 + | bottomSide: 0 + | topSide: 0 + |- rightSide: 0 + | padName: yourpad, 9, 8, 7, 6 + | leftSide: 0 + | bottomSide: 0 + | topSide: 0 + |""".stripMargin + ) + } + + behavior.of("top module with blackbox") it should "pass simple testbench" in { val optionsManager = new TesterOptionsManager { firrtlOptions = firrtlOptions.copy( compilerName = "verilog" - // annotations = List(TechnologyLocation("./RealTech")) ) testerOptions = testerOptions.copy(isVerbose = true, backendName = "verilator", displayBase = 10) commonOptions = commonOptions.copy(targetDirName = "test_run_dir/PadsTB") } iotesters.Driver.execute(() => new ExampleTopModuleWithBB, optionsManager) { c => val dir = optionsManager.commonOptions.targetDirName - checkOutputs(dir) + checkOutputs(dir) new SimpleTopModuleTester(c) - } should be (true) + } should be(true) } -/* + /* it should "create proper IO pads + black box in low firrtl" in { val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions { firrtlOptions = firrtlOptions.copy(compilerName = "low") @@ -196,15 +245,15 @@ class IOPadSpec extends FlatSpec with Matchers { } val success = chisel3.Driver.execute(optionsManager, () => new ExampleTopModuleWithBB) match { case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, firrtl))) => - firrtl should include ("ExampleTopModuleWithBB_PadFrame") + firrtl should include ("ExampleTopModuleWithBB_PadFrame") firrtl should include ("ExampleTopModuleWithBB_Internal") - firrtl should not include ("FakeBBPlaceholder") + firrtl should not include ("FakeBBPlaceholder") true case _ => false - } + } success should be (true) - } -*/ + } + */ it should "create proper IO pads + black box in verilog" in { val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions { firrtlOptions = firrtlOptions.copy( @@ -214,13 +263,13 @@ class IOPadSpec extends FlatSpec with Matchers { //commonOptions = commonOptions.copy(globalLogLevel = logger.LogLevel.Info) } val success = chisel3.Driver.execute(optionsManager, () => new ExampleTopModuleWithBB) match { - case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, verilog))) => + case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, verilog))) => true case _ => false - } - success should be (true) + } + success should be(true) val dir = optionsManager.commonOptions.targetDirName checkOutputs(dir) - } + } -} \ No newline at end of file +} diff --git a/tapeout/src/test/scala/transforms/ResetInverterSpec.scala b/tapeout/src/test/scala/transforms/ResetInverterSpec.scala index 07fca302..4b5de967 100644 --- a/tapeout/src/test/scala/transforms/ResetInverterSpec.scala +++ b/tapeout/src/test/scala/transforms/ResetInverterSpec.scala @@ -3,7 +3,7 @@ package barstools.tapeout.transforms import chisel3._ -import firrtl._ +import chisel3.stage.ChiselStage import org.scalatest.{FreeSpec, Matchers} class ExampleModuleNeedsResetInverted extends Module with ResetInverter { @@ -19,22 +19,15 @@ class ExampleModuleNeedsResetInverted extends Module with ResetInverter { } class ResetNSpec extends FreeSpec with Matchers { - "Inverting reset needs to be done throughout module" in { - val optionsManager = new ExecutionOptionsManager("dsptools") with HasChiselExecutionOptions with HasFirrtlOptions { - firrtlOptions = firrtlOptions.copy(compilerName = "low", customTransforms = List(new ResetInverterTransform)), - } - chisel3.Driver.execute(optionsManager, () => new ExampleModuleNeedsResetInverted) match { - case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, firrtl))) => - chirrtl should include ("input reset :") - chirrtl should not include "input reset_n :" - chirrtl should not include "node reset = not(reset_n)" + val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted, Array()) + chirrtl should include("input reset :") + (chirrtl should not).include("input reset_n :") + (chirrtl should not).include("node reset = not(reset_n)") - firrtl should include ("input reset_n :") - firrtl should include ("node reset = not(reset_n)") - firrtl should not include "input reset :" - case _ => - // bad - } + val firrtl = (new ChiselStage).emitFirrtl(new ExampleModuleNeedsResetInverted, Array("-X", "low")) + firrtl should include("input reset_n :") + firrtl should include("node reset = not(reset_n)") + (firrtl should not).include("input reset :") } } diff --git a/tapeout/src/test/scala/transforms/retime/RetimeSpec.scala b/tapeout/src/test/scala/transforms/retime/RetimeSpec.scala index 76223b71..1f2de5a8 100644 --- a/tapeout/src/test/scala/transforms/retime/RetimeSpec.scala +++ b/tapeout/src/test/scala/transforms/retime/RetimeSpec.scala @@ -2,13 +2,12 @@ package barstools.tapeout.transforms.retime.test -import chisel3._ -import firrtl._ -import org.scalatest.{FlatSpec, Matchers} -import chisel3.experimental._ -import chisel3.util.HasBlackBoxInline -import chisel3.iotesters._ import barstools.tapeout.transforms.retime._ +import chisel3._ +import chisel3.stage.ChiselStage +import firrtl._ +import logger.Logger +import org.scalatest.{FlatSpec, Matchers} class RetimeSpec extends FlatSpec with Matchers { def normalized(s: String): String = { @@ -25,20 +24,47 @@ class RetimeSpec extends FlatSpec with Matchers { it should "pass simple retime module annotation" in { val gen = () => new RetimeModule() val dir = uniqueDirName(gen, "RetimeModule") - chisel3.Driver.execute(Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final"), gen) shouldBe a [ChiselExecutionSuccess] - val lines = io.Source.fromFile(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json").getLines().map(normalized).mkString("\n") - lines should include("barstools.tapeout.transforms.retime.RetimeTransform") + Logger.makeScope(Seq.empty) { + val captor = new Logger.OutputCaptor + Logger.setOutput(captor.printStream) + val firrtl = (new ChiselStage).emitFirrtl( + new RetimeModule(), + Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info") + ) + firrtl.nonEmpty should be(true) + //Make sure we got the RetimeTransform scheduled + captor.getOutputAsString should include ("barstools.tapeout.transforms.retime.RetimeTransform") + } + + val lines = FileUtils.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json") + .map(normalized) + .mkString("\n") + lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation") + lines should include(""""target":"RetimeModule.RetimeModule"""") } - // TODO(azidar): need to fix/add instance annotations - ignore should "pass simple retime instance annotation" in { + it should "pass simple retime instance annotation" in { val gen = () => new RetimeInstance() val dir = uniqueDirName(gen, "RetimeInstance") - chisel3.Driver.execute(Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final.anno"), gen) shouldBe a [ChiselExecutionSuccess] - val lines = io.Source.fromFile(s"test_run_dir/$dir/final.anno").getLines().map(normalized).toSeq - lines should contain ("Annotation(ComponentName(instance, ModuleName(RetimeInstance,CircuitName(RetimeInstance))),class barstools.tapeout.transforms.retime.RetimeTransform,retime)") + Logger.makeScope(Seq.empty) { + val captor = new Logger.OutputCaptor + Logger.setOutput(captor.printStream) + val firrtl = (new ChiselStage).emitFirrtl( + new RetimeInstance(), + Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info") + ) + firrtl.nonEmpty should be(true) + //Make sure we got the RetimeTransform scheduled + captor.getOutputAsString should include ("barstools.tapeout.transforms.retime.RetimeTransform") + } + + val lines = FileUtils.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json") + .map(normalized) + .mkString("\n") + lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation") + lines should include(""""target":"RetimeInstance.MyModule"""") } }