This is mess clean it up
This commit is contained in:
26
.scalafmt.conf
Normal file
26
.scalafmt.conf
Normal 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]
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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!")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user