Refactor tapeout for Chisel 3.4, Firrtl 1.4
- Remove clk package based on discussion with Colin - Annotations need to be refactored to using latest API - Generally that involves making annos generated by a anonymous ChiselAnnotation - The chisel annotations will use RunFirrtlTransform to queue up its associated transform - Chisel annotation provieds toFirrtl to generate Firrtl form of annotation - Usages of unapply on firrtl annotations cannot use generic unapply(target, transform, data) which has been eliminated - Have transforms use with DependencyAPIMigration to avoid deprecated `form`s - Added some 'see License comments - TechnologyLocation section of AddIOPadsSpec does not currently run because there is no content for it. - Added some tests for annotation serialization here
This commit is contained in:
@@ -2,64 +2,74 @@
|
||||
|
||||
package barstools.tapeout.transforms.pads
|
||||
|
||||
import java.io.File
|
||||
|
||||
import barstools.tapeout.transforms.HasSetTechnologyLocation
|
||||
import chisel3._
|
||||
import chisel3.experimental._
|
||||
import chisel3.iotesters._
|
||||
import chisel3.util.HasBlackBoxInline
|
||||
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
|
||||
val analog2 = Analog(3.W)
|
||||
})
|
||||
// Generates a "FakeBB.v" file with the following Verilog module
|
||||
setInline("FakeBB.v",
|
||||
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)
|
||||
|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)
|
||||
)) {
|
||||
class ExampleTopModuleWithBB
|
||||
extends TopModule(
|
||||
supplyAnnos = Seq(
|
||||
SupplyAnnotation(padName = "vdd", leftSide = 3, bottomSide = 2),
|
||||
SupplyAnnotation(padName = "vss", rightSide = 1)
|
||||
)
|
||||
)
|
||||
with HasSetTechnologyLocation {
|
||||
val io = IO(new Bundle {
|
||||
val a = Input(UInt(15.W))
|
||||
val b = a.chiselCloneType
|
||||
val b = Input(a.cloneType)
|
||||
val c = Input(SInt(14.W))
|
||||
val x = Output(UInt(16.W))
|
||||
val y = x.chiselCloneType
|
||||
val y = Output(x.cloneType)
|
||||
val z = Output(SInt(16.W))
|
||||
val analog1 = Analog(3.W)
|
||||
val analog2 = analog1.chiselCloneType
|
||||
val analog2 = analog1.cloneType
|
||||
val v = Output(Vec(3, UInt(5.W)))
|
||||
})
|
||||
|
||||
setTechnologyLocation("./RealTech")
|
||||
|
||||
// Can annotate aggregates with pad side location + pad name (should be a name in the yaml template)
|
||||
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) }
|
||||
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
|
||||
@@ -74,8 +84,8 @@ class ExampleTopModuleWithBB extends TopModule(
|
||||
io.x := io.a + 1.U
|
||||
io.y := io.b - 1.U
|
||||
|
||||
io.v foreach { lhs => lhs := io.a }
|
||||
|
||||
io.v.foreach { lhs => lhs := io.a }
|
||||
|
||||
}
|
||||
|
||||
class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c) {
|
||||
@@ -89,10 +99,10 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c)
|
||||
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)) }
|
||||
c.io.v.foreach { out => expect(out, ax(i)) }
|
||||
}
|
||||
// Analog can't be peeked + poked
|
||||
}
|
||||
// Analog can't be peeked + poked
|
||||
}
|
||||
|
||||
// Notes: Annotations
|
||||
// a in 15: left, default digital
|
||||
@@ -110,7 +120,7 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c)
|
||||
// vdd, bottom: 2, group of 1
|
||||
// vss, right: 1, group of 2
|
||||
// Notes: Used pads
|
||||
// digital horizontal (from_tristate_foundry)
|
||||
// digital horizontal (from_tristate_foundry)
|
||||
// in + out
|
||||
// analog fast_custom horizontal
|
||||
// analog slow_foundry vertical
|
||||
@@ -122,72 +132,111 @@ class SimpleTopModuleTester(c: ExampleTopModuleWithBB) extends PeekPokeTester(c)
|
||||
|
||||
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 readOutputFile(dir: String, f: String): String = {
|
||||
FileUtils.getText(dir + File.separator + f)
|
||||
}
|
||||
def readResource(resource: String): String = {
|
||||
val stream = getClass.getResourceAsStream(resource)
|
||||
scala.io.Source.fromInputStream(stream).mkString
|
||||
}
|
||||
|
||||
def checkOutputs(dir: String) = {
|
||||
def checkOutputs(dir: String): Unit = {
|
||||
// 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
|
||||
|// 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)
|
||||
readOutputFile(dir, "pad_digital_from_tristate_foundry_horizontal_input_array.v") should include(padBBEx)
|
||||
|
||||
val verilog = readOutputFile(dir, "ExampleTopModuleWithBB.v")
|
||||
val verilog = readOutputFile(dir, "ExampleTopModuleWithBB.v")
|
||||
// Pad frame + top should be exact
|
||||
verilog should include (readResource("/PadAnnotationVerilogPart.v"))
|
||||
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"
|
||||
behavior.of("Pad Annotations")
|
||||
|
||||
import barstools.tapeout.transforms._
|
||||
it should "serialize pad annotations" in {
|
||||
val noIOPadAnnotation = NoIOPadAnnotation("dog")
|
||||
noIOPadAnnotation.serialize should include("noPad: dog")
|
||||
|
||||
val ioPadAnnotation = IOPadAnnotation("left", "oliver")
|
||||
ioPadAnnotation.serialize should include(
|
||||
"""padSide: left
|
||||
|padName: oliver
|
||||
|""".stripMargin)
|
||||
|
||||
val modulePadAnnotation = ModulePadAnnotation(
|
||||
"top",
|
||||
11,
|
||||
42,
|
||||
Seq(
|
||||
SupplyAnnotation("mypad, 1, 2 ,3 , 4"),
|
||||
SupplyAnnotation("yourpad, 9, 8, 7, 6")
|
||||
)
|
||||
)
|
||||
|
||||
modulePadAnnotation.serialize should be(
|
||||
"""defaultPadSide: top
|
||||
|coreWidth: 11
|
||||
|coreHeight: 42
|
||||
|supplyAnnos:
|
||||
|- rightSide: 0
|
||||
| padName: mypad, 1, 2 ,3 , 4
|
||||
| leftSide: 0
|
||||
| bottomSide: 0
|
||||
| topSide: 0
|
||||
|- rightSide: 0
|
||||
| padName: yourpad, 9, 8, 7, 6
|
||||
| leftSide: 0
|
||||
| bottomSide: 0
|
||||
| topSide: 0
|
||||
|""".stripMargin
|
||||
)
|
||||
}
|
||||
|
||||
behavior.of("top module with blackbox")
|
||||
|
||||
it should "pass simple testbench" in {
|
||||
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)
|
||||
checkOutputs(dir)
|
||||
new SimpleTopModuleTester(c)
|
||||
} should be (true)
|
||||
} 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")
|
||||
@@ -196,15 +245,15 @@ class IOPadSpec extends FlatSpec with Matchers {
|
||||
}
|
||||
val success = chisel3.Driver.execute(optionsManager, () => new ExampleTopModuleWithBB) match {
|
||||
case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, firrtl))) =>
|
||||
firrtl should include ("ExampleTopModuleWithBB_PadFrame")
|
||||
firrtl should include ("ExampleTopModuleWithBB_PadFrame")
|
||||
firrtl should include ("ExampleTopModuleWithBB_Internal")
|
||||
firrtl should not include ("FakeBBPlaceholder")
|
||||
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(
|
||||
@@ -214,13 +263,13 @@ class IOPadSpec extends FlatSpec with Matchers {
|
||||
//commonOptions = commonOptions.copy(globalLogLevel = logger.LogLevel.Info)
|
||||
}
|
||||
val success = chisel3.Driver.execute(optionsManager, () => new ExampleTopModuleWithBB) match {
|
||||
case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, verilog))) =>
|
||||
case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, verilog))) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
success should be (true)
|
||||
}
|
||||
success should be(true)
|
||||
val dir = optionsManager.commonOptions.targetDirName
|
||||
checkOutputs(dir)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import chisel3._
|
||||
import firrtl._
|
||||
import chisel3.stage.ChiselStage
|
||||
import org.scalatest.{FreeSpec, Matchers}
|
||||
|
||||
class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
|
||||
@@ -19,22 +19,15 @@ class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
|
||||
}
|
||||
|
||||
class ResetNSpec extends FreeSpec with Matchers {
|
||||
|
||||
"Inverting reset needs to be done throughout module" in {
|
||||
val optionsManager = new ExecutionOptionsManager("dsptools") with HasChiselExecutionOptions with HasFirrtlOptions {
|
||||
firrtlOptions = firrtlOptions.copy(compilerName = "low", customTransforms = List(new ResetInverterTransform)),
|
||||
}
|
||||
chisel3.Driver.execute(optionsManager, () => new ExampleModuleNeedsResetInverted) match {
|
||||
case ChiselExecutionSuccess(_, chirrtl, Some(FirrtlExecutionSuccess(_, firrtl))) =>
|
||||
chirrtl should include ("input reset :")
|
||||
chirrtl should not include "input reset_n :"
|
||||
chirrtl should not include "node reset = not(reset_n)"
|
||||
val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted, Array())
|
||||
chirrtl should include("input reset :")
|
||||
(chirrtl should not).include("input reset_n :")
|
||||
(chirrtl should not).include("node reset = not(reset_n)")
|
||||
|
||||
firrtl should include ("input reset_n :")
|
||||
firrtl should include ("node reset = not(reset_n)")
|
||||
firrtl should not include "input reset :"
|
||||
case _ =>
|
||||
// bad
|
||||
}
|
||||
val firrtl = (new ChiselStage).emitFirrtl(new ExampleModuleNeedsResetInverted, Array("-X", "low"))
|
||||
firrtl should include("input reset_n :")
|
||||
firrtl should include("node reset = not(reset_n)")
|
||||
(firrtl should not).include("input reset :")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
package barstools.tapeout.transforms.retime.test
|
||||
|
||||
import chisel3._
|
||||
import firrtl._
|
||||
import org.scalatest.{FlatSpec, Matchers}
|
||||
import chisel3.experimental._
|
||||
import chisel3.util.HasBlackBoxInline
|
||||
import chisel3.iotesters._
|
||||
import barstools.tapeout.transforms.retime._
|
||||
import chisel3._
|
||||
import chisel3.stage.ChiselStage
|
||||
import firrtl._
|
||||
import logger.Logger
|
||||
import org.scalatest.{FlatSpec, Matchers}
|
||||
|
||||
class RetimeSpec extends FlatSpec with Matchers {
|
||||
def normalized(s: String): String = {
|
||||
@@ -25,20 +24,47 @@ class RetimeSpec extends FlatSpec with Matchers {
|
||||
it should "pass simple retime module annotation" in {
|
||||
val gen = () => new RetimeModule()
|
||||
val dir = uniqueDirName(gen, "RetimeModule")
|
||||
chisel3.Driver.execute(Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final"), gen) shouldBe a [ChiselExecutionSuccess]
|
||||
|
||||
val lines = io.Source.fromFile(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json").getLines().map(normalized).mkString("\n")
|
||||
lines should include("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
Logger.makeScope(Seq.empty) {
|
||||
val captor = new Logger.OutputCaptor
|
||||
Logger.setOutput(captor.printStream)
|
||||
val firrtl = (new ChiselStage).emitFirrtl(
|
||||
new RetimeModule(),
|
||||
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")
|
||||
)
|
||||
firrtl.nonEmpty should be(true)
|
||||
//Make sure we got the RetimeTransform scheduled
|
||||
captor.getOutputAsString should include ("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
}
|
||||
|
||||
val lines = FileUtils.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json")
|
||||
.map(normalized)
|
||||
.mkString("\n")
|
||||
lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation")
|
||||
lines should include(""""target":"RetimeModule.RetimeModule"""")
|
||||
}
|
||||
|
||||
// TODO(azidar): need to fix/add instance annotations
|
||||
ignore should "pass simple retime instance annotation" in {
|
||||
it should "pass simple retime instance annotation" in {
|
||||
val gen = () => new RetimeInstance()
|
||||
val dir = uniqueDirName(gen, "RetimeInstance")
|
||||
chisel3.Driver.execute(Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final.anno"), gen) shouldBe a [ChiselExecutionSuccess]
|
||||
|
||||
val lines = io.Source.fromFile(s"test_run_dir/$dir/final.anno").getLines().map(normalized).toSeq
|
||||
lines should contain ("Annotation(ComponentName(instance, ModuleName(RetimeInstance,CircuitName(RetimeInstance))),class barstools.tapeout.transforms.retime.RetimeTransform,retime)")
|
||||
Logger.makeScope(Seq.empty) {
|
||||
val captor = new Logger.OutputCaptor
|
||||
Logger.setOutput(captor.printStream)
|
||||
val firrtl = (new ChiselStage).emitFirrtl(
|
||||
new RetimeInstance(),
|
||||
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")
|
||||
)
|
||||
firrtl.nonEmpty should be(true)
|
||||
//Make sure we got the RetimeTransform scheduled
|
||||
captor.getOutputAsString should include ("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
}
|
||||
|
||||
val lines = FileUtils.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json")
|
||||
.map(normalized)
|
||||
.mkString("\n")
|
||||
lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation")
|
||||
lines should include(""""target":"RetimeInstance.MyModule"""")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user