Add Pads + other utilities (#7)

[stevo]: adds a bunch of pad frame commits, as well as beginning work on clocking annotations and constraints


* start add io pads pass

* save progress adding yaml pad info

* saving some semi-presentable work -- parses yaml for pad templates and associates templates with ports

* added black boxes to the module; still need to hook up

* added supply pad yaml example; added option to not include pad for an IO, blackboxed that cat + bit extraction functions

* rewrite createbbs and some other parts of the transform

* finally got blackboxhelper to work -- seems there was a typo in the firrtl pass (?) have not connected them up properly in the padframe

* finished first version of pad transform; need to add bells and whistles + special case stuff

* made a bunch of changes in firrtl to shorthand things

* done with padframe for signals

* started major refactoring; first of pad yaml stuff

* forgot to update verilogTemplate -> verilog

* rename ParsePadYaml -> ChipPadsYaml; moved some stuff

* separated out stuff that describes pads i.e. direction, type, side

* forgot to update import for yamlhelpers

* trying to make the process of creating annotations more structured

* saving annotation helpers but prob better to switch to yaml

* saving changes -- reworking annotations

* fixing some bugs; properly annotated ports with pads

* annotate supply pads

* lesson (re)learned. cleaned up constants

* finished adding supply pads to pad frame; still need to generate io file

* also committing updated transform; still without io file

* big typo was causing pad verilog files not to be generated

* verilator passes with transform; had to fix verilog bb typo

* added unused pads; added more thorough tests + did visual inspection of output; made some port types more explicit

* renamed files/classes to be clearer

* started creating pad io template

* update spec so that transform order matters

* get rid of logger

* went around in circles with blackboxhelper + way to annotate

* finished adding + testing pad.io creation

* starting clkgen pass -- made model for asynchronously reset clk divider + wrappers for programmatic bundling

* temporarily locating albert's utility functions here

* saving work on clk constraints

* redid input config passing -- pass in tech directory instead; seems like getting clk sink, src, and relationship works

* not done; need to pause to do tapeout-y things. the clk gen pass gets all the clks and their sources, but i need to build a proper graph to handle clks coming out of muxes
This commit is contained in:
Angie Wang
2017-03-05 18:50:56 -08:00
committed by Stevo
parent e09cbe5b7e
commit f1c437f830
29 changed files with 2891 additions and 5 deletions

7
.gitignore vendored
View File

@@ -1,8 +1,11 @@
### local stuff
### Local stuff
proprietary/
src/main/scala/dsptools/sandbox.sc
test_run_dir/
*.fir
*.f
*.anno
### XilinxISE template
# intermediate build files
*.bgn
@@ -342,4 +345,4 @@ project/plugins/project/
hs_err_pid*
# ignore lib from rocket build
lib
lib

View File

@@ -6,7 +6,7 @@ lazy val commonSettings = Seq(
organization := "edu.berkeley.cs",
version := "0.1-SNAPSHOT",
scalaVersion := "2.11.8",
scalacOptions := Seq("-deprecation", "-feature"),
scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls"),
libraryDependencies ++= commonDependencies
)
@@ -21,4 +21,5 @@ lazy val tapeout = (project in file("tapeout"))
libraryDependencies ++= Seq("chisel3","chisel-iotesters").map {
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
}
)
)
.settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls"))

View File

@@ -7,9 +7,14 @@ object Dependencies {
val scalacheckVersion = "1.12.4"
val scalacheck = "org.scalacheck" %% "scalacheck" % scalacheckVersion % "test"
// Templating!
val handlebarsVersion = "2.1.1"
val handlebars = "com.gilt" %% "handlebars-scala" % handlebarsVersion
val commonDependencies: Seq[ModuleID] = Seq(
scalatest,
scalacheck
scalacheck,
handlebars
)
}

View File

@@ -0,0 +1,113 @@
# Pad types must be one of digital, analog, or supply; pad names must be unique!
# This just shows you how you can template things with {{}}, if/else, and the following parameters:
# isInput: Boolean (each digital pad entry should be configurable between both input and output)
# isHorizontal: Boolean (each pad entry should be configurable between both horizontal and vertical)
# NOTE: Expects 1-bit in/out to be named in/out for digital; and 1-bit io for analog (supplies don't have ports)
# Expects module name to be obtained from {{name}} which is derived from yaml name, tpe in the Firrtl pass
# Pipe is used for stripping margins, but indentation is required before the pipe for the yaml reader to work
---
tpe: analog
name: slow_foundry
width: 0
height: 0
verilog: |
|// Foundry Analog Pad Example
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance PAD
|module {{name}}(
| inout io
|);
|endmodule
---
tpe: analog
name: fast_custom
width: 0
height: 0
verilog: |
|// Custom Analog Pad Example
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance PAD
|module {{name}}(
| inout io
|);
|endmodule
---
tpe: digital
name: from_tristate_foundry
width: 0
height: 0
verilog: |
|// Digital Pad Example
|// Signal Direction: {{#if isInput}}Input{{else}}Output{{/if}}
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance PAD
|module {{name}}(
| input in,
| output reg out
|);
| // Where you would normally dump your pad instance
| always @* begin
| out = in;
| end
|endmodule
---
tpe: digital
name: fake_digital
width: 0
height: 0
verilog: |
|// (Fake/Unused) Digital Pad Example
|// Signal Direction: {{#if isInput}}Input{{else}}Output{{/if}}
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance PAD
|module {{name}}(
| input in,
| output reg out
|);
| // Where you would normally dump your pad instance
| always @* begin
| out = in;
| end
|endmodule
---
tpe: supply
name: vdd
width: 0
height: 0
supplySetNum: 1
verilog: |
|// VDD Pad Example (No IO)
|// Can group some number together as required by the foundry
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance array PAD[0:0], PAD[2:0], etc.
|module {{name}}(
|);
|endmodule
---
tpe: supply
name: vss
width: 0
height: 0
supplySetNum: 2
verilog: |
|// VSS Pad Example (No IO)
|// Can group some number together as required by the foundry
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance array PAD[0:0], PAD[2:0], etc.
|module {{name}}(
|);
|endmodule
---
tpe: supply
name: avss
width: 0
height: 0
supplySetNum: 1
verilog: |
|// Analog VSS Pad Example (No IO)
|// Can group some number together as required by the foundry
|// Pad Orientation: {{#if isHorizontal}}Horizontal{{else}}Vertical{{/if}}
|// Call your instance array PAD[0:0], PAD[2:0], etc.
|module {{name}}(
|);
|endmodule

View File

@@ -0,0 +1,43 @@
# Example for Innovus: https://legacy.iis.ee.ethz.ch/~vlsi2/ex05/ex05.pdf
---
file: pads.io
left: "1" # Bottom to top
top: "2" # Left to right
right: "3" # Bottom to top
bottom: "4" # Left to right
# Note: In your scripts, you should specify instance array styles
# i.e. hdl_instance_array_naming_style string (For Genus)
instanceArray: "{{signal}}[{{idx}}]"
padLine: |
| (inst name = "{{padInst}}") # Side: {{side}}, Order: {{padIdx}}
template: |
|(globals
| version = 3
| io_order = default
|)
|(iopad
| (bottomleft
| (inst name="corner_ll" cell="CORNER_EXAMPLE" )
| )
| (bottomright
| (inst name="corner_lr" orientation=MY cell="CORNER_EXAMPLE" )
| )
| (topleft
| (inst name="corner_ul" orientation=MX cell="CORNER_EXAMPLE" )
| )
| (topright
| (inst name="corner_ur" cell="CORNER_EXAMPLE" )
| )
| (left
|{{leftPads}}
| )
| (right
|{{rightPads}}
| )
| (top
|{{topPads}}
| )
| (bottom
|{{bottomPads}}
| )
|)

View File

@@ -0,0 +1,248 @@
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 {
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) {
def getAnno = ChiselAnnotation(target, classOf[ClkSrcTransform], anno.serialize)
}
// Firrtl version
case class TargetClkPortAnnoF(target: ComponentName, anno: ClkPortAnnotation) extends FirrtlClkTransformAnnotation {
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) {
def getAnno = ChiselAnnotation(target, classOf[ClkSrcTransform], anno.serialize)
}
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 =>
private def doNotDedup(module: Module): Unit = {
annotate(ChiselAnnotation(module, classOf[DedupModules], "nodedup!"))
}
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).getAnno)
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 = {
p.dir match {
case chisel3.core.Direction.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.Direction.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).getAnno)
}
}

View File

@@ -0,0 +1,128 @@
package barstools.tapeout.transforms.clkgen
import chisel3.experimental.{withClockAndReset, withClock, withReset}
import chisel3._
import chisel3.util.RegInit
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!")
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

@@ -0,0 +1,27 @@
package barstools.tapeout.transforms.clkgen
import firrtl._
import firrtl.annotations._
import firrtl.passes._
import firrtl.ir._
class ClkSrcTransform extends Transform with SimpleRun {
override def inputForm: CircuitForm = LowForm
override def outputForm: CircuitForm = LowForm
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)
val passSeq = Seq(
InferTypes,
new CreateClkConstraints(clkModAnnos, clkPortAnnos, targetDir)
)
CircuitState(runPasses(state.circuit, passSeq), LowForm)
}
}
}

View File

@@ -0,0 +1,152 @@
// 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 {
def name = "Create clock constraints"
// 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()).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 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("All top module input clks/clk module output clocks must be sinks/sources!"))
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

@@ -0,0 +1,57 @@
package barstools.tapeout.transforms.pads
import firrtl._
import firrtl.annotations._
import firrtl.passes._
import firrtl.ir._
import barstools.tapeout.transforms._
// Main Add IO Pad transform operates on low Firrtl
class AddIOPadsTransform extends Transform with SimpleRun {
override def inputForm: CircuitForm = LowForm
override def outputForm: CircuitForm = LowForm
override def execute(state: CircuitState): CircuitState = {
val collectedAnnos = HasPadAnnotation(getMyAnnotations(state))
collectedAnnos match {
// Transform not used
case None => CircuitState(state.circuit, LowForm)
case Some(x) =>
val techLoc = (new TechnologyLocation).get(state)
// Get foundry pad templates from yaml
val foundryPads = FoundryPadsYaml.parse(techLoc)
val portPads = AnnotatePortPads(state.circuit, x.topModName, foundryPads, x.componentAnnos,
HasPadAnnotation.getSide(x.defaultPadSide))
val supplyPads = AnnotateSupplyPads(foundryPads, x.supplyAnnos)
val (circuitWithBBs, bbAnnotations) = CreatePadBBs(state.circuit, portPads, supplyPads)
val namespace = Namespace(state.circuit)
val padFrameName = namespace newName s"${x.topModName}_PadFrame"
val topInternalName = namespace newName s"${x.topModName}_Internal"
val targetDir = barstools.tapeout.transforms.GetTargetDir(state)
PadPlacementFile.generate(techLoc, targetDir, padFrameName, portPads, supplyPads)
val passSeq = Seq(
Legalize,
ResolveGenders,
// Types really need to be known...
InferTypes,
new AddPadFrame(x.topModName, padFrameName, topInternalName, portPads, supplyPads),
RemoveEmpty,
CheckInitialization,
InferTypes,
Uniquify,
ResolveKinds,
ResolveGenders
)
// Expects BlackBox helper to be run after to inline pad Verilog!
val prevAnnos = state.annotations.getOrElse(AnnotationMap(Seq.empty)).annotations
val cs = CircuitState(
runPasses(circuitWithBBs, passSeq),
LowForm,
Some(AnnotationMap(prevAnnos ++ bbAnnotations))
)
// TODO: *.f file is overwritten on subsequent executions, but it doesn't seem to be used anywhere?
(new firrtl.transforms.BlackBoxSourceHelper).execute(cs)
}
}
}

