From e4cd2b01fe5e4e89770a81443cf609657b683a67 Mon Sep 17 00:00:00 2001 From: chick Date: Thu, 10 Sep 2020 14:35:10 -0700 Subject: [PATCH] This is mess clean it up --- .scalafmt.conf | 26 ++ build.sbt | 4 +- macros/src/test/scala/MacroCompilerSpec.scala | 3 +- .../transforms/.clkgen/ClkAnnotations.scala | 247 ------------------ .../scala/transforms/.clkgen/ClkDivider.scala | 130 --------- .../transforms/.clkgen/ClkSrcTransform.scala | 34 --- .../.clkgen/CreateClkConstraints.scala | 152 ----------- .../transforms/.pads/ChiselTopModule.scala | 60 +++-- .../transforms/.pads/PadAnnotations.scala | 79 ++++-- .../scala/transforms/.clkgen/ClkGenSpec.scala | 181 ------------- 10 files changed, 120 insertions(+), 796 deletions(-) create mode 100644 .scalafmt.conf delete mode 100644 tapeout/src/main/scala/transforms/.clkgen/ClkAnnotations.scala delete mode 100644 tapeout/src/main/scala/transforms/.clkgen/ClkDivider.scala delete mode 100644 tapeout/src/main/scala/transforms/.clkgen/ClkSrcTransform.scala delete mode 100644 tapeout/src/main/scala/transforms/.clkgen/CreateClkConstraints.scala delete mode 100644 tapeout/src/test/scala/transforms/.clkgen/ClkGenSpec.scala diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 00000000..f74e5504 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,26 @@ +version = 2.6.4 + +maxColumn = 120 +align = most +continuationIndent.defnSite = 2 +assumeStandardLibraryStripMargin = true +docstrings = ScalaDoc +lineEndings = preserve +includeCurlyBraceInSelectChains = false +danglingParentheses = true + +align.tokens.add = [ + { + code = ":" + } +] + +newlines.alwaysBeforeCurlyBraceLambdaParams = false +newlines.alwaysBeforeMultilineDef = false +newlines.implicitParamListModifierForce = [before] + +verticalMultiline.atDefnSite = true + +optIn.annotationNewlines = true + +rewrite.rules = [SortImports, PreferCurlyFors, AvoidInfix] diff --git a/build.sbt b/build.sbt index 9ec44bf7..c01a71e6 100644 --- a/build.sbt +++ b/build.sbt @@ -1,8 +1,8 @@ // See LICENSE for license details. val defaultVersions = Map( - "chisel3" -> "3.2-SNAPSHOT", - "chisel-iotesters" -> "1.3-SNAPSHOT" + "chisel3" -> "3.4-SNAPSHOT", + "chisel-iotesters" -> "1.5-SNAPSHOT" ) lazy val commonSettings = Seq( diff --git a/macros/src/test/scala/MacroCompilerSpec.scala b/macros/src/test/scala/MacroCompilerSpec.scala index dfecc0c1..0bc0f486 100644 --- a/macros/src/test/scala/MacroCompilerSpec.scala +++ b/macros/src/test/scala/MacroCompilerSpec.scala @@ -1,9 +1,10 @@ +// See LICENSE for license details. + package barstools.macros import firrtl.ir.{Circuit, NoInfo} import firrtl.passes.RemoveEmpty import firrtl.Parser.parse -import firrtl.Utils.ceilLog2 import java.io.{File, StringWriter} import mdf.macrolib.SRAMMacro diff --git a/tapeout/src/main/scala/transforms/.clkgen/ClkAnnotations.scala b/tapeout/src/main/scala/transforms/.clkgen/ClkAnnotations.scala deleted file mode 100644 index 3a2f9ba3..00000000 --- a/tapeout/src/main/scala/transforms/.clkgen/ClkAnnotations.scala +++ /dev/null @@ -1,247 +0,0 @@ -package barstools.tapeout.transforms.clkgen - -import net.jcazevedo.moultingyaml._ -import firrtl.annotations._ -import chisel3.experimental._ -import chisel3._ -import firrtl._ -import firrtl.transforms.DedupModules - -object ClkAnnotationsYaml extends DefaultYamlProtocol { - implicit val _clksrc = yamlFormat3(ClkSrc) - implicit val _sink = yamlFormat1(Sink) - implicit val _clkport = yamlFormat2(ClkPortAnnotation) - implicit val _genclk = yamlFormat4(GeneratedClk) - implicit val _clkmod = yamlFormat2(ClkModAnnotation) -} -case class ClkSrc(period: Double, waveform: Seq[Double] = Seq(), async: Seq[String] = Seq()) { - def getWaveform = if (waveform == Seq.empty) Seq(0, period/2) else waveform - // async = ids of top level clocks that are async with this clk - // Default is 50% duty cycle, period units is default - require(getWaveform.sorted == getWaveform, "Waveform edges must be in order") - require(getWaveform.length == 2, "Must specify time for rising edge, then time for falling edge") -} - -case class Sink(src: Option[ClkSrc] = None) - -case class ClkPortAnnotation(tag: Option[Sink] = None, id: String) { - import ClkAnnotationsYaml._ - def serialize: String = this.toYaml.prettyPrint -} - -abstract class ClkModType { - def serialize: String -} -case object ClkMux extends ClkModType { - def serialize: String = "mux" -} -case object ClkDiv extends ClkModType { - def serialize: String = "div" -} -case object ClkGen extends ClkModType { - def serialize: String = "gen" -} - -// Unlike typical SDC, starts at 0. -// Otherwise, see pg. 63 of "Constraining Designs for Synthesis and Timing Analysis" -// by S. Gangadharan -// original clk: |-----|_____|-----|_____| -// edges: 0 1 2 3 4 -// div. by 4, 50% duty cycle --> edges = 0, 2, 4 -// ---> |-----------|___________| -// sources = source id's -case class GeneratedClk( - id: String, - sources: Seq[String] = Seq(), - referenceEdges: Seq[Int] = Seq(), - period: Option[Double] = None) { - require(referenceEdges.sorted == referenceEdges, "Edges must be in order for generated clk") - if (referenceEdges.nonEmpty) require(referenceEdges.length % 2 == 1, "# of reference edges must be odd!") -} - -case class ClkModAnnotation(tpe: String, generatedClks: Seq[GeneratedClk]) { - - def modType: ClkModType = HasClkAnnotation.modType(tpe) - - modType match { - case ClkDiv => - generatedClks foreach { c => - require(c.referenceEdges.nonEmpty, "Reference edges must be defined for clk divider!") - require(c.sources.length == 1, "Clk divider output can only have 1 source") - require(c.period.isEmpty, "No period should be specified for clk divider output") - } - case ClkMux => - generatedClks foreach { c => - require(c.referenceEdges.isEmpty, "Reference edges must not be defined for clk mux!") - require(c.period.isEmpty, "No period should be specified for clk mux output") - require(c.sources.nonEmpty, "Clk muxes must have sources!") - } - case ClkGen => - generatedClks foreach { c => - require(c.referenceEdges.isEmpty, "Reference edges must not be defined for clk gen!") - require(c.sources.isEmpty, "Clk generators shouldn't have constrained sources") - require(c.period.nonEmpty, "Clk generator output period should be specified!") - } - } - import ClkAnnotationsYaml._ - def serialize: String = this.toYaml.prettyPrint -} - -abstract class FirrtlClkTransformAnnotation { - def targetName: String -} - -// Firrtl version -case class TargetClkModAnnoF(target: ModuleName, anno: ClkModAnnotation) extends FirrtlClkTransformAnnotation with SingleTargetAnnotation[ModuleName] { - def duplicate(n: ModuleName): TargetClkModAnnoF = this.copy(target = n) - def getAnno = Annotation(target, classOf[ClkSrcTransform], anno.serialize) - def targetName = target.name - def modType = anno.modType - def generatedClks = anno.generatedClks - def getAllClkPorts = anno.generatedClks.map(x => - List(List(x.id), x.sources).flatten).flatten.distinct.map(Seq(targetName, _).mkString(".")) -} - -// Chisel version -case class TargetClkModAnnoC(target: Module, anno: ClkModAnnotation) extends ChiselAnnotation { - def toFirrtl = TargetClkModAnnoF(target.toNamed, anno) -} - -// Firrtl version -case class TargetClkPortAnnoF(target: ComponentName, anno: ClkPortAnnotation) extends FirrtlClkTransformAnnotation with SingleTargetAnnotation[ComponentName] { - def duplicate(n: ComponentName): TargetClkPortAnnoF = this.copy(target = n) - def getAnno = Annotation(target, classOf[ClkSrcTransform], anno.serialize) - def targetName = Seq(target.module.name, target.name).mkString(".") - def modId = Seq(target.module.name, anno.id).mkString(".") - def sink = anno.tag -} - -// Chisel version -case class TargetClkPortAnnoC(target: Element, anno: ClkPortAnnotation) extends ChiselAnnotation { - def toFirrtl = TargetClkPortAnnoF(target.toNamed, anno) -} - -object HasClkAnnotation { - - import ClkAnnotationsYaml._ - - def modType(tpe: String): ClkModType = tpe match { - case s: String if s == ClkMux.serialize => ClkMux - case s: String if s == ClkDiv.serialize => ClkDiv - case s: String if s == ClkGen.serialize => ClkGen - case _ => throw new Exception("Clock module annotaiton type invalid") - } - - def unapply(a: Annotation): Option[FirrtlClkTransformAnnotation] = a match { - case Annotation(f, t, s) if t == classOf[ClkSrcTransform] => f match { - case m: ModuleName => - Some(TargetClkModAnnoF(m, s.parseYaml.convertTo[ClkModAnnotation])) - case c: ComponentName => - Some(TargetClkPortAnnoF(c, s.parseYaml.convertTo[ClkPortAnnotation])) - case _ => throw new Exception("Clk source annotation only valid on module or component!") - } - case _ => None - } - - def apply(annos: Seq[Annotation]): Option[(Seq[TargetClkModAnnoF],Seq[TargetClkPortAnnoF])] = { - // Get all clk-related annotations - val clkAnnos = annos.map(x => unapply(x)).flatten - val targets = clkAnnos.map(x => x.targetName) - require(targets.distinct.length == targets.length, "Only 1 clk related annotation is allowed per component/module") - if (clkAnnos.length == 0) None - else { - val componentAnnos = clkAnnos.filter { - case TargetClkPortAnnoF(ComponentName(_, ModuleName(_, _)), _) => true - case _ => false - }.map(x => x.asInstanceOf[TargetClkPortAnnoF]) - val associatedMods = componentAnnos.map(x => x.target.module.name) - val moduleAnnos = clkAnnos.filter { - case TargetClkModAnnoF(ModuleName(m, _), _) => - require(associatedMods contains m, "Clk modules should always have clk port annotations!") - true - case _ => false - }.map(x => x.asInstanceOf[TargetClkModAnnoF]) - Some((moduleAnnos, componentAnnos)) - } - } - -} - -// Applies to both black box + normal module -trait IsClkModule { - - self: chisel3.Module => - - doNotDedup(this) - - private def extractElementNames(signal: Data): Seq[String] = { - val names = signal match { - case elt: Record => - elt.elements.map { case (key, value) => extractElementNames(value).map(x => key + "_" + x) }.toSeq.flatten - case elt: Vec[_] => - elt.zipWithIndex.map { case (elt, i) => extractElementNames(elt).map(x => i + "_" + x) }.toSeq.flatten - case elt: Element => Seq("") - case elt => throw new Exception(s"Cannot extractElementNames for type ${elt.getClass}") - } - names.map(s => s.stripSuffix("_")) - } - - // TODO: Replace! - def extractElements(signal: Data): Seq[Element] = { - signal match { - case elt: Record => - elt.elements.map { case (key, value) => extractElements(value) }.toSeq.flatten - case elt: Vec[_] => - elt.map { elt => extractElements(elt) }.toSeq.flatten - case elt: Element => Seq(elt) - case elt => throw new Exception(s"Cannot extractElements for type ${elt.getClass}") - } - } - - def getIOName(signal: Element): String = { - val possibleNames = extractElements(io).zip(extractElementNames(io)).map { - case (sig, name) if sig == signal => Some(name) - case _ => None - }.flatten - if (possibleNames.length == 1) possibleNames.head - else throw new Exception("You can only get the name of an io port!") - } - - def annotateDerivedClks(tpe: ClkModType, generatedClks: Seq[GeneratedClk]): Unit = - annotateDerivedClks(ClkModAnnotation(tpe.serialize, generatedClks)) - def annotateDerivedClks(anno: ClkModAnnotation): Unit = annotateDerivedClks(this, anno) - def annotateDerivedClks(m: Module, anno: ClkModAnnotation): Unit = - annotate(TargetClkModAnnoC(m, anno)) - - def annotateClkPort(p: Element): Unit = annotateClkPort(p, None, "") - def annotateClkPort(p: Element, sink: Sink): Unit = annotateClkPort(p, Some(sink), "") - def annotateClkPort(p: Element, id: String): Unit = annotateClkPort(p, None, id) - def annotateClkPort(p: Element, sink: Sink, id: String): Unit = annotateClkPort(p, Some(sink), id) - def annotateClkPort(p: Element, sink: Option[Sink], id: String): Unit = { - // If no id is specified, it'll try to figure out a name, assuming p is an io port - val newId = id match { - case "" => - getIOName(p) - case _ => id - } - annotateClkPort(p, ClkPortAnnotation(sink, newId)) - } - - def annotateClkPort(p: Element, anno: ClkPortAnnotation): Unit = { - DataMirror.directionOf(p) match { - case chisel3.core.ActualDirection.Input => - require(anno.tag.nonEmpty, "Module inputs must be clk sinks") - require(anno.tag.get.src.isEmpty, - "Clock module (not top) input clks should not have clk period, etc. specified") - case chisel3.core.ActualDirection.Output => - require(anno.tag.isEmpty, "Module outputs must not be clk sinks (they're sources!)") - case _ => - throw new Exception("Clk port direction must be specified!") - } - p match { - case _: chisel3.core.Clock => - case _ => throw new Exception("Clock port must be of type Clock") - } - annotate(TargetClkPortAnnoC(p, anno)) - } -} diff --git a/tapeout/src/main/scala/transforms/.clkgen/ClkDivider.scala b/tapeout/src/main/scala/transforms/.clkgen/ClkDivider.scala deleted file mode 100644 index 23402982..00000000 --- a/tapeout/src/main/scala/transforms/.clkgen/ClkDivider.scala +++ /dev/null @@ -1,130 +0,0 @@ -package barstools.tapeout.transforms.clkgen - -import chisel3.experimental.{withClockAndReset, withClock, withReset} -import chisel3._ -import barstools.tapeout.transforms._ -import chisel3.util.HasBlackBoxInline - -// WARNING: ONLY WORKS WITH VERILATOR B/C YOU NEED ASYNC RESET! - -class SEClkDividerIO(phases: Seq[Int]) extends Bundle { - val reset = Input(Bool()) - val inClk = Input(Clock()) - val outClks = Output(CustomIndexedBundle(Clock(), phases)) - override def cloneType = (new SEClkDividerIO(phases)).asInstanceOf[this.type] -} - -class SEClkDividerBB(phases: Seq[Int], f: String) extends BlackBox with HasBlackBoxInline { - val verilog = scala.io.Source.fromFile(f).getLines.mkString("\n") - // names without io - val io = IO(new SEClkDividerIO(phases)) - val modName = this.getClass.getSimpleName - require(verilog contains modName, "Clk divider Verilog module must be named ClkDividerBB") - io.elements foreach { case (field, elt) => - require(verilog contains field, s"Verilog file should contain io ${field}")} - setInline(s"${modName}.v", verilog) -} - -class AsyncRegInit extends BlackBox with HasBlackBoxInline { - val io = IO(new Bundle { - val clk = Input(Clock()) - val reset = Input(Bool()) - val init = Input(Bool()) - val in = Input(Bool()) - val out = Output(Bool()) - }) - - setInline("AsyncRegInit.v", - s""" - |module AsyncRegInit( - | input clk, - | input reset, - | input init, - | input in, - | output reg out - |); - | always @ (posedge clk or posedge reset) begin - | if (reset) begin - | out <= init; - | end else begin - | out <= in; - | end - | end - |endmodule - """.stripMargin) -} - -object AsyncRegInit { - def apply(clk: Clock, reset: Bool, init: Bool): AsyncRegInit = { - val asyncRegInit = Module(new AsyncRegInit) - asyncRegInit.io.clk := clk - asyncRegInit.io.reset := reset - asyncRegInit.io.init := init - asyncRegInit - } -} - -// TODO: Convert analogFile into implicit? -// If syncReset = false, it's implied that reset is strobed before any clk rising edge happens -// i.e. when this is a clkgen fed by another clkgen --> need to adjust the indexing b/c -// you're already shifting on the first clk rising edge -class SEClkDivider(divBy: Int, phases: Seq[Int], analogFile: String = "", syncReset: Boolean = true) - extends Module with IsClkModule { - - require(phases.distinct.length == phases.length, "Phases should be distinct!") - phases foreach { p => - require(p < divBy, "Phases must be < divBy") - } - - val io = IO(new SEClkDividerIO(phases)) - - annotateClkPort(io.inClk, Sink()) - - val referenceEdges = phases.map(p => Seq(2 * p, 2 * (p + 1), 2 * (p + divBy))) - - val generatedClks = io.outClks.elements.zip(referenceEdges).map { case ((field, eltx), edges) => - val elt = eltx.asInstanceOf[Element] - annotateClkPort(elt) - GeneratedClk(getIOName(elt), sources = Seq(getIOName(io.inClk)), edges) - }.toSeq - - annotateDerivedClks(ClkDiv, generatedClks) - - require(divBy >= 1, "Clk division factor must be >= 1") - - divBy match { - case i: Int if i == 1 => - require(phases == Seq(0), "Clk division by 1 shouldn't generate new phases") - io.outClks(0) := io.inClk - case i: Int if i > 1 && analogFile == "" => - // Shift register based clock divider (duty cycle is NOT 50%) - val initVals = Seq(true.B) ++ Seq.fill(divBy - 1)(false.B) - - /************ Real design assumes asnyc reset!!! - withClockAndReset(io.inClk, io.reset) { - val regs = initVals.map(i => RegInit(i)) - // Close the loop - regs.head := regs.last - // Shift register - regs.tail.zip(regs.init) foreach { case (lhs, rhs) => lhs := rhs } - // Assign register output to correct clk out - phases foreach { idx => io.outClks(idx) := regs(idx).asClock } - } - *************/ - - val regs = initVals.map(i => AsyncRegInit(io.inClk, io.reset, i)) - regs.head.io.in := regs.last.io.out - regs.tail.zip(regs.init) foreach { case (lhs, rhs) => lhs.io.in := rhs.io.out } - phases foreach { idx => - val regIdx = if (syncReset) idx else (idx + 1) % divBy - io.outClks(idx) := regs(regIdx).io.out.asClock - } - - case _ => - if (new java.io.File(analogFile).exists) { - val bb = Module(new SEClkDividerBB(phases, analogFile)) - io <> bb.io - } - else throw new Exception("Clock divider Verilog file invalid!") - } -} diff --git a/tapeout/src/main/scala/transforms/.clkgen/ClkSrcTransform.scala b/tapeout/src/main/scala/transforms/.clkgen/ClkSrcTransform.scala deleted file mode 100644 index 0f5dc571..00000000 --- a/tapeout/src/main/scala/transforms/.clkgen/ClkSrcTransform.scala +++ /dev/null @@ -1,34 +0,0 @@ -// See LICENSE for license details. - -package barstools.tapeout.transforms.clkgen - -import firrtl._ -import firrtl.passes._ - -import scala.collection.mutable - -class ClkSrcTransform extends Transform with SeqTransformBased { - - override def inputForm: CircuitForm = LowForm - override def outputForm: CircuitForm = LowForm - - val transformList = new mutable.ArrayBuffer[Transform] - def transforms = transformList - - override def execute(state: CircuitState): CircuitState = { - val collectedAnnos = HasClkAnnotation(getMyAnnotations(state)) - collectedAnnos match { - // Transform not used - case None => CircuitState(state.circuit, LowForm) - case Some((clkModAnnos, clkPortAnnos)) => - val targetDir = barstools.tapeout.transforms.GetTargetDir(state) - - transformList ++= Seq( - // InferTypes, - new CreateClkConstraints(clkModAnnos, clkPortAnnos, targetDir) - ) - val ret = runTransforms(state) - CircuitState(ret.circuit, outputForm, ret.annotations, ret.renames) - } - } -} diff --git a/tapeout/src/main/scala/transforms/.clkgen/CreateClkConstraints.scala b/tapeout/src/main/scala/transforms/.clkgen/CreateClkConstraints.scala deleted file mode 100644 index 6975eb73..00000000 --- a/tapeout/src/main/scala/transforms/.clkgen/CreateClkConstraints.scala +++ /dev/null @@ -1,152 +0,0 @@ -// See license file for details - -package barstools.tapeout.transforms.clkgen - -import firrtl.passes.clocklist._ -import firrtl.annotations._ -import firrtl.ir._ -import firrtl.Utils._ -import barstools.tapeout.transforms._ -import scala.collection.immutable.ListMap - -// TODO: Really should be moved out of memlib -import firrtl.passes.memlib.AnalysisUtils._ -import firrtl.passes._ - -// TODO: Wait until Albert merges into firrtl -import firrtl.analyses._ - -class CreateClkConstraints( - clkModAnnos: Seq[TargetClkModAnnoF], - clkPortAnnos: Seq[TargetClkPortAnnoF], - targetDir: String) extends Pass { - - // TODO: Are annotations only valid on ports? - - def run(c: Circuit): Circuit = { -/* - val top = c.main - - // Remove everything from the circuit, unless it has a clock type - // This simplifies the circuit drastically so InlineInstances doesn't take forever. - val onlyClockCircuit = RemoveAllButClocks.run(c) - - val instanceGraph = new InstanceGraph(onlyClockCircuit) - - val clkModNames = clkModAnnos.map(x => x.targetName) - // ** Module name -> Absolute path of (unique) instance - val clkMods = clkModNames.map { x => - // NoDeDup was run so only 1 instance of each module should exist - val inst = instanceGraph.findInstancesInHierarchy(x) - require(inst.length == 1, "Clk modules should have not ben dedup-ed") - // Return map of module name to absolute path as a string - // Note: absolute path doesn't contain top module + to work with inlineInstances, - // delimit with $ - x -> inst.head.tail.map(y => y.name).mkString("$") - }.toMap - - val clkPortIds = clkPortAnnos.map { a => a.modId } - require(clkPortIds.distinct.length == clkPortIds.length, "All clk port IDs must be unique!") - - val allModClkPorts = clkModAnnos.map { x => - val modClkPorts = x.getAllClkPorts - require(modClkPorts.intersect(clkPortIds).length == modClkPorts.length, - "Clks given relationships via clk modules must have been annotated as clk ports") - modClkPorts - }.flatten.distinct - - val clkPortMap = clkPortIds.zip(clkPortAnnos).toMap - val clkModMap = clkModNames.zip(clkModAnnos).toMap - - val (clkSinksTemp, clkSrcsTemp) = clkPortAnnos.partition { - case TargetClkPortAnnoF(_, ClkPortAnnotation(tag, _)) if tag.nonEmpty => true - case _ => false - } - - def convertClkPortAnnoToMap(annos: Seq[TargetClkPortAnnoF]): ListMap[String, String] = - ListMap(annos.map { x => - val target = x.target - val absPath = { - if (top == target.module.name) LowerName(target.name) - else Seq(clkMods(target.module.name), LowerName(target.name)).mkString(".") - } - x.modId -> absPath - }.sortBy(_._1): _*) - - // ** clk port -> absolute path - val clkSinks = convertClkPortAnnoToMap(clkSinksTemp) - val clkSrcs = convertClkPortAnnoToMap(clkSrcsTemp) - - clkSrcs foreach { case (id, path) => - require(allModClkPorts contains id, "All clock source properties must be defined by their respective modules") } - - // Don't inline clock modules - val modulesToInline = (c.modules.collect { - case Module(_, n, _, _) if n != top && !clkModNames.contains(n) => - ModuleName(n, CircuitName(top)) - }).toSet - - val inlineTransform = new InlineInstances - val inlinedCircuit = inlineTransform.run(onlyClockCircuit, modulesToInline, Set(), None).circuit - - val topModule = inlinedCircuit.modules.find(_.name == top).getOrElse(throwInternalError) - - // Build a hashmap of connections to use for getOrigins - val connects = getConnects(topModule) - - // Clk sinks are either inputs to clock modules or top clk inputs --> separate - // ** clk port -> absolute path - val (topClks, clkModSinks) = clkSinks.partition { - case (modId, absPath) if modId.split("\\.").head == top => true - case _ => false - } - - // Must be 1:1 originally! - def flipMapping(m: ListMap[String, String]): ListMap[String, String] = - m.map { case (a, b) => b -> a } - - val clkSrcsFlip = flipMapping(clkSrcs) - val topClksFlip = flipMapping(topClks) - - // Find origins of clk mod sinks - val clkModSinkToSourceMap = clkModSinks.map { case (sinkId, sinkAbsPath) => - val sourceAbsPath = getOrigin(connects, sinkAbsPath).serialize - val sourceId = { - // sources of sinks are generated clks or top level clk inputs - if (clkSrcsFlip.contains(sourceAbsPath)) clkSrcsFlip(sourceAbsPath) - else if (topClksFlip.contains(sourceAbsPath)) topClksFlip(sourceAbsPath) - else throw new Exception(s"Absolute path $sourceAbsPath of clk source for $sinkId not found!") - } - sinkId -> sourceId - } - - c.modules.foreach { - case mod: DefModule => - mod.ports.foreach { - case Port(_, n, dir, tpe) - if tpe == ClockType && - ((dir == Input && mod.name == top) || (dir == Output && clkModNames.contains(mod.name))) => - clkPortAnnos.find(x => - // TODO: Not sufficiently general for output clks? Might have forgotten to label a clk module... - LowerName(x.target.name) == n && x.target.module.name == mod.name).getOrElse( - throw new Exception( - s"All top module input clks/clk module output clocks must be sinks/sources! $n not annotated!")) - case _ => - } - } - - // Find sinks used to derive clk mod sources - val clkModSourceToSinkMap: Seq[(String, Seq[String])] = clkModAnnos.map(x => { - val modName = x.targetName - x.generatedClks.map(y => Seq(modName, y.id).mkString(".") -> y.sources.map(z => Seq(modName, z).mkString("."))) - } ).flatten - - topClks.foreach {x => println(s"top clk: $x")} - clkModSinks.foreach { x => println(s"clk sink: $x")} - clkSrcs.foreach { x => println(s"gen clk: $x")} - clkModSinkToSourceMap.foreach { x => println(s"sink -> src: $x")} - clkModSourceToSinkMap.foreach { x => println(s"src -> dependent sinks: $x")} -*/ - c - } -} \ No newline at end of file diff --git a/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala b/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala index 36979ce5..a348ff72 100644 --- a/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala +++ b/tapeout/src/main/scala/transforms/.pads/ChiselTopModule.scala @@ -1,9 +1,11 @@ +// See LICENSE for license details. + package barstools.tapeout.transforms.pads import chisel3._ -import barstools.tapeout.transforms.clkgen._ import chisel3.experimental._ -import firrtl.transforms.DedupModules +import firrtl.Transform +import firrtl.annotations.Annotation // TODO: Move out of pads @@ -20,29 +22,12 @@ abstract class TopModule( coreHeight: Int = 0, usePads: Boolean = true, override_clock: Option[Clock] = None, - override_reset: Option[Bool] = None) extends Module with IsClkModule { + override_reset: Option[Bool] = None) extends Module { override_clock.foreach(clock := _) override_reset.foreach(reset := _) - override def annotateClkPort(p: Element, anno: ClkPortAnnotation): Unit = { - DataMirror.directionOf(p) match { - case chisel3.core.ActualDirection.Input => - require(anno.tag.nonEmpty, "Top Module input clks must be clk sinks") - require(anno.tag.get.src.nonEmpty, - "Top module input clks must have clk period, etc. specified") - case _ => - throw new Exception("Clk port direction must be specified!") - } - p match { - case _: chisel3.core.Clock => - case _ => throw new Exception("Clock port must be of type Clock") - } - annotate(TargetClkPortAnnoC(p, anno)) - } - - override def annotateDerivedClks(m: Module, anno: ClkModAnnotation): Unit = - throw new Exception("Top module cannot be pure clock module!") + private val mySelf = this // Annotate module as top module (that requires pad transform) // Specify the yaml file that indicates how pads are templated, @@ -55,7 +40,38 @@ abstract class TopModule( coreHeight = coreHeight, supplyAnnos = supplyAnnos ) - annotate(TargetModulePadAnnoC(this, modulePadAnnotation)) + //TODO: PORT-1.4: Remove commented code + // annotate(TargetModulePadAnnoC(this, modulePadAnnotation)) + annotate(new ChiselAnnotation with RunFirrtlTransform { + override def toFirrtl: Annotation = { + TargetModulePadAnnoF(mySelf.toNamed, modulePadAnnotation) + } + def transformClass: Class[_ <: Transform] = classOf[AddIOPadsTransform] + }) + } + + private def extractElementNames(signal: Data): Seq[String] = { + val names = signal match { + case elt: Record => + elt.elements.map { case (key, value) => extractElementNames(value).map(x => key + "_" + x) }.toSeq.flatten + case elt: Vec[_] => + elt.zipWithIndex.map { case (elt, i) => extractElementNames(elt).map(x => i + "_" + x) }.toSeq.flatten + case elt: Element => Seq("") + case elt => throw new Exception(s"Cannot extractElementNames for type ${elt.getClass}") + } + names.map(s => s.stripSuffix("_")) + } + + // TODO: Replace! + def extractElements(signal: Data): Seq[Element] = { + signal match { + case elt: Record => + elt.elements.map { case (key, value) => extractElements(value) }.toSeq.flatten + case elt: Vec[_] => + elt.map { elt => extractElements(elt) }.toSeq.flatten + case elt: Element => Seq(elt) + case elt => throw new Exception(s"Cannot extractElements for type ${elt.getClass}") + } } // Annotate IO with side + pad name diff --git a/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala b/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala index ed870092..c1f2d783 100644 --- a/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala +++ b/tapeout/src/main/scala/transforms/.pads/PadAnnotations.scala @@ -1,10 +1,10 @@ +// See LICENSE for license details. + package barstools.tapeout.transforms.pads import firrtl.annotations._ import chisel3.experimental._ import chisel3._ -import barstools.tapeout.transforms._ -import firrtl._ import net.jcazevedo.moultingyaml._ @@ -23,26 +23,32 @@ abstract class FirrtlPadTransformAnnotation { abstract class IOAnnotation { def serialize: String } + case class IOPadAnnotation(padSide: String, padName: String) extends IOAnnotation { import PadAnnotationsYaml._ def serialize: String = this.toYaml.prettyPrint def getPadSide: PadSide = HasPadAnnotation.getSide(padSide) } + case class NoIOPadAnnotation(noPad: String = "") extends IOAnnotation { import PadAnnotationsYaml._ def serialize: String = this.toYaml.prettyPrint - def field = "noPad:" + def field: String = "noPad:" } + // Firrtl version -case class TargetIOPadAnnoF(target: ComponentName, anno: IOAnnotation) extends FirrtlPadTransformAnnotation with SingleTargetAnnotation[ComponentName] { +case class TargetIOPadAnnoF(target: ComponentName, anno: IOAnnotation) + extends FirrtlPadTransformAnnotation with SingleTargetAnnotation[ComponentName] { + def duplicate(n: ComponentName): TargetIOPadAnnoF = this.copy(target = n) - def getAnno = Annotation(target, classOf[AddIOPadsTransform], anno.serialize) - def targetName = 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) -} +//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( @@ -51,6 +57,7 @@ case class SupplyAnnotation( rightSide: Int = 0, topSide: Int = 0, bottomSide: Int = 0) + // The chip top should have a default pad side, a pad template file, and supply annotations case class ModulePadAnnotation( defaultPadSide: String = Top.serialize, @@ -63,17 +70,16 @@ case class ModulePadAnnotation( require(supplyPadNames.distinct.length == supplyPadNames.length, "Supply pads should only be specified once!") def getDefaultPadSide: PadSide = HasPadAnnotation.getSide(defaultPadSide) } + // Firrtl version -case class TargetModulePadAnnoF(target: ModuleName, anno: ModulePadAnnotation) extends FirrtlPadTransformAnnotation with SingleTargetAnnotation[ModuleName] { +case class TargetModulePadAnnoF(target: ModuleName, anno: ModulePadAnnotation) + extends FirrtlPadTransformAnnotation with SingleTargetAnnotation[ModuleName] { + def duplicate(n: ModuleName): TargetModulePadAnnoF = this.copy(target = n) - def getAnno = Annotation(target, classOf[AddIOPadsTransform], anno.serialize) - def targetName = target.name -} -// Chisel version -case class TargetModulePadAnnoC(target: Module, anno: ModulePadAnnotation) extends ChiselAnnotation { - def toFirrtl = TargetModulePadAnnoF(target.toNamed, anno) + def targetName: String = target.name } + case class CollectedAnnos( componentAnnos: Seq[TargetIOPadAnnoF], moduleAnnos: TargetModulePadAnnoF) { @@ -95,16 +101,34 @@ object HasPadAnnotation { case _ => throw new Exception(s" $a not a valid pad side annotation!") } + //TODO: PORT-1.4: Remove commented code +// def unapply(a: Annotation): Option[FirrtlPadTransformAnnotation] = a match { +// case Annotation(f, t, s) if t == classOf[AddIOPadsTransform] => f match { +// case m: ModuleName => +// Some(TargetModulePadAnnoF(m, s.parseYaml.convertTo[ModulePadAnnotation])) +// case c: ComponentName if s.contains(NoIOPadAnnotation().field) => +// Some(TargetIOPadAnnoF(c, s.parseYaml.convertTo[NoIOPadAnnotation])) +// case c: ComponentName => +// Some(TargetIOPadAnnoF(c, s.parseYaml.convertTo[IOPadAnnotation])) +// case _ => throw new Exception("Annotation only valid on module or component") +// } +// case _ => None +// } + + //scalastyle:off cyclomatic.complexity def unapply(a: Annotation): Option[FirrtlPadTransformAnnotation] = a match { - case Annotation(f, t, s) if t == classOf[AddIOPadsTransform] => f match { - case m: ModuleName => - Some(TargetModulePadAnnoF(m, s.parseYaml.convertTo[ModulePadAnnotation])) - case c: ComponentName if s.contains(NoIOPadAnnotation().field) => - Some(TargetIOPadAnnoF(c, s.parseYaml.convertTo[NoIOPadAnnotation])) - case c: ComponentName => - Some(TargetIOPadAnnoF(c, s.parseYaml.convertTo[IOPadAnnotation])) - case _ => throw new Exception("Annotation only valid on module or component") - } + 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 } @@ -113,8 +137,9 @@ object HasPadAnnotation { val padAnnos = annos.map(x => unapply(x)).flatten 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) None - else { + if (padAnnos.length == 0) { + None + } else { val moduleAnnosTemp = padAnnos.filter { case TargetModulePadAnnoF(_, _) => true case _ => false diff --git a/tapeout/src/test/scala/transforms/.clkgen/ClkGenSpec.scala b/tapeout/src/test/scala/transforms/.clkgen/ClkGenSpec.scala deleted file mode 100644 index 17ae1c76..00000000 --- a/tapeout/src/test/scala/transforms/.clkgen/ClkGenSpec.scala +++ /dev/null @@ -1,181 +0,0 @@ -// See LICENSE for license details. - -package barstools.tapeout.transforms.clkgen - -import chisel3._ -import firrtl._ -import org.scalatest.{FlatSpec, Matchers} -import chisel3.experimental._ -import chisel3.iotesters._ -import chisel3.util.HasBlackBoxInline -import barstools.tapeout.transforms.pads.TopModule - -// Purely to see that clk src tagging works with BBs -class FakeBBClk extends BlackBox with HasBlackBoxInline with IsClkModule { - val io = IO(new Bundle { - val inClk = Input(Clock()) - val outClk = Output(Vec(3, Clock())) - }) - - annotateClkPort(io.inClk, Sink()) - val generatedClks = io.outClk.map { case elt => - val id = getIOName(elt) - val srcId = getIOName(io.inClk) - annotateClkPort(elt.asInstanceOf[Element]) - GeneratedClk(id, Seq(srcId), Seq(0, 1, 2)) - }.toSeq - - annotateDerivedClks(ClkDiv, generatedClks) - - // Generates a "FakeBB.v" file with the following Verilog module - setInline("FakeBBClk.v", - s""" - |module FakeBBClk( - | input inClk, - | output outClk_0, - | output outClk_1, - | output outClk_2 - |); - | always @* begin - | outClk_0 = inClk; - | outClk_1 = inClk; - | outClk_2 = inClk; - | end - |endmodule - """.stripMargin) -} - -class ModWithNestedClkIO(numPhases: Int) extends Bundle { - val inClk = Input(Clock()) - val bbOutClk = Output(Vec(3, Clock())) - val clkDivOut = Output(Vec(numPhases, Clock())) -} - -class TestModWithNestedClkIO(numPhases: Int) extends Bundle { - val bbOutClk = Output(Vec(3, Bool())) - val clkDivOut = Output(Vec(numPhases, Bool())) -} - -class ModWithNestedClk(divBy: Int, phases: Seq[Int], syncReset: Boolean) extends Module { - - val io = IO(new ModWithNestedClkIO(phases.length)) - - val bb = Module(new FakeBBClk) - bb.io.inClk := io.inClk - io.bbOutClk := bb.io.outClk - val clkDiv = Module(new SEClkDivider(divBy, phases, syncReset = syncReset)) - clkDiv.io.reset := reset - clkDiv.io.inClk := io.inClk - phases.zipWithIndex.foreach { case (phase, idx) => io.clkDivOut(idx) := clkDiv.io.outClks(phase) } - -} - -class TopModuleWithClks(val divBy: Int, val phases: Seq[Int]) extends TopModule(usePads = false) { - val io = IO(new Bundle { - val gen1 = new TestModWithNestedClkIO(phases.length) - val gen2 = new TestModWithNestedClkIO(phases.length) - val gen3 = new TestModWithNestedClkIO(phases.length) - val fakeClk1 = Input(Clock()) - val fakeClk2 = Input(Clock()) - }) - - // TODO: Don't have to type Some - annotateClkPort(clock, - id = "clock", // not in io bundle - sink = Sink(Some(ClkSrc(period = 5.0, async = Seq(getIOName(io.fakeClk1))))) - ) - annotateClkPort(io.fakeClk1, Sink(Some(ClkSrc(period = 4.0)))) - annotateClkPort(io.fakeClk2, Sink(Some(ClkSrc(period = 3.0)))) - - // Most complicated: test chain of clock generators - val gen1 = Module(new ModWithNestedClk(divBy, phases, syncReset = true)) - io.gen1.bbOutClk := Vec(gen1.io.bbOutClk.map(x => x.asUInt)) - io.gen1.clkDivOut := Vec(gen1.io.clkDivOut.map(x => x.asUInt)) - gen1.io.inClk := clock - // ClkDiv on generated clk -> reset occurs before first input clk edge - val gen2 = Module(new ModWithNestedClk(divBy, phases, syncReset = false)) - io.gen2.bbOutClk := Vec(gen2.io.bbOutClk.map(x => x.asUInt)) - io.gen2.clkDivOut := Vec(gen2.io.clkDivOut.map(x => x.asUInt)) - gen2.io.inClk := gen1.io.clkDivOut.last - val gen3 = Module(new ModWithNestedClk(divBy, phases, syncReset = false)) - io.gen3.bbOutClk := Vec(gen3.io.bbOutClk.map(x => x.asUInt)) - io.gen3.clkDivOut := Vec(gen3.io.clkDivOut.map(x => x.asUInt)) - gen3.io.inClk := gen1.io.clkDivOut.last -} - -class TopModuleWithClksTester(c: TopModuleWithClks) extends PeekPokeTester(c) { - val maxT = c.divBy * c.divBy * 4 - val numSubClkOutputs = c.io.gen1.clkDivOut.length - val gen1Out = Seq.fill(numSubClkOutputs)(scala.collection.mutable.ArrayBuffer[Int]()) - val gen2Out = Seq.fill(numSubClkOutputs)(scala.collection.mutable.ArrayBuffer[Int]()) - val gen3Out = Seq.fill(numSubClkOutputs)(scala.collection.mutable.ArrayBuffer[Int]()) - reset(10) - for (t <- 0 until maxT) { - for (k <- 0 until numSubClkOutputs) { - gen1Out(k) += peek(c.io.gen1.clkDivOut(k)).intValue - gen2Out(k) += peek(c.io.gen2.clkDivOut(k)).intValue - gen3Out(k) += peek(c.io.gen3.clkDivOut(k)).intValue - } - step(1) - } - - val clkCounts = (0 until maxT) - val clkCountsModDiv = clkCounts.map(_ % c.divBy) - for (k <- 0 until numSubClkOutputs) { - val expected = clkCountsModDiv.map(x => if (x == c.phases(k)) 1 else 0) - expect(gen1Out(k) == expected, s"gen1Out($k) incorrect!") - println(s"gen1Out($k): \t${gen1Out(k).mkString("")}") - } - - val gen1ClkCounts = (0 until maxT/c.divBy).map(i => Seq.fill(c.divBy)(i)).flatten - val gen1ClkCountsModDiv = gen1ClkCounts.map(_ % c.divBy) - - for (k <- 0 until numSubClkOutputs) { - // Handle initial transient - val fillVal = if (c.phases.last == c.divBy - 1 && k == numSubClkOutputs - 1) 1 else 0 - val expected = Seq.fill(c.phases.last)(fillVal) ++ - gen1ClkCountsModDiv.map(x => if (x == c.phases(k)) 1 else 0).dropRight(c.phases.last) - expect(gen2Out(k) == expected, s"gen1Out($k) incorrect!") - println(s"gen2Out($k): \t${gen2Out(k).mkString("")}") - println(s"expected: \t${expected.mkString("")}") - } - - expect(gen2Out == gen3Out, "gen2Out should equal gen3Out") - -} - -class ClkGenSpec extends FlatSpec with Matchers { - - def readOutputFile(dir: String, f: String): String = - scala.io.Source.fromFile(Seq(dir, f).mkString("/")).getLines.mkString("\n") - def readResource(resource: String): String = { - val stream = getClass.getResourceAsStream(resource) - scala.io.Source.fromInputStream(stream).mkString - } - - def checkOutputs(dir: String) = { - } - - behavior of "top module with clk gens" - - it should "pass simple testbench" in { - val optionsManager = new TesterOptionsManager { - firrtlOptions = firrtlOptions.copy( - compilerName = "verilog" - /*annotations = List(passes.clocklist.ClockListAnnotation( - s"-c:TopModuleWithClks:-m:TopModuleWithClks:-o:test.clk" - )), - customTransforms = Seq(new passes.clocklist.ClockListTransform())*/ - ) - testerOptions = testerOptions.copy(isVerbose = false, backendName = "verilator", displayBase = 10) - commonOptions = commonOptions.copy(targetDirName = "test_run_dir/ClkTB") - } - // WARNING: TB requires that phase divBy - 1 should be at the end of the Seq to be OK during initial transient - iotesters.Driver.execute(() => new TopModuleWithClks(4, Seq(0, 1, 3)), optionsManager) { c => - val dir = optionsManager.commonOptions.targetDirName - checkOutputs(dir) - new TopModuleWithClksTester(c) - } should be (true) - } - -} \ No newline at end of file