This is mess clean it up

This commit is contained in:
chick
2020-09-10 14:35:10 -07:00
parent f791073f02
commit e4cd2b01fe
10 changed files with 120 additions and 796 deletions

26
.scalafmt.conf Normal file
View File

@@ -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]

View File

@@ -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(

View File

@@ -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

View File

@@ -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))
}
}

View File

@@ -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!")
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}