View File

@@ -0,0 +1,135 @@
// See LICENSE for license details.
package barstools.tapeout.transforms.pads
import firrtl.annotations._
import firrtl.ir._
import firrtl._
import firrtl.passes._
// Analog is like UInt, SInt; it's not a direction (which is kind of weird)
// WARNING: Analog type is associated with Verilog InOut! i.e. even if digital pads are tri-statable, b/c tristate
// requires an additional ctrl signal, digital pads must be operated in a single "static" condition here; Analog will
// be paired with analog pads
class AddPadFrame(
topMod: String,
padFrameName: String,
topInternalName: String,
ioPads: Seq[PortIOPad],
supplyPads: Seq[TopSupplyPad]) extends Pass {
def name: String = "Add Padframe"
def run(c: Circuit): Circuit = {
// New modules consist of old modules (with top renamed to internal) + padFrame + newTop
val newMods = c.modules.map {
case mod: Module if mod.name == topMod =>
// Original top module is now internal module
mod.copy(name = topInternalName)
case m => m
} ++ Seq(buildPadFrame(), buildTopWrapper())
// Reparent so circuit top is whatever uses pads!
// TODO: Can the top level be a blackbox?
c.copy(modules = newMods, main = topMod)
}
def intName(p: PortIOPad) = s"${p.portName}_Int"
def extName(p: PortIOPad) = s"${p.portName}_Ext"
def buildTopWrapper(): Module = {
// outside -> padframe -> internal
// Top (with same name) contains 1) padframe + 2) internal signals
val padFrameInst = WDefInstance(padFrameName, padFrameName)
val topInternalInst = WDefInstance(topInternalName, topInternalName)
val padFrameRef = WRef(padFrameName)
val topInternalRef = WRef(topInternalName)
val connects = ioPads.map { p =>
val io = WRef(p.portName)
val intIo = WSubField(topInternalRef, p.portName)
val padFrameIntIo = WSubField(padFrameRef, intName(p))
val padFrameExtIo = WSubField(padFrameRef, extName(p))
p.port.tpe match {
case AnalogType(_) =>
// Analog pads only have 1 port
// If Analog port doesn't have associated pad, don't hook it up to the padframe
val analogAttachInt = Seq(Attach(NoInfo, Seq(io, intIo)))
if (p.pad.isEmpty) analogAttachInt
else analogAttachInt :+ Attach(NoInfo, Seq(io, padFrameExtIo))
case _ => p.portDirection match {
case Input =>
// input to padframe ; padframe to internal
Seq(Connect(NoInfo, padFrameExtIo, io), Connect(NoInfo, intIo, padFrameIntIo))
case Output =>
// internal to padframe ; padframe to output
Seq(Connect(NoInfo, padFrameIntIo, intIo), Connect(NoInfo, io, padFrameExtIo))
}
}
}.flatten
val stmts = Seq(padFrameInst, topInternalInst) ++ connects
val ports = ioPads.map(p => p.port)
Module(NoInfo, topMod, ports = ports, body = Block(stmts))
}
def buildPadFrame(): Module = {
// Internal = connection to original RTL; External = connection to outside world
// Note that for analog pads, since there's only 1 port, only _Ext is used
val intPorts = ioPads.map(p => p.port.tpe match {
case AnalogType(_) => None
case _ => Some(p.port.copy(name = intName(p), direction = Utils.swap(p.portDirection)))
}).flatten
val extPorts = ioPads.map(p => p.port.tpe match {
// If an analog port doesn't have a pad associated with it, don't add it to the padframe
case AnalogType(_) if p.pad.isEmpty => None
case _ => Some(p.port.copy(name = extName(p)))
} ).flatten
// Only create pad black boxes for ports that require them
val ioPadInsts = ioPads.filter(x => !x.pad.isEmpty).map(p => WDefInstance(p.firrtlBBName, p.firrtlBBName))
// Connect to pad only if used ; otherwise leave dangling for Analog
// and just connect through for digital (assumes no supplies)
val connects = ioPads.map { p =>
val intRef = WRef(intName(p), p.port.tpe)
val extRef = WRef(extName(p), p.port.tpe)
p.pad match {
// No pad needed -- just connect through
case None => p.port.tpe match {
case AnalogType(_) =>
Seq(EmptyStmt)
case _ =>
val (lhs, rhs) = p.portDirection match {
case Input => (intRef, extRef)
case Output => (extRef, intRef)
}
Seq(Connect(NoInfo, lhs, rhs))
}
// Add pad
case Some(x) =>
val padRef = WRef(p.firrtlBBName)
p.port.tpe match {
// Analog type has 1:1 mapping to inout
case AnalogType(_) =>
val padIORef = WSubField(padRef, AnalogPad.ioName)
Seq(Attach(NoInfo, Seq(padIORef, extRef)))
// Normal verilog in/out can be mapped to uint, sint, or clocktype, so need cast
case _ =>
val padBBType = UIntType(getWidth(p.port.tpe))
val padInRef = WSubField(padRef, DigitalPad.inName, padBBType, UNKNOWNGENDER)
val padOutRef = WSubField(padRef, DigitalPad.outName, padBBType, UNKNOWNGENDER)
val (rhsPadIn, lhsPadOut) = p.portDirection match {
case Input => (extRef, intRef)
case Output => (intRef, extRef)
}
// Pad inputs are treated as UInts, so need to do type conversion
// from type to UInt pad input; from pad output to type
Seq(
Connect(NoInfo, padInRef, castRhs(padBBType, rhsPadIn)),
Connect(NoInfo, lhsPadOut, castRhs(p.port.tpe, padOutRef)))
}
}
}.flatten
val supplyPadInsts = supplyPads.map(p => p.instNames.map(n => WDefInstance(n, p.firrtlBBName))).flatten
Module(NoInfo, padFrameName, ports = intPorts ++ extPorts, body = Block(ioPadInsts ++ connects ++ supplyPadInsts))
}
}

View File

@@ -0,0 +1,135 @@
package barstools.tapeout.transforms.pads
import firrtl.annotations._
import firrtl._
import firrtl.ir._
import firrtl.passes._
import barstools.tapeout.transforms._
// TODO: Make some trait with commonalities between IO Pad + supply pad
// Pads associated with IO Ports! (Not supplies!)
case class PortIOPad(
pad: Option[FoundryPad],
padSide: PadSide,
port: Port) {
def arrayInstNamePrefix(mod: String): String = Seq(mod, firrtlBBName, getPadName).mkString("/")
def arrayInstNameSuffix: String = pad match {
case None => throw new Exception("Port needs to use pad to get array instance name!")
case Some(x) => "/" + x.padInstName
}
def portName = port.name
def portWidth = bitWidth(port.tpe).intValue
def portDirection = port.direction
def padOrientation = padSide.orientation
def padType = pad match {
case None => NoPad
case Some(x) => x.padType
}
def widthParamName = "WIDTH"
def getPadName: String = pad match {
case None => throw new Exception("Cannot get pad name when no pad specified!")
case Some(x) => x.getName(portDirection, padOrientation)
}
def getPadArrayName: String = Seq(getPadName, "array").mkString("_")
// Firrtl black box name must be unique, even though the parameterized Verilog modules don't
// need to have separate names
def firrtlBBName = Seq(getPadArrayName, portName).mkString("_")
// Note: This includes both the pad wrapper + an additional wrapper for n-bit wide to
// multiple pad conversion!
def createPadInline(): String = {
// For blackboxing bit extraction/concatenation (with module arrays)
def io(): String = padType match {
case DigitalPad =>
s"""| input [${widthParamName}-1:0] ${DigitalPad.inName},
| output reg [${widthParamName}-1:0] ${DigitalPad.outName}""".stripMargin
case AnalogPad =>
s" inout [${widthParamName}-1:0] ${AnalogPad.ioName}"
case _ => throw new Exception("IO pad can only be digital or analog")
}
def assignIO(): String = padType match {
case DigitalPad =>
s"""| .${DigitalPad.inName}(${DigitalPad.inName}),
| .${DigitalPad.outName}(${DigitalPad.outName})""".stripMargin
case AnalogPad =>
s" .${AnalogPad.ioName}(${AnalogPad.ioName})"
case _ => throw new Exception("IO pad can only be digital or analog")
}
def getPadVerilog(): String = pad match {
case None => throw new Exception("Cannot get Verilog when no pad specified!")
case Some(x) => x.getVerilog(portDirection, padOrientation)
}
s"""inline
|${getPadArrayName}.v
|${getPadVerilog}
|module ${getPadArrayName} #(
| parameter int ${widthParamName}=1
|)(
|${io}
|);
| ${getPadName} ${getPadName}[${widthParamName}-1:0](
|${assignIO}
| );
|endmodule""".stripMargin
}
}
object AnnotatePortPads {
def apply(
c: Circuit,
topMod: String,
pads: Seq[FoundryPad],
componentAnnos: Seq[TargetIOPadAnnoF],
defaultSide: PadSide): Seq[PortIOPad] = {
def lowerAnnotations(): Seq[TargetIOPadAnnoF] = {
componentAnnos map { x => x.target match {
case c: ComponentName => x.copy(target = c.copy(name = LowerName(c.name)))
case _ => throw new Exception("Not a component annotation! Can't lower!")
}}
}
// Make annotations match low form
val annos = lowerAnnotations()
def getPortIOPad(port: Port): PortIOPad = {
val portAnnos = annos.find(_.targetName == port.name)
// Ports can only be digital or analog
val padTypeRequired = port.tpe match {
case AnalogType(_) => AnalogPad
case _ => DigitalPad
}
val validPads = pads.filter(_.padType == padTypeRequired)
require(validPads.length > 0, s"No ${padTypeRequired.serialize} pads specified in the config yaml file!")
portAnnos match {
case None =>
// If no pad-related annotation is found on a port, use defaults based off of port type
PortIOPad(Some(validPads.head), defaultSide, port)
case Some(x) =>
x.anno match {
case NoIOPadAnnotation(_) =>
// Some ports might not want attached pads
PortIOPad(None, defaultSide, port)
case IOPadAnnotation(padSide, padName) if padName.isEmpty =>
// If no pad name is used, select the first valid pad based off of port type
PortIOPad(Some(validPads.head), HasPadAnnotation.getSide(padSide), port)
case IOPadAnnotation(padSide, padName) =>
// If name doesn't match any provided -- maybe someone typoed?
validPads.find(_.name == padName) match {
case None =>
throw new Exception(
s"Pad name associated with ${port.name} doesn't match valid pad names. Did you typo?")
case Some(x) =>
PortIOPad(Some(x), HasPadAnnotation.getSide(padSide), port)
}
}
}
}
// Top MUST be internal module
c.modules.filter(_.name == topMod).head.ports.map(x => getPortIOPad(x))
}
}

