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
This commit is contained in:
chick
2020-09-11 17:06:19 -07:00
parent e4cd2b01fe
commit 67de39e957
13 changed files with 275 additions and 218 deletions

View File

@@ -3,24 +3,19 @@
package barstools.tapeout.transforms.pads package barstools.tapeout.transforms.pads
import firrtl._ import firrtl._
import firrtl.annotations._
import firrtl.passes._ import firrtl.passes._
import firrtl.ir._
import barstools.tapeout.transforms._ import barstools.tapeout.transforms._
import scala.collection.mutable import scala.collection.mutable
// Main Add IO Pad transform operates on low Firrtl // Main Add IO Pad transform operates on low Firrtl
class AddIOPadsTransform extends Transform with SeqTransformBased { class AddIOPadsTransform extends Transform with SeqTransformBased with DependencyAPIMigration {
override def inputForm: CircuitForm = LowForm
override def outputForm: CircuitForm = LowForm
val transformList = new mutable.ArrayBuffer[Transform] val transformList = new mutable.ArrayBuffer[Transform]
def transforms: Seq[Transform] = transformList def transforms: Seq[Transform] = transformList
override def execute(state: CircuitState): CircuitState = { override def execute(state: CircuitState): CircuitState = {
val collectedAnnos = HasPadAnnotation(getMyAnnotations(state)) val collectedAnnos = HasPadAnnotation(state.annotations)
collectedAnnos match { collectedAnnos match {
// Transform not used // Transform not used
case None => state case None => state

View File

@@ -77,7 +77,12 @@ abstract class TopModule(
// Annotate IO with side + pad name // Annotate IO with side + pad name
def annotatePad(sig: Element, side: PadSide = defaultPadSide, name: String = ""): Unit = if (usePads) { def annotatePad(sig: Element, side: PadSide = defaultPadSide, name: String = ""): Unit = if (usePads) {
val anno = IOPadAnnotation(side.serialize, name) 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, name: String): Unit = annotatePad(sig, side = defaultPadSide, name)
def annotatePad(sig: Aggregate, side: PadSide): Unit = annotatePad(sig, side, 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 // 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! // 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) } 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 // Since this is a super class, this should be the first thing that gets run

View File

@@ -1,3 +1,5 @@
// See LICENSE for license details.
package barstools.tapeout.transforms.pads package barstools.tapeout.transforms.pads
import net.jcazevedo.moultingyaml._ import net.jcazevedo.moultingyaml._
@@ -85,8 +87,9 @@ object FoundryPadsYaml extends DefaultYamlProtocol {
implicit val _pad = yamlFormat6(FoundryPad) implicit val _pad = yamlFormat6(FoundryPad)
def parse(techDir: String): Seq[FoundryPad] = { def parse(techDir: String): Seq[FoundryPad] = {
val file = techDir + exampleResource val file = techDir + exampleResource
if(techDir != "" && !(new java.io.File(file)).exists()) if(techDir != "" && !(new java.io.File(file)).exists()) {
throw new Exception("Technology directory must contain FoundryPads.yaml!") throw new Exception(s"Technology directory $techDir must contain FoundryPads.yaml!")
}
val out = (new YamlFileReader(exampleResource)).parse[FoundryPad](if (techDir == "") "" else file) val out = (new YamlFileReader(exampleResource)).parse[FoundryPad](if (techDir == "") "" else file)
val padNames = out.map(x => x.correctedName) val padNames = out.map(x => x.correctedName)
require(padNames.distinct.length == padNames.length, "Pad names must be unique!") require(padNames.distinct.length == padNames.length, "Pad names must be unique!")

View File

@@ -3,9 +3,6 @@
package barstools.tapeout.transforms.pads package barstools.tapeout.transforms.pads
import firrtl.annotations._ import firrtl.annotations._
import chisel3.experimental._
import chisel3._
import net.jcazevedo.moultingyaml._ import net.jcazevedo.moultingyaml._
object PadAnnotationsYaml extends DefaultYamlProtocol { object PadAnnotationsYaml extends DefaultYamlProtocol {
@@ -13,6 +10,17 @@ object PadAnnotationsYaml extends DefaultYamlProtocol {
implicit val _noiopad = yamlFormat1(NoIOPadAnnotation) implicit val _noiopad = yamlFormat1(NoIOPadAnnotation)
implicit val _supplyanno = yamlFormat5(SupplyAnnotation) implicit val _supplyanno = yamlFormat5(SupplyAnnotation)
implicit val _modulepadanno = yamlFormat4(ModulePadAnnotation) 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 { abstract class FirrtlPadTransformAnnotation {
@@ -25,14 +33,12 @@ abstract class IOAnnotation {
} }
case class IOPadAnnotation(padSide: String, padName: String) extends IOAnnotation { case class IOPadAnnotation(padSide: String, padName: String) extends IOAnnotation {
import PadAnnotationsYaml._ def serialize: String = PadAnnotationsYaml.serialize(this)
def serialize: String = this.toYaml.prettyPrint
def getPadSide: PadSide = HasPadAnnotation.getSide(padSide) def getPadSide: PadSide = HasPadAnnotation.getSide(padSide)
} }
case class NoIOPadAnnotation(noPad: String = "") extends IOAnnotation { case class NoIOPadAnnotation(noPad: String = "") extends IOAnnotation {
import PadAnnotationsYaml._ def serialize: String = PadAnnotationsYaml.serialize(this)
def serialize: String = this.toYaml.prettyPrint
def field: String = "noPad:" def field: String = "noPad:"
} }
@@ -44,12 +50,6 @@ case class TargetIOPadAnnoF(target: ComponentName, anno: IOAnnotation)
def targetName: String = target.name 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 // A bunch of supply pads (designated by name, # on each chip side) can be associated with the top module
case class SupplyAnnotation( case class SupplyAnnotation(
padName: String, padName: String,
@@ -64,9 +64,9 @@ case class ModulePadAnnotation(
coreWidth: Int = 0, coreWidth: Int = 0,
coreHeight: Int = 0, coreHeight: Int = 0,
supplyAnnos: Seq[SupplyAnnotation] = Seq.empty) { supplyAnnos: Seq[SupplyAnnotation] = Seq.empty) {
import PadAnnotationsYaml._
def serialize: String = this.toYaml.prettyPrint def serialize: String = PadAnnotationsYaml.serialize(this)
val supplyPadNames = supplyAnnos.map(_.padName) def supplyPadNames: Seq[String] = supplyAnnos.map(_.padName)
require(supplyPadNames.distinct.length == supplyPadNames.length, "Supply pads should only be specified once!") require(supplyPadNames.distinct.length == supplyPadNames.length, "Supply pads should only be specified once!")
def getDefaultPadSide: PadSide = HasPadAnnotation.getSide(defaultPadSide) def getDefaultPadSide: PadSide = HasPadAnnotation.getSide(defaultPadSide)
} }
@@ -91,7 +91,6 @@ case class CollectedAnnos(
} }
object HasPadAnnotation { object HasPadAnnotation {
import PadAnnotationsYaml._
def getSide(a: String): PadSide = a match { def getSide(a: String): PadSide = a match {
case i if i == Left.serialize => Left case i if i == Left.serialize => Left
@@ -115,26 +114,13 @@ object HasPadAnnotation {
// case _ => None // 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] = { def apply(annos: Seq[Annotation]): Option[CollectedAnnos] = {
// Get all pad-related annotations (config files, pad sides, pad names, etc.) // 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) val targets = padAnnos.map(x => x.targetName)
require(targets.distinct.length == targets.length, "Only 1 pad related annotation is allowed per component/module") require(targets.distinct.length == targets.length, "Only 1 pad related annotation is allowed per component/module")
if (padAnnos.length == 0) { if (padAnnos.length == 0) {

View File

@@ -7,7 +7,6 @@ import firrtl.ir._
import firrtl.annotations._ import firrtl.annotations._
import firrtl.Mappers._ import firrtl.Mappers._
case class KeepNameAnnotation(target: ModuleTarget) case class KeepNameAnnotation(target: ModuleTarget)
extends SingleTargetAnnotation[ModuleTarget] { extends SingleTargetAnnotation[ModuleTarget] {
def duplicate(n: ModuleTarget) = this.copy(n) 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 // 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 // allow FIRRTL to be linked together using "cat" and ExtModules don't get
// emitted, this should be safe. // emitted, this should be safe.
class AddSuffixToModuleNames extends Transform { class AddSuffixToModuleNames extends Transform with DependencyAPIMigration {
def inputForm = LowForm
def outputForm = LowForm
def processAnnos(annos: AnnotationSeq): (AnnotationSeq, (String) => String) = { def processAnnos(annos: AnnotationSeq): (AnnotationSeq, (String) => String) = {
val whitelist = annos.collect({ case KeepNameAnnotation(tgt) => tgt.module }).toSet val whitelist = annos.collect({ case KeepNameAnnotation(tgt) => tgt.module }).toSet

View File

@@ -20,10 +20,10 @@ class EnumerateModulesPass(enumerate: (Module) => Unit) extends Pass {
} }
} }
class EnumerateModules(enumerate: (Module) => Unit) extends Transform with SeqTransformBased { class EnumerateModules(enumerate: (Module) => Unit)
def inputForm = LowForm extends Transform with SeqTransformBased with DependencyAPIMigration {
def outputForm = LowForm
def transforms = Seq(new EnumerateModulesPass(enumerate)) def transforms: Seq[Transform] = Seq(new EnumerateModulesPass(enumerate))
def execute(state: CircuitState): CircuitState = { def execute(state: CircuitState): CircuitState = {
val ret = runTransforms(state) val ret = runTransforms(state)

View File

@@ -1,16 +1,11 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import firrtl._ import firrtl._
import firrtl.ir._
import firrtl.annotations._ import firrtl.annotations._
import firrtl.stage.FirrtlCircuitAnnotation import firrtl.ir._
import firrtl.passes.Pass
import java.io.File
import firrtl.annotations.AnnotationYamlProtocol._
import firrtl.passes.memlib.ReplSeqMemAnnotation import firrtl.passes.memlib.ReplSeqMemAnnotation
import firrtl.stage.FirrtlCircuitAnnotation
import firrtl.transforms.BlackBoxResourceFileNameAnno import firrtl.transforms.BlackBoxResourceFileNameAnno
import net.jcazevedo.moultingyaml._
import logger.LazyLogging import logger.LazyLogging
trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions => trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>

View File

@@ -2,19 +2,15 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import chisel3.internal.InstanceId import chisel3.experimental.RunFirrtlTransform
import firrtl.PrimOps.Not import firrtl.PrimOps.Not
import firrtl.annotations.{Annotation, CircuitName, ModuleName, Named} import firrtl.annotations.{Annotation, CircuitName, ModuleName, SingleTargetAnnotation}
import firrtl.ir.{Input, UIntType, IntWidth, Module, Port, DefNode, NoInfo, Reference, DoPrim, Block, Circuit} import firrtl.ir._
import firrtl.passes.Pass import firrtl.passes.Pass
import firrtl.{CircuitForm, CircuitState, LowForm, Transform} import firrtl.{CircuitState, DependencyAPIMigration, Transform}
object ResetInverterAnnotation { case class ResetInverterAnnotation(target: ModuleName) extends SingleTargetAnnotation[ModuleName] {
def apply(target: ModuleName): Annotation = Annotation(target, classOf[ResetInverterTransform], "invert") override def duplicate(n: ModuleName): Annotation = ResetInverterAnnotation(n)
def unapply(a: Annotation): Option[Named] = a match {
case Annotation(m, t, "invert") if t == classOf[ResetInverterTransform] => Some(m)
case _ => None
}
} }
object ResetN extends Pass { object ResetN extends Pass {
@@ -42,12 +38,9 @@ object ResetN extends Pass {
} }
} }
class ResetInverterTransform extends Transform { class ResetInverterTransform extends Transform with DependencyAPIMigration {
override def inputForm: CircuitForm = LowForm
override def outputForm: CircuitForm = LowForm
override def execute(state: CircuitState): CircuitState = { override def execute(state: CircuitState): CircuitState = {
getMyAnnotations(state) match { state.annotations.filter(_.isInstanceOf[ResetInverterAnnotation]) match {
case Nil => state case Nil => state
case Seq(ResetInverterAnnotation(ModuleName(state.circuit.main, CircuitName(_)))) => case Seq(ResetInverterAnnotation(ModuleName(state.circuit.main, CircuitName(_)))) =>
state.copy(circuit = ResetN.run(state.circuit)) state.copy(circuit = ResetN.run(state.circuit))
@@ -60,7 +53,8 @@ class ResetInverterTransform extends Transform {
trait ResetInverter { trait ResetInverter {
self: chisel3.Module => self: chisel3.Module =>
def invert[T <: chisel3.internal.LegacyModule](module: T): Unit = { 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) def toFirrtl: Annotation = ResetInverterAnnotation(module.toNamed)
}) })
} }

View File

@@ -2,27 +2,17 @@
package barstools.tapeout.transforms.retime package barstools.tapeout.transforms.retime
import chisel3.internal.InstanceId import chisel3.experimental.RunFirrtlTransform
import firrtl.PrimOps.Not import firrtl.annotations._
import firrtl.annotations.{Annotation, CircuitName, ModuleName, Named, ComponentName} import firrtl.{CircuitState, DependencyAPIMigration, Transform}
import firrtl.ir.{Input, UIntType, IntWidth, Module, Port, DefNode, NoInfo, Reference, DoPrim, Block, Circuit}
import firrtl.passes.Pass
import firrtl.{CircuitForm, CircuitState, LowForm, Transform}
object RetimeAnnotation { case class RetimeAnnotation(target: Named) extends SingleTargetAnnotation[Named] {
def apply(target: ModuleName): Annotation = Annotation(target, classOf[RetimeTransform], "retime") override def duplicate(n: Named): Annotation = RetimeAnnotation(n)
def unapply(a: Annotation): Option[Named] = a match {
case Annotation(m, t, "retime") if t == classOf[RetimeTransform] => Some(m)
case _ => None
}
} }
class RetimeTransform extends Transform { class RetimeTransform extends Transform with DependencyAPIMigration {
override def inputForm: CircuitForm = LowForm
override def outputForm: CircuitForm = LowForm
override def execute(state: CircuitState): CircuitState = { override def execute(state: CircuitState): CircuitState = {
getMyAnnotations(state) match { state.annotations.filter(_.isInstanceOf[RetimeAnnotation]) match {
case Nil => state case Nil => state
case seq => seq.foreach { case seq => seq.foreach {
case RetimeAnnotation(ModuleName(module, CircuitName(_))) => case RetimeAnnotation(ModuleName(module, CircuitName(_))) =>
@@ -39,8 +29,10 @@ class RetimeTransform extends Transform {
trait RetimeLib { trait RetimeLib {
self: chisel3.Module => self: chisel3.Module =>
def retime[T <: chisel3.internal.LegacyModule](module: T): Unit = { 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) def toFirrtl: Annotation = RetimeAnnotation(module.toNamed)
}) })
} }

View File

@@ -1,9 +1,11 @@
// See LICENSE for license details.
package barstools.tapeout.transforms package barstools.tapeout.transforms
import chisel3.experimental.{ChiselAnnotation, annotate}
import firrtl._ import firrtl._
import firrtl.annotations._ import firrtl.annotations._
import firrtl.passes._ import firrtl.transforms.BlackBoxTargetDirAnno
import firrtl.ir._
object WriteConfig { object WriteConfig {
def apply(dir: String, file: String, contents: String): Unit = { def apply(dir: String, file: String, contents: String): Unit = {
@@ -17,8 +19,7 @@ object GetTargetDir {
def apply(state: CircuitState): String = { def apply(state: CircuitState): String = {
val annos = state.annotations val annos = state.annotations
val destDir = annos.map { val destDir = annos.map {
case Annotation(f, t, s) if t == classOf[firrtl.transforms.BlackBoxTargetDirAnno] => case BlackBoxTargetDirAnno(s) => Some(s)
Some(s)
case _ => None case _ => None
}.flatten }.flatten
val loc = { val loc = {
@@ -31,27 +32,39 @@ object GetTargetDir {
} }
} }
// Fake transform just to track Technology information directory trait HasSetTechnologyLocation {
object TechnologyLocation { self: chisel3.Module =>
def apply(dir: String): Annotation = {
Annotation(CircuitName("All"), classOf[TechnologyLocation], dir) def setTechnologyLocation(dir: String) {
annotate(new ChiselAnnotation {
override def toFirrtl: Annotation = {
TechnologyLocationAnnotation(dir)
}
})
} }
} }
class TechnologyLocation extends Transform {
def inputForm: CircuitForm = LowForm case class TechnologyLocationAnnotation(dir: String) extends SingleTargetAnnotation[CircuitName] {
def outputForm: CircuitForm = LowForm val target: CircuitName = CircuitName("All")
def execute(state: CircuitState) = throw new Exception("Technology Location transform execution doesn't work!") 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 = { def get(state: CircuitState): String = {
val annos = state.annotations val annos = state.annotations
val dir = annos.map { val dir = annos.flatMap {
case Annotation(f, t, s) if t == classOf[TechnologyLocation] => Some(s) case TechnologyLocationAnnotation(dir) => Some(dir)
case _ => None case _ => None
}.flatten }
dir.length match { dir.length match {
case 0 => "" case 0 => ""
case 1 => case 1 =>
val targetDir = new java.io.File(dir.head) 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 dir.head
case _ => throw new Exception("Only 1 tech directory annotation allowed!") case _ => throw new Exception("Only 1 tech directory annotation allowed!")
} }

View File

@@ -2,64 +2,74 @@
package barstools.tapeout.transforms.pads package barstools.tapeout.transforms.pads
import java.io.File
import barstools.tapeout.transforms.HasSetTechnologyLocation
import chisel3._ import chisel3._
import chisel3.experimental._
import chisel3.iotesters._
import chisel3.util.HasBlackBoxInline
import firrtl._ import firrtl._
import org.scalatest.{FlatSpec, Matchers} import org.scalatest.{FlatSpec, Matchers}
import chisel3.experimental._
import chisel3.util.HasBlackBoxInline
import chisel3.iotesters._
class BB extends BlackBox with HasBlackBoxInline { class BB extends BlackBox with HasBlackBoxInline {
val io = IO(new Bundle { val io = IO(new Bundle {
val c = Input(SInt(14.W)) val c = Input(SInt(14.W))
val z = Output(SInt(16.W)) val z = Output(SInt(16.W))
val analog1 = Analog(3.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 // Generates a "FakeBB.v" file with the following Verilog module
setInline("FakeBB.v", setInline(
"FakeBB.v",
s""" s"""
|module BB( |module BB(
| input [15:0] c, | input [15:0] c,
| output [15:0] z, | output [15:0] z,
| inout [2:0] analog1, | inout [2:0] analog1,
| inout [2:0] analog2 | inout [2:0] analog2
|); |);
| always @* begin | always @* begin
| z = 2 * c; | z = 2 * c;
| analog2 = analog1 + 1; | analog2 = analog1 + 1;
| end | end
|endmodule |endmodule
""".stripMargin) """.stripMargin
)
} }
// If no template file is provided, it'll use the default one (example) in the resource folder // 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 // 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 // You can designate the number of different supply pads on each chip side
class ExampleTopModuleWithBB extends TopModule( class ExampleTopModuleWithBB
supplyAnnos = Seq( extends TopModule(
SupplyAnnotation(padName = "vdd", leftSide = 3, bottomSide = 2), supplyAnnos = Seq(
SupplyAnnotation(padName = "vss", rightSide = 1) SupplyAnnotation(padName = "vdd", leftSide = 3, bottomSide = 2),
)) { SupplyAnnotation(padName = "vss", rightSide = 1)
)
)
with HasSetTechnologyLocation {
val io = IO(new Bundle { val io = IO(new Bundle {
val a = Input(UInt(15.W)) val a = Input(UInt(15.W))
val b = a.chiselCloneType val b = Input(a.cloneType)
val c = Input(SInt(14.W)) val c = Input(SInt(14.W))
val x = Output(UInt(16.W)) val x = Output(UInt(16.W))
val y = x.chiselCloneType val y = Output(x.cloneType)
val z = Output(SInt(16.W)) val z = Output(SInt(16.W))
val analog1 = Analog(3.W) val analog1 = Analog(3.W)
val analog2 = analog1.chiselCloneType val analog2 = analog1.cloneType
val v = Output(Vec(3, UInt(5.W))) 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) // Can annotate aggregates with pad side location + pad name (should be a name in the yaml template)
annotatePad(io.v, Right, "from_tristate_foundry") annotatePad(io.v, Right, "from_tristate_foundry")
// Can annotate individual elements // Can annotate individual elements
annotatePad(io.analog1, Left, "fast_custom") annotatePad(io.analog1, Left, "fast_custom")
annotatePad(io.analog2, Bottom, "slow_foundry") 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 // 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 // Some signals might not want pads associated with them
noPad(io.y) noPad(io.y)
// Clk might come directly from bump // Clk might come directly from bump
@@ -74,7 +84,7 @@ class ExampleTopModuleWithBB extends TopModule(
io.x := io.a + 1.U io.x := io.a + 1.U
io.y := io.b - 1.U io.y := io.b - 1.U
io.v foreach { lhs => lhs := io.a } io.v.foreach { lhs => lhs := io.a }
} }
@@ -89,7 +99,7 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c)
expect(c.io.x, ax(i) + 1) expect(c.io.x, ax(i) + 1)
expect(c.io.y, bx(i) - 1) expect(c.io.y, bx(i) - 1)
expect(c.io.z, 2 * cx(i)) 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
} }
@@ -122,61 +132,100 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c)
class IOPadSpec extends FlatSpec with Matchers { class IOPadSpec extends FlatSpec with Matchers {
def readOutputFile(dir: String, f: String): String = def readOutputFile(dir: String, f: String): String = {
scala.io.Source.fromFile(Seq(dir, f).mkString("/")).getLines.mkString("\n") FileUtils.getText(dir + File.separator + f)
}
def readResource(resource: String): String = { def readResource(resource: String): String = {
val stream = getClass.getResourceAsStream(resource) val stream = getClass.getResourceAsStream(resource)
scala.io.Source.fromInputStream(stream).mkString scala.io.Source.fromInputStream(stream).mkString
} }
def checkOutputs(dir: String) = { def checkOutputs(dir: String): Unit = {
// Show that black box source helper is run // Show that black box source helper is run
//readOutputFile(dir, "black_box_verilog_files.f") should include ("pad_supply_vdd_horizontal.v") //readOutputFile(dir, "black_box_verilog_files.f") should include ("pad_supply_vdd_horizontal.v")
val padBBEx = s"""// Digital Pad Example val padBBEx = s"""// Digital Pad Example
|// Signal Direction: Input |// Signal Direction: Input
|// Pad Orientation: Horizontal |// Pad Orientation: Horizontal
|// Call your instance PAD |// Call your instance PAD
|module pad_digital_from_tristate_foundry_horizontal_input( |module pad_digital_from_tristate_foundry_horizontal_input(
| input in, | input in,
| output reg out | output reg out
|); |);
| // Where you would normally dump your pad instance | // Where you would normally dump your pad instance
| always @* begin | always @* begin
| out = in; | out = in;
| end | end
|endmodule |endmodule
| |
|module pad_digital_from_tristate_foundry_horizontal_input_array #( |module pad_digital_from_tristate_foundry_horizontal_input_array #(
| parameter int WIDTH=1 | parameter int WIDTH=1
|)( |)(
| input [WIDTH-1:0] in, | input [WIDTH-1:0] in,
| output reg [WIDTH-1:0] out | output reg [WIDTH-1:0] out
|); |);
| pad_digital_from_tristate_foundry_horizontal_input pad_digital_from_tristate_foundry_horizontal_input[WIDTH-1:0]( | pad_digital_from_tristate_foundry_horizontal_input pad_digital_from_tristate_foundry_horizontal_input[WIDTH-1:0](
| .in(in), | .in(in),
| .out(out) | .out(out)
| );""".stripMargin | );""".stripMargin
// Make sure black box templating is OK // 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 // 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 // Pad Placement IO file should be exact
val padIO = readOutputFile(dir, "pads.io") val padIO = readOutputFile(dir, "pads.io")
padIO should include(readResource("/PadPlacement.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 { it should "pass simple testbench" in {
val optionsManager = new TesterOptionsManager { val optionsManager = new TesterOptionsManager {
firrtlOptions = firrtlOptions.copy( firrtlOptions = firrtlOptions.copy(
compilerName = "verilog" compilerName = "verilog"
// annotations = List(TechnologyLocation("./RealTech"))
) )
testerOptions = testerOptions.copy(isVerbose = true, backendName = "verilator", displayBase = 10) testerOptions = testerOptions.copy(isVerbose = true, backendName = "verilator", displayBase = 10)
commonOptions = commonOptions.copy(targetDirName = "test_run_dir/PadsTB") commonOptions = commonOptions.copy(targetDirName = "test_run_dir/PadsTB")
@@ -185,9 +234,9 @@ class IOPadSpec extends FlatSpec with Matchers {
val dir = optionsManager.commonOptions.targetDirName val dir = optionsManager.commonOptions.targetDirName
checkOutputs(dir) checkOutputs(dir)
new SimpleTopModuleTester(c) new SimpleTopModuleTester(c)
} should be (true) } should be(true)
} }
/* /*
it should "create proper IO pads + black box in low firrtl" in { it should "create proper IO pads + black box in low firrtl" in {
val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions { val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions {
firrtlOptions = firrtlOptions.copy(compilerName = "low") firrtlOptions = firrtlOptions.copy(compilerName = "low")
@@ -204,7 +253,7 @@ class IOPadSpec extends FlatSpec with Matchers {
} }
success should be (true) success should be (true)
} }
*/ */
it should "create proper IO pads + black box in verilog" in { it should "create proper IO pads + black box in verilog" in {
val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions { val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions {
firrtlOptions = firrtlOptions.copy( firrtlOptions = firrtlOptions.copy(
@@ -218,7 +267,7 @@ class IOPadSpec extends FlatSpec with Matchers {
true true
case _ => false case _ => false
} }
success should be (true) success should be(true)
val dir = optionsManager.commonOptions.targetDirName val dir = optionsManager.commonOptions.targetDirName
checkOutputs(dir) checkOutputs(dir)
} }

View File

@@ -3,7 +3,7 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import chisel3._ import chisel3._
import firrtl._ import chisel3.stage.ChiselStage
import org.scalatest.{FreeSpec, Matchers} import org.scalatest.{FreeSpec, Matchers}
class ExampleModuleNeedsResetInverted extends Module with ResetInverter { class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
@@ -19,22 +19,15 @@ class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
} }
class ResetNSpec extends FreeSpec with Matchers { class ResetNSpec extends FreeSpec with Matchers {
"Inverting reset needs to be done throughout module" in { "Inverting reset needs to be done throughout module" in {
val optionsManager = new ExecutionOptionsManager("dsptools") with HasChiselExecutionOptions with HasFirrtlOptions { val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted, Array())
firrtlOptions = firrtlOptions.copy(compilerName = "low", customTransforms = List(new ResetInverterTransform)), chirrtl should include("input reset :")
} (chirrtl should not).include("input reset_n :")
chisel3.Driver.execute(optionsManager, () => new ExampleModuleNeedsResetInverted) match { (chirrtl should not).include("node reset = not(reset_n)")
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)"
firrtl should include ("input reset_n :") val firrtl = (new ChiselStage).emitFirrtl(new ExampleModuleNeedsResetInverted, Array("-X", "low"))
firrtl should include ("node reset = not(reset_n)") firrtl should include("input reset_n :")
firrtl should not include "input reset :" firrtl should include("node reset = not(reset_n)")
case _ => (firrtl should not).include("input reset :")
// bad
}
} }
} }

View File

@@ -2,13 +2,12 @@
package barstools.tapeout.transforms.retime.test 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 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 { class RetimeSpec extends FlatSpec with Matchers {
def normalized(s: String): String = { def normalized(s: String): String = {
@@ -25,20 +24,47 @@ class RetimeSpec extends FlatSpec with Matchers {
it should "pass simple retime module annotation" in { it should "pass simple retime module annotation" in {
val gen = () => new RetimeModule() val gen = () => new RetimeModule()
val dir = uniqueDirName(gen, "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") Logger.makeScope(Seq.empty) {
lines should include("barstools.tapeout.transforms.retime.RetimeTransform") 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 it should "pass simple retime instance annotation" in {
ignore should "pass simple retime instance annotation" in {
val gen = () => new RetimeInstance() val gen = () => new RetimeInstance()
val dir = uniqueDirName(gen, "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 Logger.makeScope(Seq.empty) {
lines should contain ("Annotation(ComponentName(instance, ModuleName(RetimeInstance,CircuitName(RetimeInstance))),class barstools.tapeout.transforms.retime.RetimeTransform,retime)") 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"""")
} }
} }