View File

@@ -0,0 +1,56 @@
package barstools.tapeout.transforms.pads
import firrtl.annotations._
import firrtl._
import firrtl.ir._
import firrtl.passes._
case class TopSupplyPad(
pad: FoundryPad,
padSide: PadSide,
num: Int
) {
// TODO: These should be pulled into some common trait (supply + io)!
def arrayInstNamePrefix(mod: String): Seq[String] = {
instNames.map(n => Seq(mod, n, pad.padInstName).mkString("/"))
}
def supplySetNum = pad.getSupplySetNum
def padType = pad.padType
require(pad.padType == SupplyPad)
def padOrientation = padSide.orientation
def getPadName = pad.getName(NoDirection, padOrientation)
def firrtlBBName = getPadName
private def instNamePrefix = Seq(firrtlBBName, padSide.serialize).mkString("_")
def instNames = (0 until num).map(i => Seq(instNamePrefix, i.toString).mkString("_"))
def createPadInline(): String = {
def getPadVerilog(): String = pad.getVerilog(NoDirection, padOrientation)
s"""inline
|${getPadName}.v
|${getPadVerilog}""".stripMargin
}
}
object AnnotateSupplyPads {
def apply(
pads: Seq[FoundryPad],
supplyAnnos: Seq[SupplyAnnotation]
): Seq[TopSupplyPad] = {
supplyAnnos.map( a =>
pads.find(_.name == a.padName) match {
case None =>
throw new Exception(s"Supply pad ${a.padName} not found in Yaml file!")
case Some(x) =>
Seq(
TopSupplyPad(x, Left, a.leftSide),
TopSupplyPad(x, Right, a.rightSide),
TopSupplyPad(x, Top, a.topSide),
TopSupplyPad(x, Bottom, a.bottomSide))
}
).flatten.filter(_.num > 0)
}
}

View File

@@ -0,0 +1,76 @@
package barstools.tapeout.transforms.pads
import chisel3._
import barstools.tapeout.transforms.clkgen._
import chisel3.experimental._
import firrtl.transforms.DedupModules
// TODO: Move out of pads
// NOTE: You can't really annotate outside of the module itself UNLESS you break up the compile step in 2 i.e.
// annotate post-Chisel but pre-Firrtl (unfortunate non-generator friendly downside).
// It's recommended to have a Tapeout specific TopModule wrapper.
// LIMITATION: All signals of a bus must be on the same chip side
// Chisel-y annotations
abstract class TopModule(
supplyAnnos: Seq[SupplyAnnotation] = Seq.empty,
defaultPadSide: PadSide = Top,
coreWidth: Int = 0,
coreHeight: Int = 0,
usePads: Boolean = true,
override_clock: Option[Clock] = None,
override_reset: Option[Bool] = None) extends Module(override_clock, override_reset) with IsClkModule {
override def annotateClkPort(p: Element, anno: ClkPortAnnotation): Unit = {
p.dir match {
case chisel3.core.Direction.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).getAnno)
}
override def annotateDerivedClks(m: Module, anno: ClkModAnnotation): Unit =
throw new Exception("Top module cannot be pure clock module!")
// Annotate module as top module (that requires pad transform)
// Specify the yaml file that indicates how pads are templated,
// the default chip side that pads should be placed (if nothing is specified per IO),
// and supply annotations: supply pad name, location, and #
def createPads(): Unit = if (usePads) {
val modulePadAnnotation = ModulePadAnnotation(
defaultPadSide = defaultPadSide.serialize,
coreWidth = coreWidth,
coreHeight = coreHeight,
supplyAnnos = supplyAnnos
)
annotate(TargetModulePadAnnoC(this, modulePadAnnotation).getAnno)
}
// Annotate IO with side + pad name
def annotatePad(sig: Element, side: PadSide = defaultPadSide, name: String = ""): Unit = if (usePads) {
val anno = IOPadAnnotation(side.serialize, name)
annotate(TargetIOPadAnnoC(sig, anno).getAnno)
}
def annotatePad(sig: Aggregate, name: String): Unit = annotatePad(sig, side = defaultPadSide, name)
def annotatePad(sig: Aggregate, side: PadSide): Unit = annotatePad(sig, side, name = "")
def annotatePad(sig: Aggregate, side: PadSide, name: String): Unit =
extractElements(sig) foreach { x => annotatePad(x, side, name) }
// There may be cases where pads were inserted elsewhere. If that's the case, allow certain IO to
// not have pads auto added. Note that annotatePad and noPad are mutually exclusive!
def noPad(sig: Element): Unit = if (usePads) annotate(TargetIOPadAnnoC(sig, NoIOPadAnnotation()).getAnno)
def noPad(sig: Aggregate): Unit = extractElements(sig) foreach { x => noPad(x) }
// Since this is a super class, this should be the first thing that gets run
// (at least when the module is actually at the top -- currently no guarantees otherwise :( firrtl limitation)
createPads()
}

View File

@@ -0,0 +1,109 @@
package barstools.tapeout.transforms.pads
import firrtl.annotations._
import firrtl._
import firrtl.ir._
import firrtl.transforms._
object CreatePadBBs {
private [barstools] case class UsedPadInfo(
// The following are found with both supply + io pads
padInline: String, // Verilog txt
padName: String, // Pad module name
padType: PadType, // Pad type: supply, analog, digital
// The following only affects io pads (due to using parameterized modules for bit extraction / cat)
padArrayName: String, // Name of parameterized pad wrapper (that does bit extract/cat)
firrtlBBName: String, // Unique Firrtl name of each parameterized pad wrapper
portWidth: Int // Port width for analog/digital
)
def convertToUsedPad(p: PortIOPad): UsedPadInfo = {
UsedPadInfo(
padInline = p.createPadInline,
padName = p.getPadName,
padType = p.padType,
padArrayName = p.getPadArrayName,
firrtlBBName = p.firrtlBBName,
portWidth = p.portWidth)
}
def convertToUsedPad(p: TopSupplyPad): UsedPadInfo = {
UsedPadInfo(
padInline = p.createPadInline,
padName = p.getPadName,
padType = p.padType,
// Supply pads don't require bit extraction / cat so don't care
padArrayName = p.getPadName,
firrtlBBName = p.getPadName,
portWidth = 0)
}
def checkLegalPadName(namespace: Namespace, usedPads: Seq[UsedPadInfo]): Unit = {
usedPads foreach { x =>
if (namespace contains x.padName)
throw new Exception(s"Pad name ${x.padName} already used!")
if (namespace contains x.padArrayName)
throw new Exception(s"Pad array ${x.padArrayName} name already used!")
if (namespace contains x.firrtlBBName)
throw new Exception(s"Firrtl black box ${x.firrtlBBName} name already used!")
}
}
def apply(
c: Circuit,
ioPads: Seq[PortIOPad],
supplyPads: Seq[TopSupplyPad]): (Circuit, Seq[Annotation]) = {
// Add black boxes for both supply + (used) io pads
val usedPads = ioPads.filter(x => x.pad.nonEmpty).map(convertToUsedPad(_)) ++ supplyPads.map(convertToUsedPad(_))
checkLegalPadName(Namespace(c), usedPads)
// Note that we need to check for Firrtl name uniqueness here! (due to parameterization)
val uniqueExtMods = scala.collection.mutable.ArrayBuffer[UsedPadInfo]()
usedPads foreach { x =>
if (uniqueExtMods.find(_.firrtlBBName == x.firrtlBBName).isEmpty)
uniqueExtMods += x
}
// Collecting unique parameterized black boxes
// (for io, they're wrapped pads; for supply, they're pad modules directly)
val uniqueParameterizedBBs = scala.collection.mutable.ArrayBuffer[UsedPadInfo]()
uniqueExtMods foreach { x =>
if (uniqueParameterizedBBs.find(_.padArrayName == x.padArrayName).isEmpty)
uniqueParameterizedBBs += x
}
// Note: Firrtl is silly and doesn't implement true parameterization -- each module with
// parameterization that potentially affects # of IO needs to be uniquely identified
// (but only in Firrtl)
val bbs = uniqueExtMods.map(x => {
// Supply pads don't have ports
val ports = x.padType match {
case AnalogPad => Seq(Port(NoInfo, AnalogPad.ioName, Input, AnalogType(IntWidth(x.portWidth))))
case DigitalPad => Seq(
Port(NoInfo, DigitalPad.inName, Input, UIntType(IntWidth(x.portWidth))),
Port(NoInfo, DigitalPad.outName, Output, UIntType(IntWidth(x.portWidth)))
)
case SupplyPad => Seq.empty
case _ => throw new Exception("Port pad type invalid!")
}
// Supply black boxes are not parameterized
val params = x.padType match {
case AnalogPad | DigitalPad => Seq(IntParam(ioPads.head.widthParamName, x.portWidth))
case SupplyPad => Seq()
case _ => throw new Exception("Port pad type invalid!")
}
// Firrtl name is unique
ExtModule(NoInfo, x.firrtlBBName, ports, x.padArrayName, params)
} ).toSeq
// Add annotations to black boxes to inline Verilog from template
// Again, note the weirdness in parameterization -- just need to hook to one matching Firrtl instance
val annos = uniqueParameterizedBBs.map(x =>
BlackBoxSourceAnnotation(ModuleName(x.firrtlBBName, CircuitName(c.main)), x.padInline)
).toSeq
(c.copy(modules = c.modules ++ bbs), annos)
}
}

View File

@@ -0,0 +1,95 @@
package barstools.tapeout.transforms.pads
import net.jcazevedo.moultingyaml._
import firrtl._
import firrtl.ir._
import barstools.tapeout.transforms._
case class FoundryPad(
tpe: String,
name: String,
width: Int,
height: Int,
supplySetNum: Option[Int],
verilog: String) {
def padInstName = "PAD"
require(verilog.contains("{{#if isHorizontal}}"), "All pad templates must contain '{{#if isHorizontal}}'")
require(verilog.contains("{{name}}"), "All pad templates must contain module name '{{name}}'")
require(verilog.contains(padInstName), s"All pad templates should have instances called ${padInstName}")
def getSupplySetNum = supplySetNum.getOrElse(1)
val padType = tpe match {
case "digital" =>
require(verilog.contains(DigitalPad.inName), "Digital pad template must contain input called 'in'")
require(verilog.contains(DigitalPad.outName), "Digital pad template must contain output called 'out'")
require(verilog.contains("{{#if isInput}}"), "Digital pad template must contain '{{#if isInput}}'")
DigitalPad
case "analog" =>
require(verilog.contains(AnalogPad.ioName), "Analog pad template must contain inout called 'io'")
require(!verilog.contains("{{#if isInput}}"), "Analog pad template must not contain '{{#if isInput}}'")
AnalogPad
case "supply" =>
// Supply pads don't have IO
require(!verilog.contains("{{#if isInput}}"), "Supply pad template must not contain '{{#if isInput}}'")
require(
verilog.contains(s"${padInstName}["), "All supply pad templates should have instance arrays" +
" called ${padInstName}[n:0], where n = ${getSupplySetNum-1}")
require(supplySetNum.nonEmpty, "# of grouped supply pads 'supplySetNum' should be specified!")
SupplyPad
case _ => throw new Exception("Illegal pad type in config!")
}
import com.gilt.handlebars.scala.binding.dynamic._
import com.gilt.handlebars.scala.Handlebars
private val template = Handlebars(verilog)
// Make sure names don't have spaces in Verilog!
private[barstools] val correctedName = name.replace(" ", "_")
case class TemplateParams(
// isInput only used with digital pads
isInput: Boolean,
isHorizontal: Boolean) {
private val orient = if (isHorizontal) Horizontal.serialize else Vertical.serialize
private val dir = padType match {
case AnalogPad => InOut.serialize
case SupplyPad => NoDirection.serialize
case DigitalPad => if (isInput) Input.serialize else Output.serialize
}
val name = {
val start = Seq("pad", tpe, correctedName, orient)
if (padType == DigitalPad) start :+ dir
else start
}.mkString("_")
}
// Note: Analog + supply don't use direction
private def getTemplateParams(dir: Direction, orient: PadOrientation): TemplateParams =
TemplateParams(isInput = (dir == Input), isHorizontal = (orient == Horizontal))
def getVerilog(dir: Direction, orient: PadOrientation): String = {
val p = getTemplateParams(dir, orient)
template(p).stripMargin
}
def getName(dir: Direction, orient: PadOrientation): String = getTemplateParams(dir, orient).name
}
object FoundryPadsYaml extends DefaultYamlProtocol {
val exampleResource = "/FoundryPads.yaml"
implicit val _pad = yamlFormat6(FoundryPad)
def parse(techDir: String): Seq[FoundryPad] = {
val file = techDir + exampleResource
if(techDir != "" && !(new java.io.File(file)).exists())
throw new Exception("Technology directory must contain FoundryPads.yaml!")
val out = (new YamlFileReader(exampleResource)).parse[FoundryPad](if (techDir == "") "" else file)
val padNames = out.map(x => x.correctedName)
require(padNames.distinct.length == padNames.length, "Pad names must be unique!")
out
}
}

View File

@@ -0,0 +1,133 @@
package barstools.tapeout.transforms.pads
import firrtl.annotations._
import chisel3.experimental._
import chisel3._
import barstools.tapeout.transforms._
import firrtl._
import net.jcazevedo.moultingyaml._
object PadAnnotationsYaml extends DefaultYamlProtocol {
implicit val _iopad = yamlFormat2(IOPadAnnotation)
implicit val _noiopad = yamlFormat1(NoIOPadAnnotation)
implicit val _supplyanno = yamlFormat5(SupplyAnnotation)
implicit val _modulepadanno = yamlFormat4(ModulePadAnnotation)
}
abstract class FirrtlPadTransformAnnotation {
def targetName: String
}
// IO Port can either be annotated with padName + padSide OR noPad (mutually exclusive)
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:"
}
// Firrtl version
case class TargetIOPadAnnoF(target: ComponentName, anno: IOAnnotation) extends FirrtlPadTransformAnnotation {
def getAnno = Annotation(target, classOf[AddIOPadsTransform], anno.serialize)
def targetName = target.name
}
// Chisel version
case class TargetIOPadAnnoC(target: Element, anno: IOAnnotation) {
def getAnno = ChiselAnnotation(target, classOf[AddIOPadsTransform], anno.serialize)
}
// A bunch of supply pads (designated by name, # on each chip side) can be associated with the top module
case class SupplyAnnotation(
padName: String,
leftSide: Int = 0,
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,
coreWidth: Int = 0,
coreHeight: Int = 0,
supplyAnnos: Seq[SupplyAnnotation] = Seq.empty) {
import PadAnnotationsYaml._
def serialize: String = this.toYaml.prettyPrint
val supplyPadNames = supplyAnnos.map(_.padName)
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 {
def getAnno = Annotation(target, classOf[AddIOPadsTransform], anno.serialize)
def targetName = target.name
}
// Chisel version
case class TargetModulePadAnnoC(target: Module, anno: ModulePadAnnotation) {
def getAnno = ChiselAnnotation(target, classOf[AddIOPadsTransform], anno.serialize)
}
case class CollectedAnnos(
componentAnnos: Seq[TargetIOPadAnnoF],
moduleAnnos: TargetModulePadAnnoF) {
def supplyAnnos = moduleAnnos.anno.supplyAnnos
def defaultPadSide = moduleAnnos.anno.defaultPadSide
def topModName = moduleAnnos.targetName
def coreWidth = moduleAnnos.anno.coreWidth
def coreHeight = moduleAnnos.anno.coreHeight
}
object HasPadAnnotation {
import PadAnnotationsYaml._
def getSide(a: String): PadSide = a match {
case i if i == Left.serialize => Left
case i if i == Right.serialize => Right
case i if i == Top.serialize => Top
case i if i == Bottom.serialize => Bottom
case _ => throw new Exception(s" $a not a valid pad side annotation!")
}
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
}
def apply(annos: Seq[Annotation]): Option[CollectedAnnos] = {
// Get all pad-related annotations (config files, pad sides, pad names, etc.)
val padAnnos = annos.map(x => unapply(x)).flatten
val 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 {
val moduleAnnosTemp = padAnnos.filter {
case TargetModulePadAnnoF(_, _) => true
case _ => false
}
require(moduleAnnosTemp.length == 1, "Only 1 module may be designated 'Top'")
val moduleAnnos = moduleAnnosTemp.head
val topModName = moduleAnnos.targetName
val componentAnnos = padAnnos.filter {
case TargetIOPadAnnoF(ComponentName(_, ModuleName(n, _)), _) if n == topModName =>
true
case TargetIOPadAnnoF(ComponentName(_, ModuleName(n, _)), _) if n != topModName =>
throw new Exception("Pad related component annotations must all be in the same top module")
case _ => false
}.map(x => x.asInstanceOf[TargetIOPadAnnoF])
Some(CollectedAnnos(componentAnnos, moduleAnnos.asInstanceOf[TargetModulePadAnnoF]))
}
}
}

View File

@@ -0,0 +1,56 @@
package barstools.tapeout.transforms.pads
import firrtl._
import firrtl.ir._
abstract class PadOrientation extends FirrtlNode
case object Horizontal extends PadOrientation {
def serialize: String = "horizontal"
}
case object Vertical extends PadOrientation {
def serialize: String = "vertical"
}
abstract class PadType extends FirrtlNode
case object DigitalPad extends PadType {
def serialize: String = "digital"
def inName: String = "in"
def outName: String = "out"
}
case object AnalogPad extends PadType {
def serialize: String = "analog"
def ioName: String = "io"
}
case object SupplyPad extends PadType {
def serialize: String = "supply"
}
case object NoPad extends PadType {
def serialize: String = "none"
}
case object InOut extends Direction {
def serialize: String = "inout"
}
case object NoDirection extends Direction {
def serialize: String = "none"
}
abstract class PadSide extends FirrtlNode {
def orientation: PadOrientation
}
case object Left extends PadSide {
def serialize: String = "left"
def orientation: PadOrientation = Horizontal
}
case object Right extends PadSide {
def serialize: String = "right"
def orientation: PadOrientation = Horizontal
}
case object Top extends PadSide {
def serialize: String = "top"
def orientation: PadOrientation = Vertical
}
case object Bottom extends PadSide {
def serialize: String = "bottom"
def orientation: PadOrientation = Vertical
}

View File

@@ -0,0 +1,116 @@
package barstools.tapeout.transforms.pads
import net.jcazevedo.moultingyaml._
import firrtl._
import firrtl.ir._
import barstools.tapeout.transforms._
case class PadPlacement(
file: String,
left: String,
top: String,
right: String,
bottom: String,
instanceArray: String,
padLine: String,
template: String) {
require(instanceArray contains "{{signal}}", "Instance Array Template should contain {{signal}}")
require(instanceArray contains "{{idx}}", "Instance Array Template should contain {{idx}}")
require(padLine contains "{{padInst}}", "Pad line should contain {{padInst}}")
require(padLine contains "{{side}}", "Pad line should contain {{side}} (Can be in comments)")
require(padLine contains "{{padIdx}}", "Pad line should contain {{padIdx}} (Can be in comments)")
require(template contains "{{leftPads}}", "Pad line should contain {{leftPads}}")
require(template contains "{{rightPads}}", "Pad line should contain {{rightPads}}")
require(template contains "{{topPads}}", "Pad line should contain {{topPads}}")
require(template contains "{{bottomPads}}", "Pad line should contain {{bottomPads}}")
def getSideString(s: PadSide): String = s match {
case Left => left
case Right => right
case Top => top
case Bottom => bottom
}
import com.gilt.handlebars.scala.binding.dynamic._
import com.gilt.handlebars.scala.Handlebars
private val instanceArrayTemplate = Handlebars(instanceArray.stripMargin)
private val padLineTemplate = Handlebars(padLine.stripMargin)
private val padPlacementTemplate = Handlebars(template.stripMargin)
def getInstanceArray(p: InstanceArrayParams): String = instanceArrayTemplate(p).stripMargin
def getPadLine(p: PadLineParams): String = padLineTemplate(p).stripMargin.replace("&quot;", "\"")
def getPadPlacement(p: PadPlacementParams): String = padPlacementTemplate(p).stripMargin.replace("&quot;", "\"")
}
case class InstanceArrayParams(signal: String, idx: Int)
case class PadLineParams(padInst: String, side: String, padIdx: Int)
case class PadPlacementParams(leftPads: String, rightPads: String, topPads: String, bottomPads: String)
object PadPlacementFile extends DefaultYamlProtocol {
val exampleResource = "/PadPlacement.yaml"
implicit val _pad = yamlFormat8(PadPlacement)
def parse(file: String = ""): PadPlacement = {
(new YamlFileReader(exampleResource)).parse[PadPlacement](file).head
}
def generate(
techDir: String,
targetDir: String,
padFrameName: String,
portPads: Seq[PortIOPad],
supplyPads: Seq[TopSupplyPad]): Unit = {
val file = techDir + exampleResource
if(techDir != "" && !(new java.io.File(file)).exists())
throw new Exception("Technology directory must contain PadPlacement.yaml!")
val template = parse(if (techDir == "") "" else file)
val leftPads = scala.collection.mutable.ArrayBuffer[String]()
val rightPads = scala.collection.mutable.ArrayBuffer[String]()
val topPads = scala.collection.mutable.ArrayBuffer[String]()
val bottomPads = scala.collection.mutable.ArrayBuffer[String]()
def sort(side: PadSide, inst: String): Unit = side match {
case Left => leftPads += inst
case Right => rightPads += inst
case Top => topPads += inst
case Bottom => bottomPads += inst
}
// TODO: Be smarter about supply placement (+ grouping?) between signals
// Supply pad instance name: padFrameName/firrtlBBName_padSide_#num/PAD[#supplySetNum]
supplyPads foreach { p =>
val prefixes = p.arrayInstNamePrefix(padFrameName)
prefixes foreach { prefix =>
(0 until p.supplySetNum) foreach { idx =>
sort(p.padSide, template.getInstanceArray(InstanceArrayParams(prefix, idx)))
}
}
}
// IO pad instance name: padFrameName/firrtlBBName/getPadName[#portWidth]/PAD
portPads.filter(_.pad.nonEmpty) foreach { p =>
val prefix = p.arrayInstNamePrefix(padFrameName)
(0 until p.portWidth).map(idx =>
template.getInstanceArray(InstanceArrayParams(prefix, idx)) + p.arrayInstNameSuffix
) foreach { x => sort(p.padSide, x) }
}
def getLines(pads: Seq[String], side: PadSide): String = {
val seq = pads.zipWithIndex.map{ case (p, idx) =>
template.getPadLine(PadLineParams(p, template.getSideString(side), idx)) }
seq.mkString("\n")
}
val fileContents = template.getPadPlacement(PadPlacementParams(
leftPads = getLines(leftPads.toSeq, Left),
rightPads = getLines(rightPads.toSeq, Right),
topPads = getLines(topPads.toSeq, Top),
bottomPads = getLines(bottomPads.toSeq, Bottom)
))
WriteConfig(targetDir, template.file, fileContents)
}
}

View File

@@ -0,0 +1,158 @@
package firrtl
import scala.collection.immutable.{HashSet, HashMap}
import scala.collection.mutable
import scala.collection.mutable.MultiMap
class MutableDiGraph[T](
val edgeData: MultiMap[T,T] = new mutable.HashMap[T, mutable.Set[T]] with MultiMap[T, T]) {
def contains(v: T) = edgeData.contains(v)
def getVertices = edgeData.keys
def getEdges(v: T) = edgeData(v)
def addVertex(v: T): T = {
edgeData.getOrElseUpdate(v,new mutable.HashSet[T])
v
}
// Add v to keys to maintain invariant
def addEdge(u: T, v: T) = {
edgeData.getOrElseUpdate(v, new mutable.HashSet[T])
edgeData.addBinding(u,v)
}
}
object DiGraph {
def apply[T](mdg: MutableDiGraph[T]) = new DiGraph((mdg.edgeData mapValues { _.toSet }).toMap[T, Set[T]])
def apply[T](edgeData: MultiMap[T,T]) = new DiGraph((edgeData mapValues { _.toSet }).toMap[T, Set[T]])
}
class DiGraph[T] (val edges: Map[T, Set[T]]) {
def getVertices = edges.keys
def getEdges(v: T) = edges.getOrElse(v, new HashSet[T])
// Graph must be acyclic for valid linearization
def linearize(root: T) = {
val order = new mutable.ArrayBuffer[T]
val visited = new mutable.HashSet[T]
def explore(v: T): Unit = {
visited += v
for (u <- getEdges(v)) {
if (!visited.contains(u)) {
explore(u)
}
}
order.append(v)
}
explore(root)
order.reverse.toList
}
def doBFS(root: T) = {
val prev = new mutable.HashMap[T,T]
val queue = new mutable.Queue[T]
queue.enqueue(root)
while (!queue.isEmpty) {
val u = queue.dequeue
for (v <- getEdges(u)) {
if (!prev.contains(v)) {
prev(v) = u
queue.enqueue(v)
}
}
}
prev
}
def reachabilityBFS(root: T) = doBFS(root).keys.toSet
def path(start: T, end: T) = {
val nodePath = new mutable.ArrayBuffer[T]
val prev = doBFS(start)
nodePath += end
while (nodePath.last != start) {
nodePath += prev(nodePath.last)
}
nodePath.toList.reverse
}
def findSCCs = {
var counter: BigInt = 0
val stack = new mutable.Stack[T]
val onstack = new mutable.HashSet[T]
val indices = new mutable.HashMap[T, BigInt]
val lowlinks = new mutable.HashMap[T, BigInt]
val sccs = new mutable.ArrayBuffer[List[T]]
def strongConnect(v: T): Unit = {
indices(v) = counter
lowlinks(v) = counter
counter = counter + 1
stack.push(v)
onstack += v
for (w <- getEdges(v)) {
if (!indices.contains(w)) {
strongConnect(w)
lowlinks(v) = lowlinks(v).min(lowlinks(w))
} else if (onstack.contains(w)) {
lowlinks(v) = lowlinks(v).min(indices(w))
}
}
if (lowlinks(v) == indices(v)) {
val scc = new mutable.ArrayBuffer[T]
do {
val w = stack.pop
onstack -= w
scc += w
}
while (scc.last != v);
sccs.append(scc.toList)
}
}
for (v <- getVertices) {
strongConnect(v)
}
sccs.toList
}
def pathsInDAG(start: T): Map[T,List[List[T]]] = {
// paths(v) holds the set of paths from start to v
val paths = new mutable.HashMap[T,mutable.Set[List[T]]] with mutable.MultiMap[T,List[T]]
val queue = new mutable.Queue[T]
val visited = new mutable.HashSet[T]
paths.addBinding(start,List(start))
queue.enqueue(start)
visited += start
while (!queue.isEmpty) {
val current = queue.dequeue
for (v <- getEdges(current)) {
if (!visited.contains(v)) {
queue.enqueue(v)
visited += v
}
for (p <- paths(current)) {
paths.addBinding(v, p :+ v)
}
}
}
(paths map { case (k,v) => (k,v.toList) }).toMap
}
def reverse = {
val mdg = new MutableDiGraph[T]
edges foreach { case (u,edges) => edges.foreach({ v => mdg.addEdge(v,u) }) }
DiGraph(mdg)
}
def simplify(vprime: Set[T]) = {
val eprime = vprime.map( v => (v,reachabilityBFS(v) & vprime) ).toMap
new DiGraph(eprime)
}
def transformNodes[Q](f: (T) => Q): DiGraph[Q] = {
val eprime = edges.map({ case (k,v) => (f(k),v.map(f(_))) })
new DiGraph(eprime)
}
}

View File

@@ -0,0 +1,65 @@
package barstools.tapeout.transforms
import firrtl._
import firrtl.annotations._
import firrtl.passes._
import firrtl.ir._
object WriteConfig {
def apply(dir: String, file: String, contents: String): Unit = {
val writer = new java.io.PrintWriter(new java.io.File(s"$dir/$file"))
writer write contents
writer.close()
}
}
object GetTargetDir {
def apply(state: CircuitState): String = {
val annos = state.annotations.getOrElse(AnnotationMap(Seq.empty)).annotations
val destDir = annos.map {
case Annotation(f, t, s) if t == classOf[transforms.BlackBoxSourceHelper] =>
transforms.BlackBoxSource.parse(s) match {
case Some(transforms.BlackBoxTargetDir(dest)) => Some(dest)
case _ => None
}
case _ => None
}.flatten
val loc = {
if (destDir.isEmpty) "."
else destDir.head
}
val targetDir = new java.io.File(loc)
if(!targetDir.exists()) FileUtils.makeDirectory(targetDir.getAbsolutePath)
loc
}
}
// Fake transform just to track Technology information directory
object TechnologyLocation {
def apply(dir: String): Annotation = {
Annotation(CircuitName("All"), classOf[TechnologyLocation], dir)
}
}
class TechnologyLocation extends Transform {
def inputForm: CircuitForm = LowForm
def outputForm: CircuitForm = LowForm
def execute(state: CircuitState) = throw new Exception("Technology Location transform execution doesn't work!")
def get(state: CircuitState): String = {
val annos = state.annotations.getOrElse(AnnotationMap(Seq.empty)).annotations
val dir = annos.map {
case Annotation(f, t, s) if t == classOf[TechnologyLocation] => Some(s)
case _ => None
}.flatten
dir.length match {
case 0 => ""
case 1 =>
val targetDir = new java.io.File(dir.head)
if(!targetDir.exists()) throw new Exception("Technology yaml directory doesn't exist!")
dir.head
case _ => throw new Exception("Only 1 tech directory annotation allowed!")
}
}
}

View File

@@ -0,0 +1,51 @@
package firrtl.analyses
import scala.collection.mutable
import firrtl._
import firrtl.ir._
import firrtl.Utils._
import firrtl.Mappers._
class InstanceGraph(c: Circuit) {
private def collectInstances(insts: mutable.Set[WDefInstance])(s: Statement): Statement = s match {
case i: WDefInstance =>
insts += i
i
case _ =>
s map collectInstances(insts)
s
}
val moduleMap = c.modules.map({m => (m.name,m) }).toMap
val childInstances =
new mutable.HashMap[String,mutable.Set[WDefInstance]]
for (m <- c.modules) {
childInstances(m.name) = new mutable.HashSet[WDefInstance]
m map collectInstances(childInstances(m.name))
}
val instanceGraph = new MutableDiGraph[WDefInstance]
val instanceQueue = new mutable.Queue[WDefInstance]
val topInstance = WDefInstance(c.main,c.main) // top instance
instanceQueue.enqueue(topInstance)
while (!instanceQueue.isEmpty) {
val current = instanceQueue.dequeue
for (child <- childInstances(current.module)) {
if (!instanceGraph.contains(child)) {
instanceQueue.enqueue(child)
}
instanceGraph.addEdge(current,child)
}
}
val graph = DiGraph(instanceGraph)
lazy val fullHierarchy = graph.pathsInDAG(topInstance)
def findInstancesInHierarchy(module: String): List[List[WDefInstance]] = {
val instances = graph.getVertices.filter(_.module == module).toList
instances flatMap { i => fullHierarchy(i) }
}
}

View File

@@ -0,0 +1,5 @@
package barstools.tapeout.transforms
object LowerName {
def apply(s: String): String = s.replace(".", "_").replace("[", "_")replace("]", "")
}

View File

@@ -0,0 +1,24 @@
package barstools.tapeout.transforms
import chisel3._
import scala.collection.immutable.ListMap
final class CustomBundle(elts: (String, Data)*) extends Record {
val elements = ListMap(elts map { case (field, elt) => field -> elt.chiselCloneType }: _*)
def apply(elt: String): Data = elements(elt)
override def cloneType = (new CustomBundle(elements.toList: _*)).asInstanceOf[this.type]
}
final class CustomIndexedBundle(elts: (Int, Data)*) extends Record {
// Must be String, Data
val elements = ListMap(elts map { case (field, elt) => field.toString -> elt.chiselCloneType }: _*)
def indexedElements = ListMap(elts map { case (field, elt) => field -> elt.chiselCloneType }: _*)
def apply(elt: Int): Data = elements(elt.toString)
override def cloneType = (new CustomIndexedBundle(indexedElements.toList: _*)).asInstanceOf[this.type]
}
object CustomIndexedBundle {
def apply(gen: Data, idxs: Seq[Int]) = new CustomIndexedBundle(idxs.map(_ -> gen): _*)
// Allows Vecs of elements of different types/widths
def apply(gen: Seq[Data]) = new CustomIndexedBundle(gen.zipWithIndex.map{ case (elt, field) => field -> elt }: _*)
}

View File

@@ -0,0 +1,21 @@
package barstools.tapeout.transforms
import net.jcazevedo.moultingyaml._
import java.io.File
class YamlFileReader(resource: String) {
def parse[A](file: String = "")(implicit reader: YamlReader[A]) : Seq[A] = {
// If the user doesn't provide a Yaml file name, use defaults
val yamlString = file match {
case f if f.isEmpty =>
// Use example config if no file is provided
val stream = getClass.getResourceAsStream(resource)
io.Source.fromInputStream(stream).mkString
case f if new File(f).exists =>
scala.io.Source.fromFile(f).getLines.mkString("\n")
case _ =>
throw new Exception("No valid Yaml file found!")
}
yamlString.parseYamls.map(x => reader.read(x))
}
}

View File

@@ -0,0 +1,231 @@
module ExampleTopModuleWithBB_PadFrame(
output clock_Int,
output reset_Int,
output [14:0] io_a_Int,
output [14:0] io_b_Int,
output [13:0] io_c_Int,
input [15:0] io_x_Int,
input [15:0] io_y_Int,
input [15:0] io_z_Int,
input [4:0] io_v_0_Int,
input [4:0] io_v_1_Int,
input [4:0] io_v_2_Int,
input clock_Ext,
input reset_Ext,
input [14:0] io_a_Ext,
input [14:0] io_b_Ext,
input [13:0] io_c_Ext,
output [15:0] io_x_Ext,
output [15:0] io_y_Ext,
output [15:0] io_z_Ext,
inout [2:0] io_analog1_Ext,
inout [2:0] io_analog2_Ext,
output [4:0] io_v_0_Ext,
output [4:0] io_v_1_Ext,
output [4:0] io_v_2_Ext
);
wire pad_digital_from_tristate_foundry_vertical_input_array_reset_in;
wire pad_digital_from_tristate_foundry_vertical_input_array_reset_out;
wire [14:0] pad_digital_from_tristate_foundry_horizontal_input_array_io_a_in;
wire [14:0] pad_digital_from_tristate_foundry_horizontal_input_array_io_a_out;
wire [14:0] pad_digital_from_tristate_foundry_horizontal_input_array_io_b_in;
wire [14:0] pad_digital_from_tristate_foundry_horizontal_input_array_io_b_out;
wire [13:0] pad_digital_from_tristate_foundry_horizontal_input_array_io_c_in;
wire [13:0] pad_digital_from_tristate_foundry_horizontal_input_array_io_c_out;
wire [15:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_x_in;
wire [15:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_x_out;
wire [15:0] pad_digital_from_tristate_foundry_vertical_output_array_io_z_in;
wire [15:0] pad_digital_from_tristate_foundry_vertical_output_array_io_z_out;
wire [4:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0_in;
wire [4:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0_out;
wire [4:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1_in;
wire [4:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1_out;
wire [4:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2_in;
wire [4:0] pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2_out;
pad_digital_from_tristate_foundry_vertical_input_array #(.WIDTH(1)) pad_digital_from_tristate_foundry_vertical_input_array_reset (
.in(pad_digital_from_tristate_foundry_vertical_input_array_reset_in),
.out(pad_digital_from_tristate_foundry_vertical_input_array_reset_out)
);
pad_digital_from_tristate_foundry_horizontal_input_array #(.WIDTH(15)) pad_digital_from_tristate_foundry_horizontal_input_array_io_a (
.in(pad_digital_from_tristate_foundry_horizontal_input_array_io_a_in),
.out(pad_digital_from_tristate_foundry_horizontal_input_array_io_a_out)
);
pad_digital_from_tristate_foundry_horizontal_input_array #(.WIDTH(15)) pad_digital_from_tristate_foundry_horizontal_input_array_io_b (
.in(pad_digital_from_tristate_foundry_horizontal_input_array_io_b_in),
.out(pad_digital_from_tristate_foundry_horizontal_input_array_io_b_out)
);
pad_digital_from_tristate_foundry_horizontal_input_array #(.WIDTH(14)) pad_digital_from_tristate_foundry_horizontal_input_array_io_c (
.in(pad_digital_from_tristate_foundry_horizontal_input_array_io_c_in),
.out(pad_digital_from_tristate_foundry_horizontal_input_array_io_c_out)
);
pad_digital_from_tristate_foundry_horizontal_output_array #(.WIDTH(16)) pad_digital_from_tristate_foundry_horizontal_output_array_io_x (
.in(pad_digital_from_tristate_foundry_horizontal_output_array_io_x_in),
.out(pad_digital_from_tristate_foundry_horizontal_output_array_io_x_out)
);
pad_digital_from_tristate_foundry_vertical_output_array #(.WIDTH(16)) pad_digital_from_tristate_foundry_vertical_output_array_io_z (
.in(pad_digital_from_tristate_foundry_vertical_output_array_io_z_in),
.out(pad_digital_from_tristate_foundry_vertical_output_array_io_z_out)
);
pad_analog_fast_custom_horizontal_array #(.WIDTH(3)) pad_analog_fast_custom_horizontal_array_io_analog1 (
.io(io_analog1_Ext)
);
pad_analog_slow_foundry_vertical_array #(.WIDTH(3)) pad_analog_slow_foundry_vertical_array_io_analog2 (
.io(io_analog2_Ext)
);
pad_digital_from_tristate_foundry_horizontal_output_array #(.WIDTH(5)) pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0 (
.in(pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0_in),
.out(pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0_out)
);
pad_digital_from_tristate_foundry_horizontal_output_array #(.WIDTH(5)) pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1 (
.in(pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1_in),
.out(pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1_out)
);
pad_digital_from_tristate_foundry_horizontal_output_array #(.WIDTH(5)) pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2 (
.in(pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2_in),
.out(pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2_out)
);
pad_supply_vdd_horizontal pad_supply_vdd_horizontal_left_0 (
);
pad_supply_vdd_horizontal pad_supply_vdd_horizontal_left_1 (
);
pad_supply_vdd_horizontal pad_supply_vdd_horizontal_left_2 (
);
pad_supply_vdd_vertical pad_supply_vdd_vertical_bottom_0 (
);
pad_supply_vdd_vertical pad_supply_vdd_vertical_bottom_1 (
);
pad_supply_vss_horizontal pad_supply_vss_horizontal_right_0 (
);
assign clock_Int = clock_Ext;
assign reset_Int = pad_digital_from_tristate_foundry_vertical_input_array_reset_out;
assign io_a_Int = pad_digital_from_tristate_foundry_horizontal_input_array_io_a_out;
assign io_b_Int = pad_digital_from_tristate_foundry_horizontal_input_array_io_b_out;
assign io_c_Int = $signed(pad_digital_from_tristate_foundry_horizontal_input_array_io_c_out);
assign io_x_Ext = pad_digital_from_tristate_foundry_horizontal_output_array_io_x_out;
assign io_y_Ext = io_y_Int;
assign io_z_Ext = $signed(pad_digital_from_tristate_foundry_vertical_output_array_io_z_out);
assign io_v_0_Ext = pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0_out;
assign io_v_1_Ext = pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1_out;
assign io_v_2_Ext = pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2_out;
assign pad_digital_from_tristate_foundry_vertical_input_array_reset_in = reset_Ext;
assign pad_digital_from_tristate_foundry_horizontal_input_array_io_a_in = io_a_Ext;
assign pad_digital_from_tristate_foundry_horizontal_input_array_io_b_in = io_b_Ext;
assign pad_digital_from_tristate_foundry_horizontal_input_array_io_c_in = $unsigned(io_c_Ext);
assign pad_digital_from_tristate_foundry_horizontal_output_array_io_x_in = io_x_Int;
assign pad_digital_from_tristate_foundry_vertical_output_array_io_z_in = $unsigned(io_z_Int);
assign pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0_in = io_v_0_Int;
assign pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1_in = io_v_1_Int;
assign pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2_in = io_v_2_Int;
endmodule
module ExampleTopModuleWithBB(
input clock,
input reset,
input [14:0] io_a,
input [14:0] io_b,
input [13:0] io_c,
output [15:0] io_x,
output [15:0] io_y,
output [15:0] io_z,
inout [2:0] io_analog1,
inout [2:0] io_analog2,
output [4:0] io_v_0,
output [4:0] io_v_1,
output [4:0] io_v_2
);
wire ExampleTopModuleWithBB_PadFrame_clock_Int;
wire ExampleTopModuleWithBB_PadFrame_reset_Int;
wire [14:0] ExampleTopModuleWithBB_PadFrame_io_a_Int;
wire [14:0] ExampleTopModuleWithBB_PadFrame_io_b_Int;
wire [13:0] ExampleTopModuleWithBB_PadFrame_io_c_Int;
wire [15:0] ExampleTopModuleWithBB_PadFrame_io_x_Int;
wire [15:0] ExampleTopModuleWithBB_PadFrame_io_y_Int;
wire [15:0] ExampleTopModuleWithBB_PadFrame_io_z_Int;
wire [4:0] ExampleTopModuleWithBB_PadFrame_io_v_0_Int;
wire [4:0] ExampleTopModuleWithBB_PadFrame_io_v_1_Int;
wire [4:0] ExampleTopModuleWithBB_PadFrame_io_v_2_Int;
wire ExampleTopModuleWithBB_PadFrame_clock_Ext;
wire ExampleTopModuleWithBB_PadFrame_reset_Ext;
wire [14:0] ExampleTopModuleWithBB_PadFrame_io_a_Ext;
wire [14:0] ExampleTopModuleWithBB_PadFrame_io_b_Ext;
wire [13:0] ExampleTopModuleWithBB_PadFrame_io_c_Ext;
wire [15:0] ExampleTopModuleWithBB_PadFrame_io_x_Ext;
wire [15:0] ExampleTopModuleWithBB_PadFrame_io_y_Ext;
wire [15:0] ExampleTopModuleWithBB_PadFrame_io_z_Ext;
wire [4:0] ExampleTopModuleWithBB_PadFrame_io_v_0_Ext;
wire [4:0] ExampleTopModuleWithBB_PadFrame_io_v_1_Ext;
wire [4:0] ExampleTopModuleWithBB_PadFrame_io_v_2_Ext;
wire ExampleTopModuleWithBB_Internal_clock;
wire ExampleTopModuleWithBB_Internal_reset;
wire [14:0] ExampleTopModuleWithBB_Internal_io_a;
wire [14:0] ExampleTopModuleWithBB_Internal_io_b;
wire [13:0] ExampleTopModuleWithBB_Internal_io_c;
wire [15:0] ExampleTopModuleWithBB_Internal_io_x;
wire [15:0] ExampleTopModuleWithBB_Internal_io_y;
wire [15:0] ExampleTopModuleWithBB_Internal_io_z;
wire [4:0] ExampleTopModuleWithBB_Internal_io_v_0;
wire [4:0] ExampleTopModuleWithBB_Internal_io_v_1;
wire [4:0] ExampleTopModuleWithBB_Internal_io_v_2;
ExampleTopModuleWithBB_PadFrame ExampleTopModuleWithBB_PadFrame (
.clock_Int(ExampleTopModuleWithBB_PadFrame_clock_Int),
.reset_Int(ExampleTopModuleWithBB_PadFrame_reset_Int),
.io_a_Int(ExampleTopModuleWithBB_PadFrame_io_a_Int),
.io_b_Int(ExampleTopModuleWithBB_PadFrame_io_b_Int),
.io_c_Int(ExampleTopModuleWithBB_PadFrame_io_c_Int),
.io_x_Int(ExampleTopModuleWithBB_PadFrame_io_x_Int),
.io_y_Int(ExampleTopModuleWithBB_PadFrame_io_y_Int),
.io_z_Int(ExampleTopModuleWithBB_PadFrame_io_z_Int),
.io_v_0_Int(ExampleTopModuleWithBB_PadFrame_io_v_0_Int),
.io_v_1_Int(ExampleTopModuleWithBB_PadFrame_io_v_1_Int),
.io_v_2_Int(ExampleTopModuleWithBB_PadFrame_io_v_2_Int),
.clock_Ext(ExampleTopModuleWithBB_PadFrame_clock_Ext),
.reset_Ext(ExampleTopModuleWithBB_PadFrame_reset_Ext),
.io_a_Ext(ExampleTopModuleWithBB_PadFrame_io_a_Ext),
.io_b_Ext(ExampleTopModuleWithBB_PadFrame_io_b_Ext),
.io_c_Ext(ExampleTopModuleWithBB_PadFrame_io_c_Ext),
.io_x_Ext(ExampleTopModuleWithBB_PadFrame_io_x_Ext),
.io_y_Ext(ExampleTopModuleWithBB_PadFrame_io_y_Ext),
.io_z_Ext(ExampleTopModuleWithBB_PadFrame_io_z_Ext),
.io_analog1_Ext(io_analog1),
.io_analog2_Ext(io_analog2),
.io_v_0_Ext(ExampleTopModuleWithBB_PadFrame_io_v_0_Ext),
.io_v_1_Ext(ExampleTopModuleWithBB_PadFrame_io_v_1_Ext),
.io_v_2_Ext(ExampleTopModuleWithBB_PadFrame_io_v_2_Ext)
);
ExampleTopModuleWithBB_Internal ExampleTopModuleWithBB_Internal (
.clock(ExampleTopModuleWithBB_Internal_clock),
.reset(ExampleTopModuleWithBB_Internal_reset),
.io_a(ExampleTopModuleWithBB_Internal_io_a),
.io_b(ExampleTopModuleWithBB_Internal_io_b),
.io_c(ExampleTopModuleWithBB_Internal_io_c),
.io_x(ExampleTopModuleWithBB_Internal_io_x),
.io_y(ExampleTopModuleWithBB_Internal_io_y),
.io_z(ExampleTopModuleWithBB_Internal_io_z),
.io_analog1(io_analog1),
.io_analog2(io_analog2),
.io_v_0(ExampleTopModuleWithBB_Internal_io_v_0),
.io_v_1(ExampleTopModuleWithBB_Internal_io_v_1),
.io_v_2(ExampleTopModuleWithBB_Internal_io_v_2)
);
assign io_x = ExampleTopModuleWithBB_PadFrame_io_x_Ext;
assign io_y = ExampleTopModuleWithBB_PadFrame_io_y_Ext;
assign io_z = ExampleTopModuleWithBB_PadFrame_io_z_Ext;
assign io_v_0 = ExampleTopModuleWithBB_PadFrame_io_v_0_Ext;
assign io_v_1 = ExampleTopModuleWithBB_PadFrame_io_v_1_Ext;
assign io_v_2 = ExampleTopModuleWithBB_PadFrame_io_v_2_Ext;
assign ExampleTopModuleWithBB_PadFrame_io_x_Int = ExampleTopModuleWithBB_Internal_io_x;
assign ExampleTopModuleWithBB_PadFrame_io_y_Int = ExampleTopModuleWithBB_Internal_io_y;
assign ExampleTopModuleWithBB_PadFrame_io_z_Int = ExampleTopModuleWithBB_Internal_io_z;
assign ExampleTopModuleWithBB_PadFrame_io_v_0_Int = ExampleTopModuleWithBB_Internal_io_v_0;
assign ExampleTopModuleWithBB_PadFrame_io_v_1_Int = ExampleTopModuleWithBB_Internal_io_v_1;
assign ExampleTopModuleWithBB_PadFrame_io_v_2_Int = ExampleTopModuleWithBB_Internal_io_v_2;
assign ExampleTopModuleWithBB_PadFrame_clock_Ext = clock;
assign ExampleTopModuleWithBB_PadFrame_reset_Ext = reset;
assign ExampleTopModuleWithBB_PadFrame_io_a_Ext = io_a;
assign ExampleTopModuleWithBB_PadFrame_io_b_Ext = io_b;
assign ExampleTopModuleWithBB_PadFrame_io_c_Ext = io_c;
assign ExampleTopModuleWithBB_Internal_clock = ExampleTopModuleWithBB_PadFrame_clock_Int;
assign ExampleTopModuleWithBB_Internal_reset = ExampleTopModuleWithBB_PadFrame_reset_Int;
assign ExampleTopModuleWithBB_Internal_io_a = ExampleTopModuleWithBB_PadFrame_io_a_Int;
assign ExampleTopModuleWithBB_Internal_io_b = ExampleTopModuleWithBB_PadFrame_io_b_Int;
assign ExampleTopModuleWithBB_Internal_io_c = ExampleTopModuleWithBB_PadFrame_io_c_Int;
endmodule

View File

@@ -0,0 +1,236 @@
(globals
version = 3
io_order = default
)
(iopad
(bottomleft
(inst name="corner_ll" cell="CORNER_EXAMPLE" )
)
(bottomright
(inst name="corner_lr" orientation=MY cell="CORNER_EXAMPLE" )
)
(topleft
(inst name="corner_ul" orientation=MX cell="CORNER_EXAMPLE" )
)
(topright
(inst name="corner_ur" cell="CORNER_EXAMPLE" )
)
(left
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vdd_horizontal_left_0/PAD[0]") # Side: 1, Order: 0
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vdd_horizontal_left_1/PAD[0]") # Side: 1, Order: 1
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vdd_horizontal_left_2/PAD[0]") # Side: 1, Order: 2
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[0]/PAD") # Side: 1, Order: 3
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[1]/PAD") # Side: 1, Order: 4
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[2]/PAD") # Side: 1, Order: 5
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[3]/PAD") # Side: 1, Order: 6
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[4]/PAD") # Side: 1, Order: 7
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[5]/PAD") # Side: 1, Order: 8
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[6]/PAD") # Side: 1, Order: 9
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[7]/PAD") # Side: 1, Order: 10
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[8]/PAD") # Side: 1, Order: 11
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[9]/PAD") # Side: 1, Order: 12
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[10]/PAD") # Side: 1, Order: 13
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[11]/PAD") # Side: 1, Order: 14
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[12]/PAD") # Side: 1, Order: 15
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[13]/PAD") # Side: 1, Order: 16
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_a/pad_digital_from_tristate_foundry_horizontal_input[14]/PAD") # Side: 1, Order: 17
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[0]/PAD") # Side: 1, Order: 18
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[1]/PAD") # Side: 1, Order: 19
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[2]/PAD") # Side: 1, Order: 20
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[3]/PAD") # Side: 1, Order: 21
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[4]/PAD") # Side: 1, Order: 22
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[5]/PAD") # Side: 1, Order: 23
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[6]/PAD") # Side: 1, Order: 24
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[7]/PAD") # Side: 1, Order: 25
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[8]/PAD") # Side: 1, Order: 26
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[9]/PAD") # Side: 1, Order: 27
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[10]/PAD") # Side: 1, Order: 28
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[11]/PAD") # Side: 1, Order: 29
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[12]/PAD") # Side: 1, Order: 30
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[13]/PAD") # Side: 1, Order: 31
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_b/pad_digital_from_tristate_foundry_horizontal_input[14]/PAD") # Side: 1, Order: 32
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[0]/PAD") # Side: 1, Order: 33
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[1]/PAD") # Side: 1, Order: 34
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[2]/PAD") # Side: 1, Order: 35
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[3]/PAD") # Side: 1, Order: 36
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[4]/PAD") # Side: 1, Order: 37
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[5]/PAD") # Side: 1, Order: 38
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[6]/PAD") # Side: 1, Order: 39
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[7]/PAD") # Side: 1, Order: 40
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[8]/PAD") # Side: 1, Order: 41
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[9]/PAD") # Side: 1, Order: 42
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[10]/PAD") # Side: 1, Order: 43
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[11]/PAD") # Side: 1, Order: 44
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[12]/PAD") # Side: 1, Order: 45
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_input_array_io_c/pad_digital_from_tristate_foundry_horizontal_input[13]/PAD") # Side: 1, Order: 46
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[0]/PAD") # Side: 1, Order: 47
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[1]/PAD") # Side: 1, Order: 48
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[2]/PAD") # Side: 1, Order: 49
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[3]/PAD") # Side: 1, Order: 50
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[4]/PAD") # Side: 1, Order: 51
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[5]/PAD") # Side: 1, Order: 52
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[6]/PAD") # Side: 1, Order: 53
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[7]/PAD") # Side: 1, Order: 54
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[8]/PAD") # Side: 1, Order: 55
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[9]/PAD") # Side: 1, Order: 56
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[10]/PAD") # Side: 1, Order: 57
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[11]/PAD") # Side: 1, Order: 58
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[12]/PAD") # Side: 1, Order: 59
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[13]/PAD") # Side: 1, Order: 60
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[14]/PAD") # Side: 1, Order: 61
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_x/pad_digital_from_tristate_foundry_horizontal_output[15]/PAD") # Side: 1, Order: 62
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_analog_fast_custom_horizontal_array_io_analog1/pad_analog_fast_custom_horizontal[0]/PAD") # Side: 1, Order: 63
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_analog_fast_custom_horizontal_array_io_analog1/pad_analog_fast_custom_horizontal[1]/PAD") # Side: 1, Order: 64
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_analog_fast_custom_horizontal_array_io_analog1/pad_analog_fast_custom_horizontal[2]/PAD") # Side: 1, Order: 65
)
(right
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vss_horizontal_right_0/PAD[0]") # Side: 3, Order: 0
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vss_horizontal_right_0/PAD[1]") # Side: 3, Order: 1
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0/pad_digital_from_tristate_foundry_horizontal_output[0]/PAD") # Side: 3, Order: 2
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0/pad_digital_from_tristate_foundry_horizontal_output[1]/PAD") # Side: 3, Order: 3
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0/pad_digital_from_tristate_foundry_horizontal_output[2]/PAD") # Side: 3, Order: 4
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0/pad_digital_from_tristate_foundry_horizontal_output[3]/PAD") # Side: 3, Order: 5
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_0/pad_digital_from_tristate_foundry_horizontal_output[4]/PAD") # Side: 3, Order: 6
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1/pad_digital_from_tristate_foundry_horizontal_output[0]/PAD") # Side: 3, Order: 7
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1/pad_digital_from_tristate_foundry_horizontal_output[1]/PAD") # Side: 3, Order: 8
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1/pad_digital_from_tristate_foundry_horizontal_output[2]/PAD") # Side: 3, Order: 9
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1/pad_digital_from_tristate_foundry_horizontal_output[3]/PAD") # Side: 3, Order: 10
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_1/pad_digital_from_tristate_foundry_horizontal_output[4]/PAD") # Side: 3, Order: 11
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2/pad_digital_from_tristate_foundry_horizontal_output[0]/PAD") # Side: 3, Order: 12
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2/pad_digital_from_tristate_foundry_horizontal_output[1]/PAD") # Side: 3, Order: 13
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2/pad_digital_from_tristate_foundry_horizontal_output[2]/PAD") # Side: 3, Order: 14
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2/pad_digital_from_tristate_foundry_horizontal_output[3]/PAD") # Side: 3, Order: 15
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_horizontal_output_array_io_v_2/pad_digital_from_tristate_foundry_horizontal_output[4]/PAD") # Side: 3, Order: 16
)
(top
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_input_array_reset/pad_digital_from_tristate_foundry_vertical_input[0]/PAD") # Side: 2, Order: 0
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[0]/PAD") # Side: 2, Order: 1
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[1]/PAD") # Side: 2, Order: 2
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[2]/PAD") # Side: 2, Order: 3
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[3]/PAD") # Side: 2, Order: 4
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[4]/PAD") # Side: 2, Order: 5
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[5]/PAD") # Side: 2, Order: 6
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[6]/PAD") # Side: 2, Order: 7
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[7]/PAD") # Side: 2, Order: 8
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[8]/PAD") # Side: 2, Order: 9
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[9]/PAD") # Side: 2, Order: 10
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[10]/PAD") # Side: 2, Order: 11
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[11]/PAD") # Side: 2, Order: 12
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[12]/PAD") # Side: 2, Order: 13
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[13]/PAD") # Side: 2, Order: 14
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[14]/PAD") # Side: 2, Order: 15
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_digital_from_tristate_foundry_vertical_output_array_io_z/pad_digital_from_tristate_foundry_vertical_output[15]/PAD") # Side: 2, Order: 16
)
(bottom
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vdd_vertical_bottom_0/PAD[0]") # Side: 4, Order: 0
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_supply_vdd_vertical_bottom_1/PAD[0]") # Side: 4, Order: 1
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_analog_slow_foundry_vertical_array_io_analog2/pad_analog_slow_foundry_vertical[0]/PAD") # Side: 4, Order: 2
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_analog_slow_foundry_vertical_array_io_analog2/pad_analog_slow_foundry_vertical[1]/PAD") # Side: 4, Order: 3
(inst name = "ExampleTopModuleWithBB_PadFrame/pad_analog_slow_foundry_vertical_array_io_analog2/pad_analog_slow_foundry_vertical[2]/PAD") # Side: 4, Order: 4
)
)

View File

@@ -0,0 +1,181 @@
// 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)
}
}

View File

@@ -0,0 +1,226 @@
// See LICENSE for license details.
package barstools.tapeout.transforms.pads
import chisel3._
import firrtl._
import org.scalatest.{FlatSpec, Matchers}
import chisel3.experimental._
import chisel3.util.HasBlackBoxInline
import chisel3.iotesters._
class BB extends BlackBox with HasBlackBoxInline {
val io = IO(new Bundle {
val c = Input(SInt(14.W))
val z = Output(SInt(16.W))
val analog1 = Analog(3.W)
val analog2 = analog1.chiselCloneType
})
// Generates a "FakeBB.v" file with the following Verilog module
setInline("FakeBB.v",
s"""
|module BB(
| input [15:0] c,
| output [15:0] z,
| inout [2:0] analog1,
| inout [2:0] analog2
|);
| always @* begin
| z = 2 * c;
| analog2 = analog1 + 1;
| end
|endmodule
""".stripMargin)
}
// If no template file is provided, it'll use the default one (example) in the resource folder
// Default pad side is Top if no side is specified for a given IO
// You can designate the number of different supply pads on each chip side
class ExampleTopModuleWithBB extends TopModule(
supplyAnnos = Seq(
SupplyAnnotation(padName = "vdd", leftSide = 3, bottomSide = 2),
SupplyAnnotation(padName = "vss", rightSide = 1)
)) {
val io = IO(new Bundle {
val a = Input(UInt(15.W))
val b = a.chiselCloneType
val c = Input(SInt(14.W))
val x = Output(UInt(16.W))
val y = x.chiselCloneType
val z = Output(SInt(16.W))
val analog1 = Analog(3.W)
val analog2 = analog1.chiselCloneType
val v = Output(Vec(3, UInt(5.W)))
})
// Can annotate aggregates with pad side location + pad name (should be a name in the yaml template)
annotatePad(io.v, Right, "from_tristate_foundry")
// Can annotate individual elements
annotatePad(io.analog1, Left, "fast_custom")
annotatePad(io.analog2, Bottom, "slow_foundry")
// Looks for a pad that matches the IO type (digital in, digital out, analog) if no name is specified
Seq(io.a, io.b, io.c, io.x) foreach { x => annotatePad(x, Left) }
// Some signals might not want pads associated with them
noPad(io.y)
// Clk might come directly from bump
noPad(clock)
val bb = Module(new BB())
bb.io.c := io.c
io.z := bb.io.z
bb.io.analog1 <> io.analog1
bb.io.analog2 <> io.analog2
io.x := io.a + 1.U
io.y := io.b - 1.U
io.v foreach { lhs => lhs := io.a }
}
class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c) {
val ax = Seq(5, 3)
val bx = Seq(8, 2)
val cx = Seq(-11, -9)
for (i <- 0 until ax.length) {
poke(c.io.a, ax(i))
poke(c.io.b, bx(i))
poke(c.io.c, cx(i))
expect(c.io.x, ax(i) + 1)
expect(c.io.y, bx(i) - 1)
expect(c.io.z, 2 * cx(i))
c.io.v foreach { out => expect(out, ax(i)) }
}
// Analog can't be peeked + poked
}
// Notes: Annotations
// a in 15: left, default digital
// b in 15: left, default digital
// c in 14: left, default digital ; signed
// x out 16: left, default digital
// y out: NOPAD
// clk in: NOPAD
// analog1 3: left, fast_custom
// analog2 3: bottom, slow_foundry
// v (vec of 3 with 5, out): right, from_tristate_foundry
// reset in: UNSPECIFIED: top, default digital
// z out 16: UNSPECIFIED: top, default digital ; signed
// vdd, left: 3, group of 1
// vdd, bottom: 2, group of 1
// vss, right: 1, group of 2
// Notes: Used pads
// digital horizontal (from_tristate_foundry)
// in + out
// analog fast_custom horizontal
// analog slow_foundry vertical
// digital vertical (from_tristate_foundry)
// in + out
// vdd horizontal
// vdd vertical
// vss horizontal
class IOPadSpec extends FlatSpec with Matchers {
def readOutputFile(dir: String, f: String): String =
scala.io.Source.fromFile(Seq(dir, f).mkString("/")).getLines.mkString("\n")
def readResource(resource: String): String = {
val stream = getClass.getResourceAsStream(resource)
scala.io.Source.fromInputStream(stream).mkString
}
def checkOutputs(dir: String) = {
// Show that black box source helper is run
//readOutputFile(dir, "black_box_verilog_files.f") should include ("pad_supply_vdd_horizontal.v")
val padBBEx = s"""// Digital Pad Example
|// Signal Direction: Input
|// Pad Orientation: Horizontal
|// Call your instance PAD
|module pad_digital_from_tristate_foundry_horizontal_input(
| input in,
| output reg out
|);
| // Where you would normally dump your pad instance
| always @* begin
| out = in;
| end
|endmodule
|
|module pad_digital_from_tristate_foundry_horizontal_input_array #(
| parameter int WIDTH=1
|)(
| input [WIDTH-1:0] in,
| output reg [WIDTH-1:0] out
|);
| pad_digital_from_tristate_foundry_horizontal_input pad_digital_from_tristate_foundry_horizontal_input[WIDTH-1:0](
| .in(in),
| .out(out)
| );""".stripMargin
// Make sure black box templating is OK
readOutputFile(dir, "pad_digital_from_tristate_foundry_horizontal_input_array.v") should include (padBBEx)
val verilog = readOutputFile(dir, "ExampleTopModuleWithBB.v")
// Pad frame + top should be exact
verilog should include (readResource("/PadAnnotationVerilogPart.v"))
// Pad Placement IO file should be exact
val padIO = readOutputFile(dir, "pads.io")
padIO should include(readResource("/PadPlacement.io"))
}
behavior of "top module with blackbox"
import barstools.tapeout.transforms._
it should "pass simple testbench" in {
val optionsManager = new TesterOptionsManager {
firrtlOptions = firrtlOptions.copy(
compilerName = "verilog"
// annotations = List(TechnologyLocation("./RealTech"))
)
testerOptions = testerOptions.copy(isVerbose = true, backendName = "verilator", displayBase = 10)
commonOptions = commonOptions.copy(targetDirName = "test_run_dir/PadsTB")
}
iotesters.Driver.execute(() => new ExampleTopModuleWithBB, optionsManager) { c =>
val dir = optionsManager.commonOptions.targetDirName
checkOutputs(dir)
new SimpleTopModuleTester(c)
} should be (true)
}
/*
it should "create proper IO pads + black box in low firrtl" in {
val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions {
firrtlOptions = firrtlOptions.copy(compilerName = "low")
commonOptions = commonOptions.copy(targetDirName = "test_run_dir/LoFirrtl")
//commonOptions = commonOptions.copy(globalLogLevel = logger.LogLevel.Info)
}
val success = chisel3.Driver.execute(optionsManager, () => new ExampleTopModuleWithBB) match {
case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, firrtl))) =>
firrtl should include ("ExampleTopModuleWithBB_PadFrame")
firrtl should include ("ExampleTopModuleWithBB_Internal")
firrtl should not include ("FakeBBPlaceholder")
true
case _ => false
}
success should be (true)
}
*/
it should "create proper IO pads + black box in verilog" in {
val optionsManager = new ExecutionOptionsManager("barstools") with HasChiselExecutionOptions with HasFirrtlOptions {
firrtlOptions = firrtlOptions.copy(
compilerName = "verilog"
)
commonOptions = commonOptions.copy(targetDirName = "test_run_dir/PadsVerilog")
//commonOptions = commonOptions.copy(globalLogLevel = logger.LogLevel.Info)
}
val success = chisel3.Driver.execute(optionsManager, () => new ExampleTopModuleWithBB) match {
case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, verilog))) =>
true
case _ => false
}
success should be (true)
val dir = optionsManager.commonOptions.targetDirName
checkOutputs(dir)
}
}