diff --git a/.github/workflows/run-ci.yml b/.github/workflows/run-ci.yml new file mode 100644 index 00000000..bee78381 --- /dev/null +++ b/.github/workflows/run-ci.yml @@ -0,0 +1,44 @@ +name: Test + +on: + pull_request: + push: + branches: + - master + +jobs: + test: + name: Unit Tests + runs-on: ubuntu-latest + strategy: + matrix: + scala: [ 2.12.14 ] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Scala + uses: olafurpg/setup-scala@v10 + - name: Cache + uses: coursier/cache-action@v5 + - name: Get submodules + run: git submodule update --init + - name: Test + run: sbt test + + doc: + name: Documentation and formatting + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Scala + uses: olafurpg/setup-scala@v10 + - name: Check Formatting + run: sbt scalafmtCheckAll + + all_test_passed: + name: "all tests passed" + runs-on: ubuntu-latest + needs: [test, doc] + steps: + - run: echo Success diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 00000000..5be685f3 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,27 @@ +version = 2.7.5 + +maxColumn = 120 +align = most +continuationIndent.defnSite = 2 +assumeStandardLibraryStripMargin = true +docstrings = ScalaDoc +lineEndings = preserve +includeCurlyBraceInSelectChains = false +danglingParentheses.defnSite = true +danglingParentheses.callSite = true + +align.tokens.add = [ + { + code = ":" + } +] + +newlines.alwaysBeforeCurlyBraceLambdaParams = false +newlines.alwaysBeforeMultilineDef = false +newlines.implicitParamListModifierForce = [before] + +verticalMultiline.atDefnSite = true + +optIn.annotationNewlines = true + +rewrite.rules = [SortImports, PreferCurlyFors, AvoidInfix] \ No newline at end of file diff --git a/iocell/src/main/resources/barstools/iocell/vsrc/Analog.v b/iocell/src/main/resources/barstools/iocell/vsrc/Analog.v new file mode 100644 index 00000000..0a9abf03 --- /dev/null +++ b/iocell/src/main/resources/barstools/iocell/vsrc/Analog.v @@ -0,0 +1,11 @@ +// See LICENSE for license details + +`timescale 1ns/1ps + +module AnalogConst #(CONST, WIDTH) ( + output [WIDTH-1:0] io +); + + assign io = CONST; + +endmodule diff --git a/iocell/src/main/scala/barstools/iocell/chisel/Analog.scala b/iocell/src/main/scala/barstools/iocell/chisel/Analog.scala new file mode 100644 index 00000000..0cdfc493 --- /dev/null +++ b/iocell/src/main/scala/barstools/iocell/chisel/Analog.scala @@ -0,0 +1,18 @@ +// See LICENSE for license details + +package barstools.iocell.chisel + +import chisel3._ +import chisel3.util.{HasBlackBoxResource} +import chisel3.experimental.{Analog, IntParam} + +class AnalogConst(value: Int, width: Int = 1) + extends BlackBox(Map("CONST" -> IntParam(value), "WIDTH" -> IntParam(width))) + with HasBlackBoxResource { + val io = IO(new Bundle { val io = Analog(width.W) }) + addResource("/barstools/iocell/vsrc/Analog.v") +} + +object AnalogConst { + def apply(value: Int, width: Int = 1) = Module(new AnalogConst(value, width)).io.io +} diff --git a/iocell/src/main/scala/barstools/iocell/chisel/IOCell.scala b/iocell/src/main/scala/barstools/iocell/chisel/IOCell.scala new file mode 100644 index 00000000..d38f8406 --- /dev/null +++ b/iocell/src/main/scala/barstools/iocell/chisel/IOCell.scala @@ -0,0 +1,338 @@ +// See LICENSE for license details + +package barstools.iocell.chisel + +import chisel3._ +import chisel3.util.{Cat, HasBlackBoxInline} +import chisel3.reflect.DataMirror +import chisel3.experimental.{Analog, BaseModule} + +// The following four IO cell bundle types are bare-minimum functional connections +// for modeling 4 different IO cell scenarios. The intention is that the user +// would create wrapper modules that extend these interfaces with additional +// control signals. These are loosely similar to the sifive-blocks PinCtrl bundles +// (https://github.com/sifive/sifive-blocks/blob/master/src/main/scala/devices/pinctrl/PinCtrl.scala), +// but we want to avoid a dependency on an external libraries. + +/** The base IO bundle for an analog signal (typically something with no digital buffers inside) + * pad: off-chip (external) connection + * core: internal connection + */ +class AnalogIOCellBundle extends Bundle { + val pad = Analog(1.W) // Pad/bump signal (off-chip) + val core = Analog(1.W) // core signal (on-chip) +} + +/** The base IO bundle for a signal with runtime-controllable direction + * pad: off-chip (external) connection + * i: input to chip logic (output from IO cell) + * ie: enable signal for i + * o: output from chip logic (input to IO cell) + * oe: enable signal for o + */ +class DigitalGPIOCellBundle extends Bundle { + val pad = Analog(1.W) + val i = Output(Bool()) + val ie = Input(Bool()) + val o = Input(Bool()) + val oe = Input(Bool()) +} + +/** The base IO bundle for a digital output signal + * pad: off-chip (external) connection + * o: output from chip logic (input to IO cell) + * oe: enable signal for o + */ +class DigitalOutIOCellBundle extends Bundle { + val pad = Output(Bool()) + val o = Input(Bool()) + val oe = Input(Bool()) +} + +/** The base IO bundle for a digital input signal + * pad: off-chip (external) connection + * i: input to chip logic (output from IO cell) + * ie: enable signal for i + */ +class DigitalInIOCellBundle extends Bundle { + val pad = Input(Bool()) + val i = Output(Bool()) + val ie = Input(Bool()) +} + +trait IOCell extends BaseModule { + var iocell_name: Option[String] = None + + /** Set IOCell name + * @param s Proposed name for the IOCell + * + * @return An inherited IOCell with given the proposed name + */ + def suggestName(s: String): this.type = { + iocell_name = Some(s) + super.suggestName(s) + } +} + +trait AnalogIOCell extends IOCell { + val io: AnalogIOCellBundle +} + +trait DigitalGPIOCell extends IOCell { + val io: DigitalGPIOCellBundle +} + +trait DigitalInIOCell extends IOCell { + val io: DigitalInIOCellBundle +} + +trait DigitalOutIOCell extends IOCell { + val io: DigitalOutIOCellBundle +} + +// The following Generic IO cell black boxes have verilog models that mimic a very simple +// implementation of an IO cell. For building a real chip, it is important to implement +// and use similar classes which wrap the foundry-specific IO cells. + +abstract class GenericIOCell extends BlackBox with HasBlackBoxInline { + val impl: String + val moduleName = this.getClass.getSimpleName + setInline(s"$moduleName.v", impl); +} + +class GenericAnalogIOCell extends GenericIOCell with AnalogIOCell { + val io = IO(new AnalogIOCellBundle) + lazy val impl = s""" +`timescale 1ns/1ps +module GenericAnalogIOCell( + inout pad, + inout core +); + + assign core = 1'bz; + assign pad = core; + +endmodule""" +} + +class GenericDigitalGPIOCell extends GenericIOCell with DigitalGPIOCell { + val io = IO(new DigitalGPIOCellBundle) + lazy val impl = s""" +`timescale 1ns/1ps +module GenericDigitalGPIOCell( + inout pad, + output i, + input ie, + input o, + input oe +); + + assign pad = oe ? o : 1'bz; + assign i = ie ? pad : 1'b0; + +endmodule""" +} + +class GenericDigitalInIOCell extends GenericIOCell with DigitalInIOCell { + val io = IO(new DigitalInIOCellBundle) + lazy val impl = s""" +`timescale 1ns/1ps +module GenericDigitalInIOCell( + input pad, + output i, + input ie +); + + assign i = ie ? pad : 1'b0; + +endmodule""" +} + +class GenericDigitalOutIOCell extends GenericIOCell with DigitalOutIOCell { + val io = IO(new DigitalOutIOCellBundle) + lazy val impl = s""" +`timescale 1ns/1ps +module GenericDigitalOutIOCell( + output pad, + input o, + input oe +); + + assign pad = oe ? o : 1'bz; + +endmodule""" +} + +trait IOCellTypeParams { + def analog(): AnalogIOCell + def gpio(): DigitalGPIOCell + def input(): DigitalInIOCell + def output(): DigitalOutIOCell +} + +case class GenericIOCellParams() extends IOCellTypeParams { + def analog() = Module(new GenericAnalogIOCell) + def gpio() = Module(new GenericDigitalGPIOCell) + def input() = Module(new GenericDigitalInIOCell) + def output() = Module(new GenericDigitalOutIOCell) +} + +object IOCell { + + /** From within a RawModule or MultiIOModule context, generate new module IOs from a given + * signal and return the new IO and a Seq containing all generated IO cells. + * @param coreSignal The signal onto which to add IO cells + * @param name An optional name or name prefix to use for naming IO cells + * @param abstractResetAsAsync When set, will coerce abstract resets to + * AsyncReset, and otherwise to Bool (sync reset) + * @return A tuple of (the generated IO data node, a Seq of all generated IO cell instances) + */ + def generateIOFromSignal[T <: Data]( + coreSignal: T, + name: String, + typeParams: IOCellTypeParams = GenericIOCellParams(), + abstractResetAsAsync: Boolean = false + ): (T, Seq[IOCell]) = { + val padSignal = IO(DataMirror.internal.chiselTypeClone[T](coreSignal)).suggestName(name) + val resetFn = if (abstractResetAsAsync) toAsyncReset else toSyncReset + val iocells = IOCell.generateFromSignal(coreSignal, padSignal, Some(s"iocell_$name"), typeParams, resetFn) + (padSignal, iocells) + } + + /** Connect two identical signals together by adding IO cells between them and return a Seq + * containing all generated IO cells. + * @param coreSignal The core-side (internal) signal onto which to connect/add IO cells + * @param padSignal The pad-side (external) signal onto which to connect IO cells + * @param name An optional name or name prefix to use for naming IO cells + * @return A Seq of all generated IO cell instances + */ + val toSyncReset: (Reset) => Bool = _.asBool + val toAsyncReset: (Reset) => AsyncReset = _.asAsyncReset + def generateFromSignal[T <: Data, R <: Reset]( + coreSignal: T, + padSignal: T, + name: Option[String] = None, + typeParams: IOCellTypeParams = GenericIOCellParams(), + concretizeResetFn: (Reset) => R = toSyncReset + ): Seq[IOCell] = { + def genCell[T <: Data]( + castToBool: (T) => Bool, + castFromBool: (Bool) => T + )(coreSignal: T, + padSignal: T + ): Seq[IOCell] = { + DataMirror.directionOf(coreSignal) match { + case ActualDirection.Input => { + val iocell = typeParams.input() + name.foreach(n => { + iocell.suggestName(n) + }) + coreSignal := castFromBool(iocell.io.i) + iocell.io.ie := true.B + iocell.io.pad := castToBool(padSignal) + Seq(iocell) + } + case ActualDirection.Output => { + val iocell = typeParams.output() + name.foreach(n => { + iocell.suggestName(n) + }) + iocell.io.o := castToBool(coreSignal) + iocell.io.oe := true.B + padSignal := castFromBool(iocell.io.pad) + Seq(iocell) + } + case _ => throw new Exception(s"Signal does not have a direction and cannot be matched to an IOCell") + } + } + def genCellForClock = genCell[Clock](_.asUInt.asBool, _.asClock) _ + def genCellForAsyncReset = genCell[AsyncReset](_.asBool, _.asAsyncReset) _ + def genCellForAbstractReset = genCell[Reset](_.asBool, concretizeResetFn) _ + + (coreSignal, padSignal) match { + case (coreSignal: Analog, padSignal: Analog) => { + if (coreSignal.getWidth == 0) { + Seq() + } else { + require( + coreSignal.getWidth == 1, + "Analogs wider than 1 bit are not supported because we can't bit-select Analogs (https://github.com/freechipsproject/chisel3/issues/536)" + ) + val iocell = typeParams.analog() + name.foreach(n => iocell.suggestName(n)) + iocell.io.core <> coreSignal + padSignal <> iocell.io.pad + Seq(iocell) + } + } + case (coreSignal: Clock, padSignal: Clock) => genCellForClock(coreSignal, padSignal) + case (coreSignal: AsyncReset, padSignal: AsyncReset) => genCellForAsyncReset(coreSignal, padSignal) + case (coreSignal: Bits, padSignal: Bits) => { + require(padSignal.getWidth == coreSignal.getWidth, "padSignal and coreSignal must be the same width") + if (padSignal.getWidth == 0) { + // This dummy assignment will prevent invalid firrtl from being emitted + DataMirror.directionOf(coreSignal) match { + case ActualDirection.Input => coreSignal := 0.U + case _ => {} + } + Seq() + } else { + DataMirror.directionOf(coreSignal) match { + case ActualDirection.Input => { + val iocells = padSignal.asBools.zipWithIndex.map { case (sig, i) => + val iocell = typeParams.input() + // Note that we are relying on chisel deterministically naming this in the index order (which it does) + // This has the side-effect of naming index 0 with no _0 suffix, which is how chisel names other signals + // An alternative solution would be to suggestName(n + "_" + i) + name.foreach(n => { + iocell.suggestName(n) + }) + iocell.io.pad := sig + iocell.io.ie := true.B + iocell + } + // Note that the reverse here is because Cat(Seq(a,b,c,d)) yields abcd, but a is index 0 of the Seq + coreSignal := Cat(iocells.map(_.io.i).reverse) + iocells + } + case ActualDirection.Output => { + val iocells = coreSignal.asBools.zipWithIndex.map { case (sig, i) => + val iocell = typeParams.output() + // Note that we are relying on chisel deterministically naming this in the index order (which it does) + // This has the side-effect of naming index 0 with no _0 suffix, which is how chisel names other signals + // An alternative solution would be to suggestName(n + "_" + i) + name.foreach(n => { + iocell.suggestName(n) + }) + iocell.io.o := sig + iocell.io.oe := true.B + iocell + } + // Note that the reverse here is because Cat(Seq(a,b,c,d)) yields abcd, but a is index 0 of the Seq + padSignal := Cat(iocells.map(_.io.pad).reverse) + iocells + } + case _ => throw new Exception("Bits signal does not have a direction and cannot be matched to IOCell(s)") + } + } + } + case (coreSignal: Reset, padSignal: Reset) => genCellForAbstractReset(coreSignal, padSignal) + case (coreSignal: Vec[_], padSignal: Vec[_]) => { + require(padSignal.size == coreSignal.size, "size of Vec for padSignal and coreSignal must be the same") + coreSignal.zip(padSignal).zipWithIndex.foldLeft(Seq.empty[IOCell]) { case (total, ((core, pad), i)) => + val ios = IOCell.generateFromSignal(core, pad, name.map(_ + "_" + i), typeParams) + total ++ ios + } + } + case (coreSignal: Record, padSignal: Record) => { + coreSignal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (eltName, core)) => + val pad = padSignal.elements(eltName) + val ios = IOCell.generateFromSignal(core, pad, name.map(_ + "_" + eltName), typeParams) + total ++ ios + } + } + case _ => { throw new Exception("Oops, I don't know how to handle this signal.") } + } + } + +} diff --git a/src/main/scala/barstools/macros/CostMetric.scala b/src/main/scala/barstools/macros/CostMetric.scala new file mode 100644 index 00000000..647889d3 --- /dev/null +++ b/src/main/scala/barstools/macros/CostMetric.scala @@ -0,0 +1,205 @@ +// See LICENSE for license details. + +package barstools.macros + +/** Trait which can calculate the cost of compiling a memory against a certain + * library memory macro using a cost function. + */ +// TODO: eventually explore compiling a single target memory using multiple +// different kinds of target memory. +trait CostMetric extends Serializable { + + /** Cost function that returns the cost of compiling a memory using a certain + * macro. + * + * @param mem Memory macro to compile (target memory) + * @param lib Library memory macro to use (library memory) + * @return The cost of this compile, defined by this cost metric, or None if + * it cannot be compiled. + */ + def cost(mem: Macro, lib: Macro): Option[Double] + + /** Helper function to return the map of arguments (or an empty map if there are none). + */ + def commandLineParams(): Map[String, String] + + // We also want this to show up for the class itself. + def name(): String +} + +// Is there a better way to do this? (static method associated to CostMetric) +trait CostMetricCompanion { + def name(): String + + /** Construct this cost metric from a command line mapping. */ + def construct(m: Map[String, String]): CostMetric +} + +// Some default cost functions. + +/** Palmer's old metric. + * TODO: figure out what is the difference between this metric and the current + * default metric and either revive or delete this metric. + */ +object OldMetric extends CostMetric with CostMetricCompanion { + override def cost(mem: Macro, lib: Macro): Option[Double] = { + /* Palmer: A quick cost function (that must be kept in sync with + * memory_cost()) that attempts to avoid compiling unnecessary + * memories. This is a lower bound on the cost of compiling a + * memory: it assumes 100% bit-cell utilization when mapping. */ + // val cost = 100 * (mem.depth * mem.width) / (lib.depth * lib.width) + + // (mem.depth * mem.width) + ??? + } + + override def commandLineParams() = Map.empty[String, String] + override def name() = "OldMetric" + override def construct(m: Map[String, String]): CostMetric = OldMetric +} + +/** An external cost function. + * Calls the specified path with paths to the JSON MDF representation of the mem + * and lib macros. The external executable should print a Double. + * None will be returned if the external executable does not print a valid + * Double. + */ +class ExternalMetric(path: String) extends CostMetric { + import mdf.macrolib.Utils.writeMacroToPath + + import java.io._ + import scala.language.postfixOps + import sys.process._ + + override def cost(mem: Macro, lib: Macro): Option[Double] = { + // Create temporary files. + val memFile = File.createTempFile("_macrocompiler_mem_", ".json") + val libFile = File.createTempFile("_macrocompiler_lib_", ".json") + + writeMacroToPath(Some(memFile.getAbsolutePath), mem.src) + writeMacroToPath(Some(libFile.getAbsolutePath), lib.src) + + // !! executes the given command + val result: String = (s"$path ${memFile.getAbsolutePath} ${libFile.getAbsolutePath}" !!).trim + + // Remove temporary files. + memFile.delete() + libFile.delete() + + try { + Some(result.toDouble) + } catch { + case _: NumberFormatException => None + } + } + + override def commandLineParams() = Map("path" -> path) + override def name(): String = ExternalMetric.name() +} + +object ExternalMetric extends CostMetricCompanion { + override def name() = "ExternalMetric" + + /** Construct this cost metric from a command line mapping. */ + override def construct(m: Map[String, String]): ExternalMetric = { + val pathOption = m.get("path") + pathOption match { + case Some(path: String) => new ExternalMetric(path) + case _ => throw new IllegalArgumentException("ExternalMetric missing option 'path'") + } + } +} + +/** The current default metric in barstools, re-defined by Donggyu. */ +// TODO: write tests for this function to make sure it selects the right things +object DefaultMetric extends CostMetric with CostMetricCompanion { + override def cost(mem: Macro, lib: Macro): Option[Double] = { + val memMask = mem.src.ports.map(_.maskGran).find(_.isDefined).flatten + val libMask = lib.src.ports.map(_.maskGran).find(_.isDefined).flatten + val memWidth = (memMask, libMask) match { + case (None, _) => mem.src.width + case (Some(p), None) => + (mem.src.width / p) * math.ceil( + p.toDouble / lib.src.width + ) * lib.src.width //We map the mask to distinct memories + case (Some(p), Some(m)) => + if (m <= p) (mem.src.width / p) * math.ceil(p.toDouble / m) * m //Using multiple m's to create a p (integrally) + else (mem.src.width / p) * m //Waste the extra maskbits + } + val maskPenalty = (memMask, libMask) match { + case (None, Some(_)) => 0.001 + case (_, _) => 0 + } + val depthCost = math.ceil(mem.src.depth.toDouble / lib.src.depth.toDouble) + val widthCost = math.ceil(memWidth / lib.src.width.toDouble) + val bitsCost = (lib.src.depth * lib.src.width).toDouble + // Fraction of wasted bits plus const per mem + val requestedBits = (mem.src.depth * mem.src.width).toDouble + val bitsWasted = depthCost * widthCost * bitsCost - requestedBits + val wastedConst = 0.05 // 0 means waste as few bits with no regard for instance count + val costPerInst = wastedConst * depthCost * widthCost + Some(1.0 * bitsWasted / requestedBits + costPerInst + maskPenalty) + } + + override def commandLineParams() = Map.empty[String, String] + override def name() = "DefaultMetric" + override def construct(m: Map[String, String]): CostMetric = DefaultMetric +} + +object MacroCompilerUtil { + import java.io._ + import java.util.Base64 + + // Adapted from https://stackoverflow.com/a/134918 + + /** Serialize an arbitrary object to String. + * Used to pass structured values through as an annotation. + */ + def objToString(o: Serializable): String = { + val byteOutput: ByteArrayOutputStream = new ByteArrayOutputStream + val objectOutput: ObjectOutputStream = new ObjectOutputStream(byteOutput) + objectOutput.writeObject(o) + objectOutput.close() + Base64.getEncoder.encodeToString(byteOutput.toByteArray) + } + + /** Deserialize an arbitrary object from String. */ + def objFromString(s: String): AnyRef = { + val data = Base64.getDecoder.decode(s) + val ois: ObjectInputStream = new ObjectInputStream(new ByteArrayInputStream(data)) + val o = ois.readObject + ois.close() + o + } +} + +object CostMetric { + + /** Define some default metric. */ + val default: CostMetric = DefaultMetric + + val costMetricCreators: scala.collection.mutable.Map[String, CostMetricCompanion] = scala.collection.mutable.Map() + + // Register some default metrics + registerCostMetric(OldMetric) + registerCostMetric(ExternalMetric) + registerCostMetric(DefaultMetric) + + /** Register a cost metric. + * @param createFuncHelper Companion object to fetch the name and construct + * the metric. + */ + def registerCostMetric(createFuncHelper: CostMetricCompanion): Unit = { + costMetricCreators.update(createFuncHelper.name(), createFuncHelper) + } + + /** Select a cost metric from string. */ + def getCostMetric(m: String, params: Map[String, String]): CostMetric = { + if (m == "default") { + CostMetric.default + } else if (!costMetricCreators.contains(m)) { + throw new IllegalArgumentException("Invalid cost metric " + m) + } else { + costMetricCreators(m).construct(params) + } + } +} diff --git a/src/main/scala/barstools/macros/MacroCompiler.scala b/src/main/scala/barstools/macros/MacroCompiler.scala new file mode 100644 index 00000000..459992f2 --- /dev/null +++ b/src/main/scala/barstools/macros/MacroCompiler.scala @@ -0,0 +1,981 @@ +// See LICENSE for license details. + +/** Terminology note: + * mem - target memory to compile, in design (e.g. Mem() in rocket) + * lib - technology SRAM(s) to use to compile mem + */ + +package barstools.macros + +import barstools.macros.Utils._ +import firrtl.Utils.{one, zero, BoolType} +import firrtl.annotations._ +import firrtl.ir._ +import firrtl.options.Dependency +import firrtl.stage.TransformManager.TransformDependency +import firrtl.stage.{FirrtlSourceAnnotation, FirrtlStage, Forms, OutputFileAnnotation, RunFirrtlTransformAnnotation} +import firrtl.{PrimOps, _} +import mdf.macrolib.{PolarizedPort, PortPolarity, SRAMCompiler, SRAMGroup, SRAMMacro} + +import java.io.{File, FileWriter} +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + +case class MacroCompilerException(msg: String) extends Exception(msg) + +// TODO The parameters could be unpacked here instead of keeping it in a serialized form +case class MacroCompilerAnnotation(content: String) extends NoTargetAnnotation { + import MacroCompilerAnnotation.Params + + def params: Params = MacroCompilerUtil.objFromString(content).asInstanceOf[Params] +} + +/** The MacroCompilerAnnotation to trigger the macro compiler. + * Note that this annotation does NOT actually target any modules for + * compilation. It simply holds all the settings for the memory compiler. The + * actual selection of which memories to compile is set in the Params. + * + * To use, simply annotate the entire circuit itself with this annotation and + * include [[MacroCompilerTransform]]. + */ +object MacroCompilerAnnotation { + + /** Macro compiler mode. */ + sealed trait CompilerMode + + /** Strict mode - must compile all memories or error out. */ + case object Strict extends CompilerMode + + /** Synflops mode - compile all memories with synflops (do not map to lib at all). */ + case object Synflops extends CompilerMode + + /** CompileAndSynflops mode - compile all memories and create mock versions of the target libs with synflops. */ + case object CompileAndSynflops extends CompilerMode + + /** FallbackSynflops - compile all memories to SRAM when possible and fall back to synflops if a memory fails. * */ + case object FallbackSynflops extends CompilerMode + + /** CompileAvailable - compile what is possible and do nothing with uncompiled memories. * */ + case object CompileAvailable extends CompilerMode + + /** The default mode for the macro compiler. + * TODO: Maybe set the default to FallbackSynflops (typical for + * vlsi_mem_gen-like scripts) once it's implemented? + */ + val Default: CompilerMode = CompileAvailable + + // Options as list of (CompilerMode, command-line name, description) + val options: Seq[(CompilerMode, String, String)] = Seq( + (Default, "default", "Select the default option from below."), + (Strict, "strict", "Compile all memories to library or return an error."), + ( + Synflops, + "synflops", + "Produces synthesizable flop-based memories for all memories (do not map to lib at all); likely useful for simulation purposes." + ), + ( + CompileAndSynflops, + "compileandsynflops", + "Compile all memories and create mock versions of the target libs with synflops; likely also useful for simulation purposes." + ), + ( + FallbackSynflops, + "fallbacksynflops", + "Compile all memories to library when possible and fall back to synthesizable flop-based memories when library synth is not possible." + ), + ( + CompileAvailable, + "compileavailable", + "Compile all memories to library when possible and do nothing in case of errors. (default)" + ) + ) + + /** Helper function to select a compiler mode. */ + def stringToCompilerMode(str: String): CompilerMode = options.collectFirst { + case (mode, cmd, _) if cmd == str => mode + } match { + case Some(x) => x + case None => throw new IllegalArgumentException("No such compiler mode " + str) + } + + /** Parameters associated to this MacroCompilerAnnotation. + * + * @param mem Path to memory lib + * @param memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf)) + * @param lib Path to library lib or None if no libraries + * @param hammerIR Path to HammerIR output or None (not generated in this case) + * @param costMetric Cost metric to use + * @param mode Compiler mode (see CompilerMode) + * @param forceCompile Set of memories to force compiling to lib regardless of the mode + * @param forceSynflops Set of memories to force compiling as flops regardless of the mode + */ + case class Params( + mem: String, + memFormat: Option[String], + lib: Option[String], + hammerIR: Option[String], + costMetric: CostMetric, + mode: CompilerMode, + useCompiler: Boolean, + forceCompile: Set[String], + forceSynflops: Set[String]) + extends Serializable + + /** Create a MacroCompilerAnnotation. + * @param c Top-level circuit name (see class description) + * @param p Parameters (see above). + */ + def apply(c: String, p: Params): MacroCompilerAnnotation = + MacroCompilerAnnotation(MacroCompilerUtil.objToString(p)) + +} + +class MacroCompilerPass( + mems: Option[Seq[Macro]], + libs: Option[Seq[Macro]], + compilers: Option[SRAMCompiler], + hammerIR: Option[String], + costMetric: CostMetric = CostMetric.default, + mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) + extends firrtl.passes.Pass { + // Helper function to check the legality of bitPairs. + // e.g. ((0,21), (22,43)) is legal + // ((0,21), (22,21)) is illegal and will throw an assert + private def checkBitPairs(bitPairs: Seq[(BigInt, BigInt)]): Unit = { + bitPairs.foldLeft(BigInt(-1))((lastBit, nextPair) => { + assert(lastBit + 1 == nextPair._1, s"Pair's first bit ${nextPair._1} does not follow last bit $lastBit") + assert(nextPair._2 >= nextPair._1, s"Pair $nextPair in bitPairs $bitPairs is illegal") + nextPair._2 + }) + } + + /** Calculate bit pairs. + * This is a list of submemories by width. + * The tuples are (lsb, msb) inclusive. + * Example: (0, 7) and (8, 15) might be a split for a width=16 memory into two width=8 target memories. + * Another example: (0, 3), (4, 7), (8, 11) may be a split for a width-12 memory into 3 width-4 target memories. + * + * @param mem Memory to compile + * @param lib Lib to compile with + * @return Bit pairs or empty list if there was an error. + */ + private def calculateBitPairs(mem: Macro, lib: Macro): Seq[(BigInt, BigInt)] = { + val pairedPorts = mem.sortedPorts.zip(lib.sortedPorts) + + val bitPairs = ArrayBuffer[(BigInt, BigInt)]() + var currentLSB: BigInt = 0 + + // Process every bit in the mem width. + for (memBit <- 0 until mem.src.width) { + val bitsInCurrentMem = memBit - currentLSB + + // We'll need to find a bitPair that works for *all* the ports of the memory. + // e.g. unmasked read port and masked write port. + // For each port, store a tentative candidate for the split. + // Afterwards, figure out which one to use. + val bitPairCandidates = ArrayBuffer[(BigInt, BigInt)]() + for ((memPort, libPort) <- pairedPorts) { + + // Sanity check to make sure we only split once per bit, once per port. + var alreadySplit: Boolean = false + + // Helper function to check if it's time to split memories. + // @param effectiveLibWidth Split memory when we have this many bits. + def splitMemory(effectiveLibWidth: Int): Unit = { + assert(!alreadySplit) + + if (bitsInCurrentMem == effectiveLibWidth) { + bitPairCandidates += ((currentLSB, memBit - 1)) + alreadySplit = true + } + } + + // Make sure we don't have a maskGran larger than the width of the memory. + assert(memPort.src.effectiveMaskGran <= memPort.src.width.get) + assert(libPort.src.effectiveMaskGran <= libPort.src.width.get) + + val libWidth = libPort.src.width.get + + // Don't consider cases of maskGran == width as "masked" since those masks + // effectively function as write-enable bits. + val memMask = if (memPort.src.effectiveMaskGran == memPort.src.width.get) None else memPort.src.maskGran + val libMask = if (libPort.src.effectiveMaskGran == libPort.src.width.get) None else libPort.src.maskGran + + (memMask, libMask) match { + // Neither lib nor mem is masked. + // No problems here. + case (None, None) => splitMemory(libWidth) + + // Only the lib is masked. + // Not an issue; we can just make all the bits in the lib mask enabled. + case (None, Some(_)) => splitMemory(libWidth) + + // Only the mem is masked. + case (Some(p), None) => + if (p % libPort.src.width.get == 0) { + // If the mem mask is a multiple of the lib width, then we're good. + // Just roll over every lib width as usual. + // e.g. lib width=4, mem maskGran={4, 8, 12, 16, ...} + splitMemory(libWidth) + } else if (libPort.src.width.get % p == 0) { + // Lib width is a multiple of the mem mask. + // Consider the case where mem mask = 4 but lib width = 8, unmasked. + // We can still compile, but will need to waste the extra bits. + splitMemory(memMask.get) + } else { + // No neat multiples. + // We might still be able to compile extremely inefficiently. + if (p < libPort.src.width.get) { + // Compile using mem mask as the effective width. (note that lib is not masked) + // e.g. mem mask = 3, lib width = 8 + splitMemory(memMask.get) + } else { + // e.g. mem mask = 13, lib width = 8 + System.err.println( + s"Unmasked target memory: unaligned mem maskGran $p with lib (${lib.src.name}) width ${libPort.src.width.get} not supported" + ) + return Seq() + } + } + + // Both lib and mem are masked. + case (Some(m), Some(l)) => + if (m == l) { + // Lib maskGran == mem maskGran, no problems + splitMemory(libWidth) + } else if (m > l) { + // Mem maskGran > lib maskGran + if (m % l == 0) { + // Mem maskGran is a multiple of lib maskGran, carry on as normal. + splitMemory(libWidth) + } else { + System.err.println(s"Mem maskGran $m is not a multiple of lib maskGran $l: currently not supported") + return Seq() + } + } else { // m < l + // Lib maskGran > mem maskGran. + if (l % m == 0) { + // Lib maskGran is a multiple of mem maskGran. + // e.g. lib maskGran = 8, mem maskGran = 4. + // In this case we can only compile very wastefully (by treating + // lib as a mem maskGran width memory) :( + splitMemory(memMask.get) + + // TODO: there's an optimization that could allow us to pack more + // bits in and be more efficient. + // e.g. say if mem maskGran = 4, lib maskGran = 8, libWidth = 32 + // We could use 16 of bit (bits 0-3, 8-11, 16-19, 24-27) instead + // of treating it as simply a width 4 (!!!) memory. + // This would require a major refactor though. + } else { + System.err.println(s"Lib maskGran $m is not a multiple of mem maskGran $l: currently not supported") + return Seq() + } + } + } + } + + // Choose an actual bit pair to add. + // We'll have to choose the smallest one (e.g. unmasked read port might be more tolerant of a bigger split than the masked write port). + if (bitPairCandidates.isEmpty) { + // No pair needed to split, just continue + } else { + val bestPair = bitPairCandidates.reduceLeft((leftPair, rightPair) => { + if (leftPair._2 - leftPair._1 + 1 > rightPair._2 - rightPair._1 + 1) leftPair else rightPair + }) + bitPairs += bestPair + currentLSB = bestPair._2 + BigInt(1) // advance the LSB pointer + } + } + // Add in the last chunk if there are any leftovers + bitPairs += ((currentLSB, mem.src.width - 1)) + + bitPairs + }.toSeq + + def compile(mem: Macro, lib: Macro): Option[(Module, Macro)] = { + assert( + mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0, + "mem and lib should have an equal number of ports" + ) + val pairedPorts = mem.sortedPorts.zip(lib.sortedPorts) + + // Width mapping. See calculateBitPairs. + val bitPairs: Seq[(BigInt, BigInt)] = calculateBitPairs(mem, lib) + if (bitPairs.isEmpty) { + System.err.println("Error occurred during bitPairs calculations (bitPairs is empty).") + return None + } + // Check bit pairs. + checkBitPairs(bitPairs) + + // Depth mapping + val stmts = ArrayBuffer[Statement]() + val outputs = mutable.HashMap[String, ArrayBuffer[(Expression, Expression)]]() + val selects = mutable.HashMap[String, Expression]() + val selectRegs = mutable.HashMap[String, Expression]() + /* Palmer: If we've got a parallel memory then we've got to take the + * address bits into account. */ + if (mem.src.depth > lib.src.depth) { + mem.src.ports.foreach { port => + val high = MacroCompilerMath.ceilLog2(mem.src.depth) + val low = MacroCompilerMath.ceilLog2(lib.src.depth) + val ref = WRef(port.address.name) + val nodeName = s"${ref.name}_sel" + val tpe = UIntType(IntWidth(high - low)) + selects(ref.name) = WRef(nodeName, tpe) + stmts += DefNode(NoInfo, nodeName, bits(ref, high - 1, low)) + // Donggyu: output selection should be piped + if (port.output.isDefined) { + val regName = s"${ref.name}_sel_reg" + val enable = (port.chipEnable, port.readEnable) match { + case (Some(ce), Some(re)) => + and(WRef(ce.name, BoolType), WRef(re.name, BoolType)) + case (Some(ce), None) => WRef(ce.name, BoolType) + case (None, Some(re)) => WRef(re.name, BoolType) + case (None, None) => one + } + selectRegs(ref.name) = WRef(regName, tpe) + stmts += DefRegister(NoInfo, regName, tpe, WRef(port.clock.get.name), zero, WRef(regName)) + stmts += Connect(NoInfo, WRef(regName), Mux(enable, WRef(nodeName), WRef(regName), tpe)) + } + } + } + for ((_, i) <- BigInt(0).until(mem.src.depth, lib.src.depth).zipWithIndex) { + for (j <- bitPairs.indices) { + val name = s"mem_${i}_$j" + // Create the instance. + stmts += WDefInstance(NoInfo, name, lib.src.name, lib.tpe) + // Connect extra ports of the lib. + stmts ++= lib.extraPorts.map { case (portName, portValue) => + Connect(NoInfo, WSubField(WRef(name), portName), portValue) + } + } + for ((memPort, libPort) <- pairedPorts) { + val addrMatch = selects.get(memPort.src.address.name) match { + case None => one + case Some(addr) => + val index = UIntLiteral(i, IntWidth(bitWidth(addr.tpe))) + DoPrim(PrimOps.Eq, Seq(addr, index), Nil, index.tpe) + } + val addrMatchReg = selectRegs.get(memPort.src.address.name) match { + case None => one + case Some(reg) => + val index = UIntLiteral(i, IntWidth(bitWidth(reg.tpe))) + DoPrim(PrimOps.Eq, Seq(reg, index), Nil, index.tpe) + } + def andAddrMatch(e: Expression) = { + and(e, addrMatch) + } + val cats = ArrayBuffer[Expression]() + for (((low, high), j) <- bitPairs.zipWithIndex) { + val inst = WRef(s"mem_${i}_$j", lib.tpe) + + def connectPorts2(mem: Expression, lib: String, polarity: Option[PortPolarity]): Statement = + Connect(NoInfo, WSubField(inst, lib), portToExpression(mem, polarity)) + def connectPorts(mem: Expression, lib: String, polarity: PortPolarity): Statement = + connectPorts2(mem, lib, Some(polarity)) + + // Clock port mapping + /* Palmer: FIXME: I don't handle memories with read/write clocks yet. */ + /* Colin not all libPorts have clocks but all memPorts do*/ + libPort.src.clock.foreach { cPort => + stmts += connectPorts(WRef(memPort.src.clock.get.name), cPort.name, cPort.polarity) + } + + // Adress port mapping + /* Palmer: The address port to a memory is just the low-order bits of + * the top address. */ + stmts += connectPorts(WRef(memPort.src.address.name), libPort.src.address.name, libPort.src.address.polarity) + + // Output port mapping + (memPort.src.output, libPort.src.output) match { + case (Some(PolarizedPort(mem, _)), Some(PolarizedPort(lib, lib_polarity))) => + /* Palmer: In order to produce the output of a memory we need to cat + * together a bunch of narrower memories, which can only be + * done after generating all the memories. This saves up the + * output statements for later. */ + val name = s"${mem}_${i}_$j" // This name is the output from the instance (mem vs ${mem}). + val exp = portToExpression(bits(WSubField(inst, lib), high - low, 0), Some(lib_polarity)) + stmts += DefNode(NoInfo, name, exp) + cats += WRef(name) + case (None, Some(_)) => + /* Palmer: If the inner memory has an output port but the outer + * one doesn't then it's safe to just leave the outer + * port floating. */ + case (None, None) => + /* Palmer: If there's no output ports at all (ie, read-only + * port on the memory) then just don't worry about it, + * there's nothing to do. */ + case (Some(PolarizedPort(mem, _)), None) => + System.err.println("WARNING: Unable to match output ports on memory") + System.err.println(s" outer output port: $mem") + return None + } + + // Input port mapping + (memPort.src.input, libPort.src.input) match { + case (Some(PolarizedPort(mem, _)), Some(PolarizedPort(lib, lib_polarity))) => + /* Palmer: The input port to a memory just needs to happen in parallel, + * this does a part select to narrow the memory down. */ + stmts += connectPorts(bits(WRef(mem), high, low), lib, lib_polarity) + case (None, Some(lib)) => + /* Palmer: If the inner memory has an input port but the other + * one doesn't then it's safe to just leave the inner + * port floating. This should be handled by the + * default value of the write enable, so nothing should + * every make it into the memory. */ + //Firrtl cares about dangling inputs now tie it off + stmts += IsInvalid(NoInfo, WSubField(inst, lib.name)) + case (None, None) => + /* Palmer: If there's no input ports at all (ie, read-only + * port on the memory) then just don't worry about it, + * there's nothing to do. */ + case (Some(PolarizedPort(mem, _)), None) => + System.err.println("WARNING: Unable to match input ports on memory") + System.err.println(s" outer input port: $mem") + return None + } + + // Mask port mapping + val memMask = memPort.src.maskPort match { + case Some(PolarizedPort(mem, _)) => + /* Palmer: The bits from the outer memory's write mask that will be + * used as the write mask for this inner memory. */ + if (libPort.src.effectiveMaskGran == libPort.src.width.get) { + bits(WRef(mem), low / memPort.src.effectiveMaskGran) + } else { + require(isPowerOfTwo(libPort.src.effectiveMaskGran), "only powers of two masks supported for now") + + // How much of this lib's width we are effectively using. + // If we have a mem maskGran less than the lib's maskGran, we'll have to take the smaller maskGran. + // Example: if we have a lib whose maskGran is 8 but our mem's maskGran is 4. + // The other case is if we're using a larger lib than mem. + val usingLessThanLibMaskGran = memPort.src.maskGran.get < libPort.src.effectiveMaskGran + val effectiveLibWidth = + if (usingLessThanLibMaskGran) + memPort.src.maskGran.get + else + libPort.src.width.get + + cat( + (0 until libPort.src.width.get by libPort.src.effectiveMaskGran) + .map(i => { + if (usingLessThanLibMaskGran && i >= effectiveLibWidth) { + // If the memMaskGran is smaller than the lib's gran, then + // zero out the upper bits. + zero + } else { + if ((low + i) >= memPort.src.width.get) { + // If our bit is larger than the whole width of the mem, just zero out the upper bits. + zero + } else { + // Pick the appropriate bit from the mem mask. + bits(WRef(mem), (low + i) / memPort.src.effectiveMaskGran) + } + } + }) + .reverse + ) + } + case None => + /* If there is a lib mask port but no mem mask port, just turn on + * all bits of the lib mask port. */ + if (libPort.src.maskPort.isDefined) { + val width = libPort.src.width.get / libPort.src.effectiveMaskGran + val value = (BigInt(1) << width) - 1 + UIntLiteral(value, IntWidth(width)) + } else { + // No mask ports on either side. + // We treat a "mask" of a single bit to be equivalent to a write + // enable (as used below). + one + } + } + + // Write enable port mapping + val memWriteEnable = memPort.src.writeEnable match { + case Some(PolarizedPort(mem, _)) => + /* Palmer: The outer memory's write enable port, or a constant 1 if + * there isn't a write enable port. */ + WRef(mem) + case None => + /* Palmer: If there is no input port on the source memory port + * then we don't ever want to turn on this write + * enable. Otherwise, we just _always_ turn on the + * write enable port on the inner memory. */ + if (memPort.src.input.isEmpty) zero else one + } + + // Chip enable port mapping + val memChipEnable = memPort.src.chipEnable match { + case Some(PolarizedPort(mem, _)) => WRef(mem) + case None => one + } + + // Read enable port mapping + /* Palmer: It's safe to ignore read enables, but we pass them through + * to the vendor memory if there's a port on there that + * implements the read enables. */ + (memPort.src.readEnable, libPort.src.readEnable) match { + case (_, None) => + case (Some(PolarizedPort(mem, _)), Some(PolarizedPort(lib, lib_polarity))) => + stmts += connectPorts(andAddrMatch(WRef(mem)), lib, lib_polarity) + case (None, Some(PolarizedPort(lib, lib_polarity))) => + stmts += connectPorts(andAddrMatch(and(not(memWriteEnable), memChipEnable)), lib, lib_polarity) + } + + /* Palmer: This is actually the memory compiler: it figures out how to + * implement the outer memory's collection of ports using what + * the inner memory has availiable. */ + ((libPort.src.maskPort, libPort.src.writeEnable, libPort.src.chipEnable): @unchecked) match { + case ( + Some(PolarizedPort(mask, mask_polarity)), + Some(PolarizedPort(we, we_polarity)), + Some(PolarizedPort(en, en_polarity)) + ) => + /* Palmer: This is the simple option: every port exists. */ + stmts += connectPorts(memMask, mask, mask_polarity) + stmts += connectPorts(andAddrMatch(memWriteEnable), we, we_polarity) + stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity) + case (Some(PolarizedPort(mask, mask_polarity)), Some(PolarizedPort(we, we_polarity)), None) => + /* Palmer: If we don't have a chip enable but do have mask ports. */ + stmts += connectPorts(memMask, mask, mask_polarity) + stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)), we, we_polarity) + case (None, Some(PolarizedPort(we, we_polarity)), chipEnable) => + if (bitWidth(memMask.tpe) == 1) { + /* Palmer: If we're expected to provide mask ports without a + * memory that actually has them then we can use the + * write enable port instead of the mask port. */ + chipEnable match { + case Some(PolarizedPort(en, en_polarity)) => + stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)), we, we_polarity) + stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity) + case _ => + stmts += connectPorts( + andAddrMatch(and(and(memWriteEnable, memChipEnable), memMask)), + we, + we_polarity + ) + } + } else { + System.err.println("cannot emulate multi-bit mask ports with write enable") + return None + } + case (None, None, None) => + // No write ports to match up (this may be a read-only port). + // This isn't necessarily an error condition. + } + } + // Cat macro outputs for selection + memPort.src.output match { + case Some(PolarizedPort(mem, _)) if cats.nonEmpty => + val name = s"${mem}_$i" + stmts += DefNode(NoInfo, name, cat(cats.toSeq.reverse)) + outputs.getOrElseUpdate(mem, ArrayBuffer[(Expression, Expression)]()) += + (addrMatchReg -> WRef(name)) + case _ => + } + } + } + // Connect mem outputs + val zeroOutputValue: Expression = UIntLiteral(0, IntWidth(mem.src.width)) + mem.src.ports.foreach { port => + port.output match { + case Some(PolarizedPort(mem, _)) => + outputs.get(mem) match { + case Some(select) => + val output = select.foldRight(zeroOutputValue) { case ((cond, tval), fval) => + Mux(cond, tval, fval, fval.tpe) + } + stmts += Connect(NoInfo, WRef(mem), output) + case None => + } + case None => + } + } + + Some((mem.module(Block(stmts.toSeq)), lib)) + } + + def run(c: Circuit): Circuit = { + var firstLib = true + val modules = (mems, libs) match { + case (Some(mems), Some(libs)) => + // Try to compile each of the memories in mems. + // The 'state' is c.modules, which is a list of all the firrtl modules + // in the 'circuit'. + mems.foldLeft(c.modules) { (modules, mem) => + val sram = mem.src + def groupMatchesMask(group: SRAMGroup, mem: SRAMMacro): Boolean = { + val memMask = mem.ports.map(_.maskGran).find(_.isDefined).flatten + val libMask = group.ports.map(_.maskGran).find(_.isDefined).flatten + (memMask, libMask) match { + case (None, _) => true + case (Some(_), None) => false + case (Some(m), Some(l)) => l <= m //Ignore memories that don't have nice mask + } + } + // Add compiler memories that might map well to libs + val compLibs = compilers match { + case Some(SRAMCompiler(_, groups)) => + groups + .filter(g => g.family == sram.family && groupMatchesMask(g, sram)) + .map(g => { + for { + w <- g.width + d <- g.depth if (sram.width % w == 0) && (sram.depth % d == 0) + } yield Seq(new Macro(buildSRAMMacro(g, d, w, g.vt.head))) + }) + case None => Seq() + } + val fullLibs = libs ++ compLibs.flatten.flatten + + // Try to compile mem against each lib in libs, keeping track of the + // best compiled version, external lib used, and cost. + val (best, _) = fullLibs.foldLeft(None: Option[(Module, Macro)], Double.MaxValue) { + case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size => + /* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same + * order, but I'm starting with what actually gets generated. */ + System.err.println(s"INFO: unable to compile ${mem.src.name} using ${lib.src.name} port count must match") + (best, cost) + case ((best, cost), lib) => + // Run the cost function to evaluate this potential compile. + costMetric.cost(mem, lib) match { + case Some(newCost) => + //System.err.println(s"Cost of ${lib.src.name} for ${mem.src.name}: ${newCost}") + // Try compiling + compile(mem, lib) match { + // If it was successful and the new cost is lower + case Some(p) if newCost < cost => (Some(p), newCost) + case _ => (best, cost) + } + case _ => (best, cost) // Cost function rejected this combination. + } + } + + // If we were able to compile anything, then replace the original module + // in the modules list with a compiled version, as well as the extmodule + // stub for the lib. + best match { + case None => + if (mode == MacroCompilerAnnotation.Strict) + throw MacroCompilerException( + s"Target memory ${mem.src.name} could not be compiled and strict mode is activated - aborting." + ) + else + modules + case Some((mod, bb)) => + hammerIR match { + case Some(f) => + val hammerIRWriter = new FileWriter(new File(f), !firstLib) + if (firstLib) hammerIRWriter.write("[\n") + hammerIRWriter.write(bb.src.toJSON().toString()) + hammerIRWriter.write("\n,\n") + hammerIRWriter.close() + firstLib = false + case None => + } + modules.filterNot(m => m.name == mod.name || m.name == bb.blackbox.name) ++ Seq(mod, bb.blackbox) + } + } + case _ => c.modules + } + c.copy(modules = modules) + } +} + +class MacroCompilerTransform extends Transform with DependencyAPIMigration { + override def prerequisites: Seq[TransformDependency] = Forms.LowForm + override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized + override def optionalPrerequisiteOf: Seq[Dependency[Emitter]] = Forms.LowEmitters + override def invalidates(a: Transform) = false + + def execute(state: CircuitState): CircuitState = state.annotations.collect { case a: MacroCompilerAnnotation => + a + } match { + case Seq(anno: MacroCompilerAnnotation) => + val MacroCompilerAnnotation.Params( + memFile, + memFileFormat, + libFile, + hammerIR, + costMetric, + mode, + useCompiler, + forceCompile, + forceSynflops + ) = anno.params + if (mode == MacroCompilerAnnotation.FallbackSynflops) { + throw new UnsupportedOperationException("Not implemented yet") + } + + // Check that we don't have any modules both forced to compile and synflops. + assert(forceCompile.intersect(forceSynflops).isEmpty, "Cannot have modules both forced to compile and synflops") + + // Read, eliminate None, get only SRAM, make firrtl macro + val mems: Option[Seq[Macro]] = (memFileFormat match { + case Some("conf") => readConfFromPath(Some(memFile)) + case _ => mdf.macrolib.Utils.readMDFFromPath(Some(memFile)) + }) match { + case Some(x: Seq[mdf.macrolib.Macro]) => + Some(filterForSRAM(Some(x)).getOrElse(List()).map { new Macro(_) }) + case _ => None + } + val libs: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(libFile) match { + case Some(x: Seq[mdf.macrolib.Macro]) => + Some(filterForSRAM(Some(x)).getOrElse(List()).map { new Macro(_) }) + case _ => None + } + val compilers: Option[mdf.macrolib.SRAMCompiler] = mdf.macrolib.Utils.readMDFFromPath(libFile) match { + case Some(x: Seq[mdf.macrolib.Macro]) => + if (useCompiler) { + findSRAMCompiler(Some(x)) + } else None + case _ => None + } + + // Helper function to turn a set of mem names into a Seq[Macro]. + def setToSeqMacro(names: Set[String]): Seq[Macro] = { + names.toSeq.map(memName => mems.get.collectFirst { case m if m.src.name == memName => m }.get) + } + + // Build lists of memories for compilation and synflops. + val memCompile = mems.map { actualMems => + val memsAdjustedForMode = if (mode == MacroCompilerAnnotation.Synflops) Seq.empty else actualMems + memsAdjustedForMode.filterNot(m => forceSynflops.contains(m.src.name)) ++ setToSeqMacro(forceCompile) + } + val memSynflops: Seq[Macro] = mems.map { actualMems => + // + val memsAdjustedForMode = if (mode == MacroCompilerAnnotation.Synflops) actualMems else Seq.empty + memsAdjustedForMode.filterNot(m => forceCompile.contains(m.src.name)) ++ setToSeqMacro(forceSynflops) + }.getOrElse(Seq.empty) + + val transforms = Seq( + new MacroCompilerPass(memCompile, libs, compilers, hammerIR, costMetric, mode), + new SynFlopsPass( + true, + memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) { + libs.get + } else { + Seq.empty + }) + ) + ) + transforms.foldLeft(state)((s, xform) => xform.runTransform(s)) + case _ => state + } +} + +class MacroCompilerOptimizations extends SeqTransform with DependencyAPIMigration { + override def prerequisites: Seq[TransformDependency] = Forms.LowForm + override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized + override def optionalPrerequisiteOf: Seq[Dependency[Emitter]] = Forms.LowEmitters + override def invalidates(a: Transform) = false + + def transforms: Seq[Transform] = Seq( + passes.RemoveValidIf, + new firrtl.transforms.ConstantPropagation, + passes.memlib.VerilogMemDelays, + new firrtl.transforms.ConstantPropagation, + passes.SplitExpressions, + passes.CommonSubexpressionElimination + ) +} + +object MacroCompiler extends App { + sealed trait MacroParam + case object Macros extends MacroParam + case object MacrosFormat extends MacroParam + case object Library extends MacroParam + case object Verilog extends MacroParam + case object Firrtl extends MacroParam + case object HammerIR extends MacroParam + case object CostFunc extends MacroParam + case object Mode extends MacroParam + case object UseCompiler extends MacroParam + + type MacroParamMap = Map[MacroParam, String] + type CostParamMap = Map[String, String] + type ForcedMemories = (Set[String], Set[String]) + val modeOptions: Seq[String] = MacroCompilerAnnotation.options.map { case (_, cmd, description) => + s" $cmd: $description" + } + val usage: String = (Seq( + "Options:", + " -n, --macro-conf: The set of macros to compile in firrtl-generated conf format (exclusive with -m)", + " -m, --macro-mdf: The set of macros to compile in MDF JSON format (exclusive with -n)", + " -l, --library: The set of macros that have blackbox instances", + " -u, --use-compiler: Flag, whether to use the memory compiler defined in library", + " -v, --verilog: Verilog output", + " -f, --firrtl: FIRRTL output (optional)", + " -hir, --hammer-ir: Hammer-IR output currently only needed for IP compilers", + " -c, --cost-func: Cost function to use. Optional (default: \"default\")", + " -cp, --cost-param: Cost function parameter. (Optional depending on the cost function.). e.g. -c ExternalMetric -cp path /path/to/my/cost/script", + " --force-compile [mem]: Force the given memory to be compiled to target libs regardless of the mode", + " --force-synflops [mem]: Force the given memory to be compiled via synflops regardless of the mode", + " --mode:" + ) ++ modeOptions).mkString("\n") + + @tailrec + def parseArgs( + map: MacroParamMap, + costMap: CostParamMap, + forcedMemories: ForcedMemories, + args: List[String] + ): (MacroParamMap, CostParamMap, ForcedMemories) = + args match { + case Nil => (map, costMap, forcedMemories) + case ("-n" | "--macro-conf") :: value :: tail => + parseArgs(map + (Macros -> value) + (MacrosFormat -> "conf"), costMap, forcedMemories, tail) + case ("-m" | "--macro-mdf") :: value :: tail => + parseArgs(map + (Macros -> value) + (MacrosFormat -> "mdf"), costMap, forcedMemories, tail) + case ("-l" | "--library") :: value :: tail => + parseArgs(map + (Library -> value), costMap, forcedMemories, tail) + case ("-u" | "--use-compiler") :: tail => + parseArgs(map + (UseCompiler -> ""), costMap, forcedMemories, tail) + case ("-v" | "--verilog") :: value :: tail => + parseArgs(map + (Verilog -> value), costMap, forcedMemories, tail) + case ("-f" | "--firrtl") :: value :: tail => + parseArgs(map + (Firrtl -> value), costMap, forcedMemories, tail) + case ("-hir" | "--hammer-ir") :: value :: tail => + parseArgs(map + (HammerIR -> value), costMap, forcedMemories, tail) + case ("-c" | "--cost-func") :: value :: tail => + parseArgs(map + (CostFunc -> value), costMap, forcedMemories, tail) + case ("-cp" | "--cost-param") :: value1 :: value2 :: tail => + parseArgs(map, costMap + (value1 -> value2), forcedMemories, tail) + case "--force-compile" :: value :: tail => + parseArgs(map, costMap, forcedMemories.copy(_1 = forcedMemories._1 + value), tail) + case "--force-synflops" :: value :: tail => + parseArgs(map, costMap, forcedMemories.copy(_2 = forcedMemories._2 + value), tail) + case "--mode" :: value :: tail => + parseArgs(map + (Mode -> value), costMap, forcedMemories, tail) + case arg :: _ => + println(s"Unknown field $arg\n") + println(usage) + sys.exit(1) + } + + def run(args: List[String]): Unit = { + val (params, costParams, forcedMemories) = + parseArgs(Map[MacroParam, String](), Map[String, String](), (Set.empty, Set.empty), args) + try { + val macros = params.get(MacrosFormat) match { + case Some("conf") => + filterForSRAM(readConfFromPath(params.get(Macros))).get.map(x => new Macro(x).blackbox) + case _ => + filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(params.get(Macros))).get + .map(x => new Macro(x).blackbox) + } + + if (macros.nonEmpty) { + // Note: the last macro in the input list is (seemingly arbitrarily) + // determined as the firrtl "top-level module". + val circuit = Circuit(NoInfo, macros, macros.last.name) + val annotations = AnnotationSeq( + Seq( + MacroCompilerAnnotation( + circuit.main, + MacroCompilerAnnotation.Params( + params(Macros), + params.get(MacrosFormat), + params.get(Library), + params.get(HammerIR), + CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams), + MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")), + params.contains(UseCompiler), + forceCompile = forcedMemories._1, + forceSynflops = forcedMemories._2 + ) + ) + ) + ) + + // The actual MacroCompilerTransform basically just generates an input circuit + val macroCompilerInput = CircuitState(circuit, annotations) + val macroCompiled = (new MacroCompilerTransform).execute(macroCompilerInput) + + // Run FIRRTL compiler + // For each generated module, have to create a new circuit with that module + // as top, and all other modules as ExtModules. This guarantees all modules + // are elaborated + val verilog = macroCompiled.circuit.modules + .map(_.name) + .map { macroName => + val (mainMod, otherMods) = macroCompiled.circuit.modules.partition(_.name == macroName) + val extMods = otherMods.map(m => ExtModule(NoInfo, m.name, m.ports, m.name, Nil)) + + val circuit = Circuit(NoInfo, mainMod ++ extMods, macroName) + (new FirrtlStage) + .execute( + Array.empty, + Seq( + OutputFileAnnotation(params.get(Verilog).get), + RunFirrtlTransformAnnotation(new VerilogEmitter), + EmitCircuitAnnotation(classOf[VerilogEmitter]), + FirrtlSourceAnnotation(circuit.serialize) + ) + ) + .collect { case c: EmittedVerilogCircuitAnnotation => c } + .head + .value + .value + } + .mkString("\n") + + val verilogWriter = new FileWriter(new File(params.get(Verilog).get)) + verilogWriter.write(verilog) + verilogWriter.close() + + params.get(HammerIR) match { + case Some(hammerIRFile: String) => + val lines = FileUtils.getLines(hammerIRFile).toList + val hammerIRWriter = new FileWriter(new File(hammerIRFile)) + // JSON means we need to destroy the last comma :( + lines.dropRight(1).foreach(l => hammerIRWriter.write(l + "\n")) + hammerIRWriter.write("]\n") + hammerIRWriter.close() + case None => + } + } else { + // Warn user + System.err.println("WARNING: Empty *.mems.conf file. No memories generated.") + + // Emit empty verilog file if no macros found + params.get(Verilog) match { + case Some(verilogFile: String) => + // Create an empty verilog file + val verilogWriter = new FileWriter(new File(verilogFile)) + verilogWriter.close() + case None => + } + params.get(HammerIR) match { + case Some(hammerIRFile: String) => + // Create an empty HammerIR file + val hammerIRWriter = new FileWriter(new File(hammerIRFile)) + hammerIRWriter.write("[]\n") + hammerIRWriter.close() + case None => + } + } + } catch { + case e: java.util.NoSuchElementException => + if (args.isEmpty) { + println("Command line arguments must be specified") + } else { + e.printStackTrace() + } + e.printStackTrace() + sys.exit(1) + case e: MacroCompilerException => + println(usage) + e.printStackTrace() + sys.exit(1) + case e: Throwable => + throw e + } + } + + run(args.toList) +} diff --git a/src/main/scala/barstools/macros/SynFlopsPass.scala b/src/main/scala/barstools/macros/SynFlopsPass.scala new file mode 100644 index 00000000..5dda0476 --- /dev/null +++ b/src/main/scala/barstools/macros/SynFlopsPass.scala @@ -0,0 +1,152 @@ +// See LICENSE for license details. + +package barstools.macros + +import barstools.macros.Utils._ +import firrtl.Utils.{one, zero} +import firrtl._ +import firrtl.ir._ +import firrtl.passes.MemPortUtils.memPortField + +import scala.collection.mutable + +class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pass { + val extraMods: mutable.ArrayBuffer[Module] = scala.collection.mutable.ArrayBuffer.empty[Module] + lazy val libMods: Map[String, Module] = libs.map { lib => + lib.src.name -> { + val (dataType, dataWidth) = lib.src.ports.foldLeft(None: Option[BigInt])((res, port) => + (res, port.maskPort) match { + case (_, None) => + res + case (None, Some(_)) => + Some(port.effectiveMaskGran) + case (Some(x), Some(_)) => + assert(x == port.effectiveMaskGran) + res + } + ) match { + case None => (UIntType(IntWidth(lib.src.width)), lib.src.width) + case Some(gran) => (UIntType(IntWidth(gran)), gran.intValue) + } + + val maxDepth = firrtl.Utils.min(lib.src.depth, 1 << 26) + + // Change macro to be mapped onto to look like the below mem + // by changing its depth, and width + val lib_macro = new Macro( + lib.src.copy( + name = "split_" + lib.src.name, + depth = maxDepth, + width = dataWidth, + ports = lib.src.ports.map(p => + p.copy( + width = p.width.map(_ => dataWidth), + depth = p.depth.map(_ => maxDepth), + maskGran = p.maskGran.map(_ => dataWidth) + ) + ) + ) + ) + val mod_macro = new MacroCompilerPass(None, None, None, None).compile(lib, lib_macro) + val (real_mod, real_macro) = mod_macro.get + + val mem = DefMemory( + NoInfo, + "ram", + dataType, + maxDepth, + 1, // writeLatency + 1, // readLatency. This is possible because of VerilogMemDelays + real_macro.readers.indices.map(i => s"R_$i"), + real_macro.writers.indices.map(i => s"W_$i"), + real_macro.readwriters.indices.map(i => s"RW_$i") + ) + + val readConnects = real_macro.readers.zipWithIndex.flatMap { case (r, i) => + val clock = portToExpression(r.src.clock.get) + val address = portToExpression(r.src.address) + val enable = (r.src.chipEnable, r.src.readEnable) match { + case (Some(en_port), Some(re_port)) => + and(portToExpression(en_port), portToExpression(re_port)) + case (Some(en_port), None) => portToExpression(en_port) + case (None, Some(re_port)) => portToExpression(re_port) + case (None, None) => one + } + val data = memPortField(mem, s"R_$i", "data") + val read = data + Seq( + Connect(NoInfo, memPortField(mem, s"R_$i", "clk"), clock), + Connect(NoInfo, memPortField(mem, s"R_$i", "addr"), address), + Connect(NoInfo, memPortField(mem, s"R_$i", "en"), enable), + Connect(NoInfo, WRef(r.src.output.get.name), read) + ) + } + + val writeConnects = real_macro.writers.zipWithIndex.flatMap { case (w, i) => + val clock = portToExpression(w.src.clock.get) + val address = portToExpression(w.src.address) + val enable = (w.src.chipEnable, w.src.writeEnable) match { + case (Some(en), Some(we)) => + and(portToExpression(en), portToExpression(we)) + case (Some(en), None) => portToExpression(en) + case (None, Some(we)) => portToExpression(we) + case (None, None) => zero // is it possible? + } + val mask = w.src.maskPort match { + case Some(m) => portToExpression(m) + case None => one + } + val data = memPortField(mem, s"W_$i", "data") + val write = portToExpression(w.src.input.get) + Seq( + Connect(NoInfo, memPortField(mem, s"W_$i", "clk"), clock), + Connect(NoInfo, memPortField(mem, s"W_$i", "addr"), address), + Connect(NoInfo, memPortField(mem, s"W_$i", "en"), enable), + Connect(NoInfo, memPortField(mem, s"W_$i", "mask"), mask), + Connect(NoInfo, data, write) + ) + } + + val readwriteConnects = real_macro.readwriters.zipWithIndex.flatMap { case (rw, i) => + val clock = portToExpression(rw.src.clock.get) + val address = portToExpression(rw.src.address) + val wmode = rw.src.writeEnable match { + case Some(we) => portToExpression(we) + case None => zero // is it possible? + } + val wmask = rw.src.maskPort match { + case Some(wm) => portToExpression(wm) + case None => one + } + val enable = (rw.src.chipEnable, rw.src.readEnable) match { + case (Some(en), Some(re)) => + and(portToExpression(en), or(portToExpression(re), wmode)) + case (Some(en), None) => portToExpression(en) + case (None, Some(re)) => or(portToExpression(re), wmode) + case (None, None) => one + } + val wdata = memPortField(mem, s"RW_$i", "wdata") + val rdata = memPortField(mem, s"RW_$i", "rdata") + val write = portToExpression(rw.src.input.get) + val read = rdata + Seq( + Connect(NoInfo, memPortField(mem, s"RW_$i", "clk"), clock), + Connect(NoInfo, memPortField(mem, s"RW_$i", "addr"), address), + Connect(NoInfo, memPortField(mem, s"RW_$i", "en"), enable), + Connect(NoInfo, memPortField(mem, s"RW_$i", "wmode"), wmode), + Connect(NoInfo, memPortField(mem, s"RW_$i", "wmask"), wmask), + Connect(NoInfo, WRef(rw.src.output.get.name), read), + Connect(NoInfo, wdata, write) + ) + } + + extraMods.append(real_macro.module(Block(mem +: (readConnects ++ writeConnects ++ readwriteConnects)))) + real_mod + } + }.toMap + + def run(c: Circuit): Circuit = { + if (!synflops) c + else c.copy(modules = c.modules.map(m => libMods.getOrElse(m.name, m)) ++ extraMods) + } +} diff --git a/src/main/scala/barstools/macros/Utils.scala b/src/main/scala/barstools/macros/Utils.scala new file mode 100644 index 00000000..2bcd116f --- /dev/null +++ b/src/main/scala/barstools/macros/Utils.scala @@ -0,0 +1,262 @@ +// See LICENSE for license details. + +package barstools.macros + +import firrtl.Utils.BoolType +import firrtl.ir._ +import firrtl.passes.memlib._ +import firrtl.{PrimOps, _} +import mdf.macrolib.{Input => _, Output => _, _} + +import scala.language.implicitConversions + +object MacroCompilerMath { + def ceilLog2(x: BigInt): Int = (x - 1).bitLength +} + +class FirrtlMacroPort(port: MacroPort) { + val src: MacroPort = port + + val isReader: Boolean = port.output.nonEmpty && port.input.isEmpty + val isWriter: Boolean = port.input.nonEmpty && port.output.isEmpty + val isReadWriter: Boolean = port.input.nonEmpty && port.output.nonEmpty + + val addrType: UIntType = UIntType(IntWidth(MacroCompilerMath.ceilLog2(port.depth.get).max(1))) + val dataType: UIntType = UIntType(IntWidth(port.width.get)) + val maskType: UIntType = UIntType(IntWidth(port.width.get / port.effectiveMaskGran)) + + // Bundle representing this macro port. + val tpe: BundleType = BundleType( + Seq(Field(port.address.name, Flip, addrType)) ++ + port.clock.map(p => Field(p.name, Flip, ClockType)) ++ + port.input.map(p => Field(p.name, Flip, dataType)) ++ + port.output.map(p => Field(p.name, Default, dataType)) ++ + port.chipEnable.map(p => Field(p.name, Flip, BoolType)) ++ + port.readEnable.map(p => Field(p.name, Flip, BoolType)) ++ + port.writeEnable.map(p => Field(p.name, Flip, BoolType)) ++ + port.maskPort.map(p => Field(p.name, Flip, maskType)) + ) + val ports: Seq[Port] = tpe.fields.map(f => + Port( + NoInfo, + f.name, + f.flip match { + case Default => Output + case Flip => Input + }, + f.tpe + ) + ) +} + +// Reads an SRAMMacro and generates firrtl blackboxes. +class Macro(srcMacro: SRAMMacro) { + val src: SRAMMacro = srcMacro + + val firrtlPorts: Seq[FirrtlMacroPort] = srcMacro.ports.map { new FirrtlMacroPort(_) } + + val writers: Seq[FirrtlMacroPort] = firrtlPorts.filter(p => p.isWriter) + val readers: Seq[FirrtlMacroPort] = firrtlPorts.filter(p => p.isReader) + val readwriters: Seq[FirrtlMacroPort] = firrtlPorts.filter(p => p.isReadWriter) + + val sortedPorts: Seq[FirrtlMacroPort] = writers ++ readers ++ readwriters + val extraPorts: Seq[(String, UIntLiteral)] = srcMacro.extraPorts.map { p => + assert(p.portType == Constant) // TODO: release it? + val name = p.name + val width = BigInt(p.width.toLong) + val value = BigInt(p.value.toLong) + name -> UIntLiteral(value, IntWidth(width)) + } + + // Bundle representing this memory blackbox + val tpe: BundleType = BundleType(firrtlPorts.flatMap(_.tpe.fields)) + + private val modPorts = firrtlPorts.flatMap(_.ports) ++ + extraPorts.map { case (name, value) => Port(NoInfo, name, Input, value.tpe) } + val blackbox: ExtModule = ExtModule(NoInfo, srcMacro.name, modPorts, srcMacro.name, Nil) + def module(body: Statement): Module = Module(NoInfo, srcMacro.name, modPorts, body) +} + +object Utils { + def filterForSRAM(s: Option[Seq[mdf.macrolib.Macro]]): Option[Seq[mdf.macrolib.SRAMMacro]] = { + s match { + case Some(l: Seq[mdf.macrolib.Macro]) => + Some(l.filter { _.isInstanceOf[mdf.macrolib.SRAMMacro] }.map { m => m.asInstanceOf[mdf.macrolib.SRAMMacro] }) + case _ => None + } + } + // This utility reads a conf in and returns MDF like mdf.macrolib.Utils.readMDFFromPath + def readConfFromPath(path: Option[String]): Option[Seq[mdf.macrolib.Macro]] = { + path.map(p => Utils.readConfFromString(FileUtils.getText(p))) + } + def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = { + MemConf.fromString(str).map { m: MemConf => + val ports = m.ports.map { case (port, num) => Seq.fill(num)(port) }.reduce(_ ++ _) + SRAMMacro( + m.name, + m.width, + m.depth, + Utils.portSpecToFamily(ports), + Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, ports) + ) + } + } + def portSpecToFamily(ports: Seq[MemPort]): String = { + val numR = ports.count { case ReadPort => true; case _ => false } + val numW = ports.count { case WritePort | MaskedWritePort => true; case _ => false } + val numRW = ports.count { case ReadWritePort | MaskedReadWritePort => true; case _ => false } + val numRStr = if (numR > 0) s"${numR}r" else "" + val numWStr = if (numW > 0) s"${numW}w" else "" + val numRWStr = if (numRW > 0) s"${numRW}rw" else "" + numRStr + numWStr + numRWStr + } + // This translates between two represenations of ports + def portSpecToMacroPort(width: Int, depth: BigInt, maskGran: Option[Int], ports: Seq[MemPort]): Seq[MacroPort] = { + var numR = 0 + var numW = 0 + var numRW = 0 + ports.map { + case ReadPort => + val portName = s"R$numR" + numR += 1 + MacroPort( + width = Some(width), + depth = Some(depth), + address = PolarizedPort(s"${portName}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)), + readEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)), + output = Some(PolarizedPort(s"${portName}_data", ActiveHigh)) + ) + case WritePort => + val portName = s"W$numW" + numW += 1 + MacroPort( + width = Some(width), + depth = Some(depth), + address = PolarizedPort(s"${portName}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)), + writeEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)), + input = Some(PolarizedPort(s"${portName}_data", ActiveHigh)) + ) + case MaskedWritePort => + val portName = s"W$numW" + numW += 1 + MacroPort( + width = Some(width), + depth = Some(depth), + address = PolarizedPort(s"${portName}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)), + writeEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)), + maskPort = Some(PolarizedPort(s"${portName}_mask", ActiveHigh)), + maskGran = maskGran, + input = Some(PolarizedPort(s"${portName}_data", ActiveHigh)) + ) + case ReadWritePort => + val portName = s"RW$numRW" + numRW += 1 + MacroPort( + width = Some(width), + depth = Some(depth), + address = PolarizedPort(s"${portName}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)), + chipEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)), + writeEnable = Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)), + input = Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)), + output = Some(PolarizedPort(s"${portName}_rdata", ActiveHigh)) + ) + case MaskedReadWritePort => + val portName = s"RW$numRW" + numRW += 1 + MacroPort( + width = Some(width), + depth = Some(depth), + address = PolarizedPort(s"${portName}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)), + chipEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)), + writeEnable = Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)), + maskPort = Some(PolarizedPort(s"${portName}_wmask", ActiveHigh)), + maskGran = maskGran, + input = Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)), + output = Some(PolarizedPort(s"${portName}_rdata", ActiveHigh)) + ) + } + } + def findSRAMCompiler(s: Option[Seq[mdf.macrolib.Macro]]): Option[mdf.macrolib.SRAMCompiler] = { + s match { + case Some(l: Seq[mdf.macrolib.Macro]) => + l.collectFirst { case x: mdf.macrolib.SRAMCompiler => + x + } + case _ => None + } + } + def buildSRAMMacros(s: mdf.macrolib.SRAMCompiler): Seq[mdf.macrolib.SRAMMacro] = { + for { + g <- s.groups + d <- g.depth + w <- g.width + vt <- g.vt + } yield mdf.macrolib.SRAMMacro( + makeName(g, d, w, vt), + w, + d, + g.family, + g.ports.map(_.copy(width = Some(w), depth = Some(d))), + vt, + g.mux, + g.extraPorts + ) + } + def buildSRAMMacro(g: mdf.macrolib.SRAMGroup, d: Int, w: Int, vt: String): mdf.macrolib.SRAMMacro = { + mdf.macrolib.SRAMMacro( + makeName(g, d, w, vt), + w, + d, + g.family, + g.ports.map(_.copy(width = Some(w), depth = Some(d))), + vt, + g.mux, + g.extraPorts + ) + } + def makeName(g: mdf.macrolib.SRAMGroup, depth: Int, width: Int, vt: String): String = { + g.name.foldLeft("") { (builder, next) => + next match { + case "depth" | "DEPTH" => builder + depth + case "width" | "WIDTH" => builder + width + case "vt" => builder + vt.toLowerCase + case "VT" => builder + vt.toUpperCase + case "family" => builder + g.family.toLowerCase + case "FAMILY" => builder + g.family.toUpperCase + case "mux" | "MUX" => builder + g.mux + case other => builder + other + } + } + } + + def and(e1: Expression, e2: Expression): DoPrim = + DoPrim(PrimOps.And, Seq(e1, e2), Nil, e1.tpe) + def or(e1: Expression, e2: Expression): DoPrim = + DoPrim(PrimOps.Or, Seq(e1, e2), Nil, e1.tpe) + def bits(e: Expression, high: BigInt, low: BigInt): Expression = + DoPrim(PrimOps.Bits, Seq(e), Seq(high, low), UIntType(IntWidth(high - low + 1))) + def bits(e: Expression, idx: BigInt): Expression = bits(e, idx, idx) + def cat(es: Seq[Expression]): Expression = + if (es.size == 1) es.head + else DoPrim(PrimOps.Cat, Seq(es.head, cat(es.tail)), Nil, UnknownType) + def not(e: Expression): DoPrim = + DoPrim(PrimOps.Not, Seq(e), Nil, e.tpe) + + // Convert a port to a FIRRTL expression, handling polarity along the way. + def portToExpression(pp: PolarizedPort): Expression = + portToExpression(WRef(pp.name), Some(pp.polarity)) + + def portToExpression(exp: Expression, polarity: Option[PortPolarity]): Expression = + polarity match { + case Some(ActiveLow) | Some(NegativeEdge) => not(exp) + case _ => exp + } + + // Check if a number is a power of two + def isPowerOfTwo(x: Int): Boolean = (x & (x - 1)) == 0 +} diff --git a/src/main/scala/barstools/tapeout/transforms/ExtraTransforms.scala b/src/main/scala/barstools/tapeout/transforms/ExtraTransforms.scala new file mode 100644 index 00000000..f7ef25c6 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/ExtraTransforms.scala @@ -0,0 +1,26 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl.Mappers._ +import firrtl._ +import firrtl.annotations.{CircuitTarget, ModuleTarget, SingleTargetAnnotation} +import firrtl.ir._ +import firrtl.stage.Forms +import firrtl.stage.TransformManager.TransformDependency +import firrtl.options.{Dependency} + +class ExtraLowTransforms extends Transform with DependencyAPIMigration { + // this PropagatePresetAnnotations is needed to run the RemoveValidIf pass (that is removed from CIRCT). + // additionally, since that pass isn't explicitly a prereq of the LowFormEmitter it + // needs to wrapped in this xform + override def prerequisites: Seq[TransformDependency] = Forms.LowForm :+ + Dependency[firrtl.transforms.PropagatePresetAnnotations] + override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized + override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters + override def invalidates(a: Transform): Boolean = false + + def execute(state: CircuitState): CircuitState = { + state + } +} diff --git a/src/main/scala/barstools/tapeout/transforms/GenerateModelStageMain.scala b/src/main/scala/barstools/tapeout/transforms/GenerateModelStageMain.scala new file mode 100644 index 00000000..08cd8d04 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/GenerateModelStageMain.scala @@ -0,0 +1,51 @@ +package barstools.tapeout.transforms + +import barstools.tapeout.transforms.stage._ +import firrtl._ +import firrtl.annotations._ +import firrtl.ir._ +import firrtl.options.{Dependency, InputAnnotationFileAnnotation, StageMain} +import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlStage, RunFirrtlTransformAnnotation} +import logger.LazyLogging + +private class GenerateModelStageMain(annotations: AnnotationSeq) extends LazyLogging { + val outAnno: Option[String] = annotations.collectFirst { case OutAnnoAnnotation(s) => s } + + val annoFiles: List[String] = annotations.flatMap { + case InputAnnotationFileAnnotation(f) => Some(f) + case _ => None + }.toList + + // Dump firrtl and annotation files + // Use global param outAnno + protected def dumpAnnos( + annotations: AnnotationSeq + ): Unit = { + outAnno.foreach { annoPath => + val outputFile = new java.io.PrintWriter(annoPath) + outputFile.write(JsonProtocol.serialize(annotations.filter(_ match { + case _: DeletedAnnotation => false + case _: EmittedComponent => false + case _: EmittedAnnotation[_] => false + case _: FirrtlCircuitAnnotation => false + case _: OutAnnoAnnotation => false + case _ => true + }))) + outputFile.close() + } + } + + def executeStageMain(): Unit = { + val annos = new FirrtlStage().execute(Array.empty, annotations) + + annos.collectFirst { case FirrtlCircuitAnnotation(circuit) => circuit } match { + case Some(circuit) => + dumpAnnos(annos) + case _ => + throw new Exception(s"executeStageMain failed while executing FIRRTL!\n") + } + } +} + +// main run class +object GenerateModelStageMain extends StageMain(new TapeoutStage()) diff --git a/src/main/scala/barstools/tapeout/transforms/retime/Retime.scala b/src/main/scala/barstools/tapeout/transforms/retime/Retime.scala new file mode 100644 index 00000000..1a9d6668 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/retime/Retime.scala @@ -0,0 +1,48 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms.retime + +import chisel3.experimental.RunFirrtlTransform +import firrtl.annotations._ +import firrtl.stage.Forms +import firrtl.stage.TransformManager.TransformDependency +import firrtl.{CircuitState, DependencyAPIMigration, Transform} + +case class RetimeAnnotation(target: Named) extends SingleTargetAnnotation[Named] { + override def duplicate(n: Named): Annotation = RetimeAnnotation(n) +} + +class RetimeTransform extends Transform with DependencyAPIMigration { + + override def prerequisites: Seq[TransformDependency] = Forms.LowForm + override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized + override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters + override def invalidates(a: Transform): Boolean = false + + override def execute(state: CircuitState): CircuitState = { + state.annotations.filter(_.isInstanceOf[RetimeAnnotation]) match { + case Nil => state + case seq => + seq.foreach { + case RetimeAnnotation(ModuleName(module, CircuitName(_))) => + logger.info(s"Retiming module $module") + case RetimeAnnotation(ComponentName(name, ModuleName(module, CircuitName(_)))) => + logger.info(s"Retiming instance $module.$name") + case _ => + throw new Exception(s"There should be RetimeAnnotations, got ${seq.mkString(" -- ")}") + } + state + } + } +} + +trait RetimeLib { + self: chisel3.Module => + + def retime[T <: chisel3.Module](module: T): Unit = { + chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform { + def transformClass: Class[_ <: Transform] = classOf[RetimeTransform] + def toFirrtl: Annotation = RetimeAnnotation(module.toNamed) + }) + } +} diff --git a/src/main/scala/barstools/tapeout/transforms/stage/TapeoutStage.scala b/src/main/scala/barstools/tapeout/transforms/stage/TapeoutStage.scala new file mode 100644 index 00000000..cdae1bfd --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/stage/TapeoutStage.scala @@ -0,0 +1,50 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms.stage + +import barstools.tapeout.transforms.GenerateModelStageMain +import chisel3.stage.ChiselCli +import firrtl.stage.{RunFirrtlTransformAnnotation} +import firrtl.AnnotationSeq +import firrtl.annotations.{Annotation, NoTargetAnnotation} +import firrtl.options.{HasShellOptions, Shell, ShellOption, Stage, Unserializable} +import firrtl.stage.FirrtlCli +import logger.Logger + +sealed trait TapeoutOption extends Unserializable { + this: Annotation => +} + +case class OutAnnoAnnotation(outAnno: String) extends NoTargetAnnotation with TapeoutOption + +object OutAnnoAnnotation extends HasShellOptions { + val options: Seq[ShellOption[_]] = Seq( + new ShellOption[String]( + longOption = "out-anno-file", + shortOption = Some("oaf"), + toAnnotationSeq = (s: String) => Seq(OutAnnoAnnotation(s)), + helpText = "out-anno-file" + ) + ) +} + +trait TapeoutCli { + this: Shell => + parser.note("Tapeout specific options") + + Seq( + OutAnnoAnnotation + ).foreach(_.addOptions(parser)) +} + +class TapeoutStage() extends Stage { + override val shell: Shell = new Shell(applicationName = "tapeout") with TapeoutCli with ChiselCli with FirrtlCli + + override def run(annotations: AnnotationSeq): AnnotationSeq = { + Logger.makeScope(annotations) { + val stageMain = new GenerateModelStageMain(annotations) + stageMain.executeStageMain() + } + annotations + } +} diff --git a/src/main/scala/barstools/tapeout/transforms/utils/FileUtils.scala b/src/main/scala/barstools/tapeout/transforms/utils/FileUtils.scala new file mode 100644 index 00000000..78d33e10 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/utils/FileUtils.scala @@ -0,0 +1,79 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms.utils + +import chisel3.experimental.{annotate, ChiselAnnotation} +import firrtl._ +import firrtl.annotations._ +import firrtl.stage.Forms +import firrtl.stage.TransformManager.TransformDependency +import firrtl.transforms.BlackBoxTargetDirAnno + +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 + val destDir = annos.map { + case BlackBoxTargetDirAnno(s) => Some(s) + 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 + } +} + +trait HasSetTechnologyLocation { + self: chisel3.Module => + + def setTechnologyLocation(dir: String) { + annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = { + TechnologyLocationAnnotation(dir) + } + }) + } +} + +case class TechnologyLocationAnnotation(dir: String) extends SingleTargetAnnotation[CircuitName] { + val target: CircuitName = CircuitName("All") + override def duplicate(n: CircuitName): Annotation = TechnologyLocationAnnotation(dir) +} + +class TechnologyLocation extends Transform with DependencyAPIMigration { + + override def prerequisites: Seq[TransformDependency] = Forms.LowForm + override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized + override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters + + def execute(state: CircuitState): CircuitState = { + throw new Exception("Technology Location transform execution doesn't work!") + } + + def get(state: CircuitState): String = { + val annos = state.annotations + val dir = annos.flatMap { + case TechnologyLocationAnnotation(dir) => Some(dir) + case _ => None + } + dir.length match { + case 0 => "" + case 1 => + val targetDir = new java.io.File(dir.head) + if (!targetDir.exists()) throw new Exception(s"Technology yaml directory $targetDir doesn't exist!") + dir.head + case _ => throw new Exception("Only 1 tech directory annotation allowed!") + } + } +} diff --git a/src/main/scala/barstools/tapeout/transforms/utils/LowerAnnotations.scala b/src/main/scala/barstools/tapeout/transforms/utils/LowerAnnotations.scala new file mode 100644 index 00000000..45502d6d --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/utils/LowerAnnotations.scala @@ -0,0 +1,5 @@ +package barstools.tapeout.transforms.utils + +object LowerName { + def apply(s: String): String = s.replace(".", "_").replace("[", "_").replace("]", "") +} diff --git a/src/main/scala/barstools/tapeout/transforms/utils/ProgrammaticBundle.scala b/src/main/scala/barstools/tapeout/transforms/utils/ProgrammaticBundle.scala new file mode 100644 index 00000000..ef98b294 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/utils/ProgrammaticBundle.scala @@ -0,0 +1,27 @@ +package barstools.tapeout.transforms.utils + +import chisel3._ + +import scala.collection.immutable.ListMap + +class CustomBundle[T <: Data](elts: (String, T)*) extends Record { + val elements = ListMap(elts.map { case (field, elt) => field -> chiselTypeOf(elt) }: _*) + def apply(elt: String): T = elements(elt) + def apply(elt: Int): T = elements(elt.toString) +} + +class CustomIndexedBundle[T <: Data](elts: (Int, T)*) extends Record { + // Must be String, Data + val elements = ListMap(elts.map { case (field, elt) => field.toString -> chiselTypeOf(elt) }: _*) + // TODO: Make an equivalent to the below work publicly (or only on subclasses?) + def indexedElements = ListMap(elts.map { case (field, elt) => field -> chiselTypeOf(elt) }: _*) + def apply(elt: Int): T = elements(elt.toString) +} + +object CustomIndexedBundle { + def apply[T <: Data](gen: T, idxs: Seq[Int]) = new CustomIndexedBundle(idxs.map(_ -> gen): _*) + // Allows Vecs of elements of different types/widths + def apply[T <: Data](gen: Seq[T]) = new CustomIndexedBundle(gen.zipWithIndex.map { case (elt, field) => + field -> elt + }: _*) +} diff --git a/src/main/scala/barstools/tapeout/transforms/utils/YamlHelpers.scala b/src/main/scala/barstools/tapeout/transforms/utils/YamlHelpers.scala new file mode 100644 index 00000000..9b58e083 --- /dev/null +++ b/src/main/scala/barstools/tapeout/transforms/utils/YamlHelpers.scala @@ -0,0 +1,23 @@ +package barstools.tapeout.transforms.utils + +import firrtl.FileUtils +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 = FileUtils.getTextResource(resource) + stream + case f if new File(f).exists => + FileUtils.getText(f) + case _ => + throw new Exception("No valid Yaml file found!") + } + yamlString.parseYamls.map(x => reader.read(x)) + } +} diff --git a/src/main/scala/mdf/macrolib/ConfReader.scala b/src/main/scala/mdf/macrolib/ConfReader.scala new file mode 100644 index 00000000..ec701d6e --- /dev/null +++ b/src/main/scala/mdf/macrolib/ConfReader.scala @@ -0,0 +1,95 @@ +package mdf.macrolib + +object ConfReader { + import scala.util.matching.Regex._ + + type ConfPort = (String, Boolean) // prefix (e.g. "RW0") and true if masked + + /** Rename ports like "read" to R0, "write" to W0, and "rw" to RW0, and + * return a count of read, write, and readwrite ports. + */ + def renamePorts(ports: Seq[String]): (Seq[ConfPort], Int, Int, Int) = { + var readCount = 0 + var writeCount = 0 + var readWriteCount = 0 + ( + ports.map { + _ match { + case "read" => readCount += 1; (s"R${readCount - 1}", false) + case "write" => writeCount += 1; (s"W${writeCount - 1}", false) + case "mwrite" => writeCount += 1; (s"W${writeCount - 1}", true) + case "rw" => readWriteCount += 1; (s"RW${readWriteCount - 1}", false) + case "mrw" => readWriteCount += 1; (s"RW${readWriteCount - 1}", true) + } + }, + readCount, + writeCount, + readWriteCount + ) + } + + def generateFirrtlPort(port: ConfPort, width: Int, depth: Int, maskGran: Option[Int]): MacroPort = { + val (prefix, masked) = port + val isReadWriter = prefix.startsWith("RW") + val isReader = prefix.startsWith("R") && !isReadWriter + val isWriter = prefix.startsWith("W") + val r = if (isReadWriter) "r" else "" + val w = if (isReadWriter) "w" else "" + MacroPort( + address = PolarizedPort(s"${prefix}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"${prefix}_clk", PositiveEdge)), + writeEnable = if (isReadWriter) Some(PolarizedPort(s"${prefix}_${w}mode", ActiveHigh)) else None, + output = if (isReader || isReadWriter) Some(PolarizedPort(s"${prefix}_${w}data", ActiveHigh)) else None, + input = if (isWriter || isReadWriter) Some(PolarizedPort(s"${prefix}_${r}data", ActiveHigh)) else None, + maskPort = if (masked) Some(PolarizedPort(s"${prefix}_${w}mask", ActiveHigh)) else None, + maskGran = if (masked) maskGran else None, + width = Some(width), + depth = Some(depth) + ) + } + + /** Read a conf line into a SRAMMacro, but returns an error string in Left + * instead of throwing errors if the line is malformed. + */ + def readSingleLineSafe(line: String): Either[String, SRAMMacro] = { + val pattern = """name ([^\s]+) depth (\d+) width (\d+) ports ([a-z,]+)\s?(?:mask_gran (\d+))?""".r + pattern.findFirstMatchIn(line) match { + case Some(m: Match) => { + val name: String = m.group(1) + val depth: Int = (m.group(2)).toInt + val width: Int = (m.group(3)).toInt + val ports: Seq[String] = (m.group(4)).split(",") + val (firrtlPorts, readPortCount, writePortCount, readWritePortCount) = renamePorts(ports) + val familyStr = + (if (readPortCount > 0) s"${readPortCount}r" else "") + + (if (writePortCount > 0) s"${writePortCount}w" else "") + + (if (readWritePortCount > 0) s"${readWritePortCount}rw" else "") + val maskGran: Option[Int] = Option(m.group(5)).map(_.toInt) + Right( + SRAMMacro( + name = name, + width = width, + depth = depth, + family = familyStr, + vt = "", + mux = 1, + ports = firrtlPorts.map(generateFirrtlPort(_, width, depth, maskGran)), + extraPorts = List() + ) + ) + } + case _ => Left("Input line did not match conf regex") + } + } + + /** Read a conf line into a SRAMMacro. */ + def readSingleLine(line: String): SRAMMacro = { + readSingleLineSafe(line).right.get + } + + /** Read the contents of the conf file into a seq of SRAMMacro. */ + def readFromString(contents: String): Seq[SRAMMacro] = { + // Trim, remove empty lines, then pass to readSingleLine + contents.split("\n").map(_.trim).filter(_ != "").map(readSingleLine(_)) + } +} diff --git a/src/main/scala/mdf/macrolib/FillerMacroBase.scala b/src/main/scala/mdf/macrolib/FillerMacroBase.scala new file mode 100644 index 00000000..688871b5 --- /dev/null +++ b/src/main/scala/mdf/macrolib/FillerMacroBase.scala @@ -0,0 +1,61 @@ +package mdf.macrolib + +import play.api.libs.json._ +import scala.language.implicitConversions + +// Filler and metal filler +abstract class FillerMacroBase(name: String, vt: String) extends Macro { + override def toString(): String = { + s"${this.getClass.getSimpleName}(name=${name}, vt=${vt})" + } + + override def toJSON(): JsObject = { + JsObject( + Seq( + "type" -> JsString(typeStr), + "name" -> Json.toJson(name), + "vt" -> Json.toJson(vt) + ) + ) + } +} +object FillerMacroBase { + def parseJSON(json: Map[String, JsValue]): Option[FillerMacroBase] = { + val typee: String = json.get("type") match { + case Some(x: JsString) => + x.value match { + case "" => return None + case x => x + } + case _ => return None + } + val name: String = json.get("name") match { + case Some(x: JsString) => + x.value match { + case "" => return None + case x => x + } + case _ => return None + } + val vt: String = json.get("vt") match { + case Some(x: JsString) => + x.value match { + case "" => return None + case x => x + } + case _ => return None + } + typee match { + case "metal filler cell" => Some(MetalFillerMacro(name, vt)) + case "filler cell" => Some(FillerMacro(name, vt)) + case _ => None + } + } +} + +case class FillerMacro(name: String, vt: String) extends FillerMacroBase(name, vt) { + override def typeStr = "filler cell" +} +case class MetalFillerMacro(name: String, vt: String) extends FillerMacroBase(name, vt) { + override def typeStr = "metal filler cell" +} diff --git a/src/main/scala/mdf/macrolib/FlipChipMacro.scala b/src/main/scala/mdf/macrolib/FlipChipMacro.scala new file mode 100644 index 00000000..88a20aea --- /dev/null +++ b/src/main/scala/mdf/macrolib/FlipChipMacro.scala @@ -0,0 +1,72 @@ +package mdf.macrolib + +import play.api.libs.json._ +import scala.collection.mutable.ListBuffer +import scala.language.implicitConversions + +// Flip Chip Macro +case class FlipChipMacro( + name: String, + bumpDimensions: (Int, Int), + bumpLocations: Seq[Seq[String]]) + extends Macro { + override def toJSON(): JsObject = { + + val output = new ListBuffer[(String, JsValue)]() + output.appendAll( + Seq( + "name" -> Json.toJson(name), + "type" -> Json.toJson(typeStr), + "bump_dimensions" -> JsArray(Seq(bumpDimensions._1, bumpDimensions._2).map { JsNumber(_) }), + "bump_locations" -> JsArray(bumpLocations.map(l => JsArray(l.map(JsString)))) + ) + ) + + JsObject(output) + } + val maxIONameSize = bumpLocations.foldLeft(0) { (size, row) => + row.foldLeft(size) { (size, str) => scala.math.max(size, str.length) } + } + def visualize: String = { + val output = new StringBuffer() + for (x <- 0 until bumpDimensions._1) { + for (y <- 0 until bumpDimensions._2) { + val name = bumpLocations(x)(y).drop(1).dropRight(1) + val extra = maxIONameSize - name.length() + val leftSpace = " " * (extra / 2) + val rightSpace = " " * (extra / 2 + extra % 2) + output.append(leftSpace + name + rightSpace + "|") + } + output.append("\n") + } + output.toString() + } + + override def typeStr = "flipchip" +} + +object FlipChipMacro { + def parseJSON(json: Map[String, JsValue]): Option[FlipChipMacro] = { + val name: String = json.get("name") match { + case Some(x: JsString) => x.as[String] + case _ => return None + } + + val bumpDimensions: (Int, Int) = json.get("bump_dimensions") match { + case Some(JsArray(x)) if x.size == 2 => + val z = x.map(_.as[JsNumber].value.intValue) + (z(0), z(1)) + case None => return None + } + val bumpLocations: Seq[Seq[String]] = json.get("bump_locations") match { + case Some(JsArray(array)) => + array.collect { case JsArray(a2) => a2.map(_.toString).toSeq }.toSeq + case _ => return None + } + // Can't have dimensions and locations which don't match + if (bumpLocations.size != bumpDimensions._1) return None + if (bumpLocations.collect { case x if x.size != bumpDimensions._2 => x }.nonEmpty) return None + + Some(FlipChipMacro(name, bumpDimensions, bumpLocations)) + } +} diff --git a/src/main/scala/mdf/macrolib/IOMacro.scala b/src/main/scala/mdf/macrolib/IOMacro.scala new file mode 100644 index 00000000..3f8ead8c --- /dev/null +++ b/src/main/scala/mdf/macrolib/IOMacro.scala @@ -0,0 +1,147 @@ +package mdf.macrolib + +import play.api.libs.json._ +import scala.collection.mutable.ListBuffer +import scala.language.implicitConversions + +sealed abstract class PortType { def toJSON(): JsString = JsString(toString) } +case object Digital extends PortType { override def toString: String = "digital" } +case object Analog extends PortType { override def toString: String = "analog" } +case object Power extends PortType { override def toString: String = "power" } +case object Ground extends PortType { override def toString: String = "ground" } +case object NoConnect extends PortType { override def toString: String = "NC" } + +sealed abstract class Direction { def toJSON(): JsString = JsString(toString) } +case object Input extends Direction { override def toString: String = "input" } +case object Output extends Direction { override def toString: String = "output" } +case object InOut extends Direction { override def toString: String = "inout" } + +sealed abstract class Termination { def toJSON(): JsValue } +case object CMOS extends Termination { override def toJSON(): JsString = JsString("CMOS") } +case class Resistive(ohms: Int) extends Termination { override def toJSON(): JsNumber = JsNumber(ohms) } + +sealed abstract class TerminationType { def toJSON(): JsString } +case object Single extends TerminationType { override def toJSON(): JsString = JsString("single") } +case object Differential extends TerminationType { override def toJSON(): JsString = JsString("differential") } + +// IO macro +case class IOMacro( + name: String, + tpe: PortType, + direction: Option[Direction] = None, + termination: Option[Termination] = None, + terminationType: Option[TerminationType] = None, + terminationReference: Option[String] = None, + matching: Seq[String] = Seq.empty[String], + bbname: Option[String] = None) + extends Macro { + override def toJSON(): JsObject = { + + val output = new ListBuffer[(String, JsValue)]() + output.appendAll( + Seq( + "name" -> Json.toJson(name), + "type" -> tpe.toJSON() + ) + ) + if (direction.isDefined) output.append("direction" -> direction.get.toJSON) + if (termination.isDefined) output.append("termination" -> termination.get.toJSON) + if (terminationType.isDefined) output.append("terminationType" -> terminationType.get.toJSON) + if (terminationReference.isDefined) output.append("terminationReference" -> JsString(terminationReference.get)) + if (matching.nonEmpty) output.append("match" -> JsArray(matching.map(JsString))) + if (bbname.nonEmpty) output.append("blackBox" -> JsString(bbname.get)) + + JsObject(output) + } + + override def typeStr = "iomacro" +} +object IOMacro { + def parseJSON(json: Map[String, JsValue]): Option[IOMacro] = { + val name: String = json.get("name") match { + case Some(x: JsString) => x.as[String] + case _ => return None + } + val tpe: PortType = json.get("type") match { + case Some(JsString("power")) => Power + case Some(JsString("ground")) => Ground + case Some(JsString("digital")) => Digital + case Some(JsString("analog")) => Analog + case Some(JsString("NC")) => NoConnect + case _ => return None + } + val direction: Option[Direction] = json.get("direction") match { + case Some(JsString("input")) => Some(Input) + case Some(JsString("output")) => Some(Output) + case Some(JsString("inout")) => Some(InOut) + case _ => None + } + val termination: Option[Termination] = json.get("termination") match { + case Some(JsNumber(x)) => Some(Resistive(x.toInt)) + case Some(JsString("CMOS")) => Some(CMOS) + case _ => None + } + val terminationType: Option[TerminationType] = json.get("terminationType") match { + case Some(JsString("differential")) => Some(Differential) + case Some(JsString("single")) => Some(Single) + case _ => None + } + val terminationRef: Option[String] = json.get("terminationReference") match { + case Some(JsString(x)) => Some(x) + case _ if terminationType.isDefined => return None + case _ => None + } + val matching: Seq[String] = json.get("match") match { + case Some(JsArray(array)) => array.map(_.as[JsString].value).toList + case _ => Seq.empty[String] + } + val bbname: Option[String] = json.get("blackBox") match { + case Some(JsString(module)) => Some(module) + case Some(_) => return None + case _ => None + } + Some(IOMacro(name, tpe, direction, termination, terminationType, terminationRef, matching, bbname)) + } +} + +case class IOProperties(name: String, top: String, ios: Seq[IOMacro]) extends Macro { + override def toJSON(): JsObject = { + val output = new ListBuffer[(String, JsValue)]() + output.appendAll( + Seq( + "name" -> Json.toJson(name), + "top" -> Json.toJson(top), + "type" -> Json.toJson(typeStr), + "ios" -> JsArray(ios.map(_.toJSON)) + ) + ) + JsObject(output) + } + + override def typeStr = "io_properties" + +} + +object IOProperties { + def parseJSON(json: Map[String, JsValue]): Option[IOProperties] = { + val name: String = json.get("name") match { + case Some(x: JsString) => x.as[String] + case _ => return None + } + val top: String = json.get("top") match { + case Some(x: JsString) => x.as[String] + case _ => return None + } + val ios: Seq[IOMacro] = json.get("ios") match { + case Some(x: JsArray) => + x.as[List[Map[String, JsValue]]].map { a => + val b = IOMacro.parseJSON(a); + if (b == None) { + return None + } else b.get + } + case _ => List() + } + Some(IOProperties(name, top, ios)) + } +} diff --git a/src/main/scala/mdf/macrolib/MacroLib.scala b/src/main/scala/mdf/macrolib/MacroLib.scala new file mode 100644 index 00000000..569c4dac --- /dev/null +++ b/src/main/scala/mdf/macrolib/MacroLib.scala @@ -0,0 +1,19 @@ +package mdf.macrolib + +import play.api.libs.json._ +import scala.collection.mutable.ListBuffer +import scala.language.implicitConversions + +// TODO: decide if we should always silently absorb errors + +// See macro_format.yml for the format description. + +// "Base class" for macros +abstract class Macro { + def name: String + + // Type of macro is determined by subclass + def typeStr: String + + def toJSON(): JsObject +} diff --git a/src/main/scala/mdf/macrolib/SRAM.scala b/src/main/scala/mdf/macrolib/SRAM.scala new file mode 100644 index 00000000..ea51b049 --- /dev/null +++ b/src/main/scala/mdf/macrolib/SRAM.scala @@ -0,0 +1,444 @@ +package mdf.macrolib + +import play.api.libs.json._ +import scala.collection.mutable.ListBuffer +import scala.language.implicitConversions + +// SRAM macro +case class SRAMMacro( + name: String, + width: Int, + depth: BigInt, + family: String, + ports: Seq[MacroPort], + vt: String = "", + mux: Int = 1, + extraPorts: Seq[MacroExtraPort] = List()) + extends Macro { + override def toJSON(): JsObject = { + val output = new ListBuffer[(String, JsValue)]() + output.appendAll( + Seq( + "type" -> JsString("sram"), + "name" -> Json.toJson(name), + "width" -> Json.toJson(width), + "depth" -> Json.toJson(depth.toString), + "mux" -> Json.toJson(mux), + "mask" -> Json.toJson(ports.exists(p => p.maskPort.isDefined)), + "ports" -> JsArray(ports.map { _.toJSON }) + ) + ) + if (family != "") { + output.appendAll(Seq("family" -> Json.toJson(family))) + } + if (vt != "") { + output.appendAll(Seq("vt" -> Json.toJson(vt))) + } + if (extraPorts.length > 0) { + output.appendAll(Seq("extra ports" -> JsArray(extraPorts.map { _.toJSON }))) + } + + JsObject(output) + } + + override def typeStr = "sram" +} +object SRAMMacro { + def parseJSON(json: Map[String, JsValue]): Option[SRAMMacro] = { + val name: String = json.get("name") match { + case Some(x: JsString) => x.as[String] + case _ => return None + } + val width: Int = json.get("width") match { + case Some(x: JsNumber) => x.value.intValue + case _ => return None + } + val depth: BigInt = json.get("depth") match { + case Some(x: JsString) => + try { BigInt(x.as[String]) } + catch { case _: Throwable => return None } + case _ => return None + } + val family: String = json.get("family") match { + case Some(x: JsString) => x.as[String] + case _ => "" // optional + } + val vt: String = json.get("vt") match { + case Some(x: JsString) => x.as[String] + case _ => "" // optional + } + val mux: Int = json.get("mux") match { + case Some(x: JsNumber) => x.value.intValue + case _ => 1 // default + } + val ports: Seq[MacroPort] = json.get("ports") match { + case Some(x: JsArray) => + x.as[List[Map[String, JsValue]]].map { a => + val b = MacroPort.parseJSON(a, width, depth); + if (b == None) { + return None + } else b.get + } + case _ => List() + } + if (ports.length == 0) { + // Can't have portless memories. + return None + } + val extraPorts: Seq[MacroExtraPort] = json.get("extra ports") match { + case Some(x: JsArray) => + x.as[List[Map[String, JsValue]]].map { a => + val b = MacroExtraPort.parseJSON(a); + if (b == None) { + return None + } else b.get + } + case _ => List() + } + Some(SRAMMacro(name, width, depth, family, ports, vt, mux, extraPorts)) + } +} + +// SRAM compiler +case class SRAMGroup( + name: Seq[String], + family: String, + vt: Seq[String], + mux: Int, + depth: Range, + width: Range, + ports: Seq[MacroPort], + extraPorts: Seq[MacroExtraPort] = List()) { + def toJSON: JsObject = { + val output = new ListBuffer[(String, JsValue)]() + output.appendAll( + Seq( + "name" -> JsArray(name.map(Json.toJson(_))), + "vt" -> JsArray(vt.map(Json.toJson(_))), + "mux" -> Json.toJson(mux), + "depth" -> JsArray(Seq(depth.start, depth.end, depth.step).map { x => Json.toJson(x) }), + "width" -> JsArray(Seq(width.start, width.end, width.step).map { x => Json.toJson(x) }), + "ports" -> JsArray(ports.map { _.toJSON }) + ) + ) + if (family != "") { + output.appendAll(Seq("family" -> Json.toJson(family))) + } + if (extraPorts.length > 0) { + output.appendAll(Seq("extra ports" -> JsArray(extraPorts.map { _.toJSON }))) + } + JsObject(output) + } +} +object SRAMGroup { + def parseJSON(json: Map[String, JsValue]): Option[SRAMGroup] = { + val family: String = json.get("family") match { + case Some(x: JsString) => x.as[String] + case _ => "" // optional + } + val name: Seq[String] = json.get("name") match { + case Some(x: JsArray) => x.as[List[JsString]].map(_.as[String]) + case _ => return None + } + val vt: Seq[String] = json.get("vt") match { + case Some(x: JsArray) => x.as[List[JsString]].map(_.as[String]) + case _ => return None + } + val mux: Int = json.get("mux") match { + case Some(x: JsNumber) => x.value.intValue + case _ => return None + } + val depth: Range = json.get("depth") match { + case Some(x: JsArray) => + val seq = x.as[List[JsNumber]].map(_.value.intValue) + Range.inclusive(seq(0), seq(1), seq(2)) + case _ => return None + } + val width: Range = json.get("width") match { + case Some(x: JsArray) => + val seq = x.as[List[JsNumber]].map(_.value.intValue) + Range.inclusive(seq(0), seq(1), seq(2)) + case _ => return None + } + val ports: Seq[MacroPort] = json.get("ports") match { + case Some(x: JsArray) => + x.as[List[Map[String, JsValue]]].map { a => + { + val b = MacroPort.parseJSON(a, None, None); + if (b == None) { + return None + } else b.get + } + } + case _ => List() + } + if (ports.length == 0) { + // Can't have portless memories. + return None + } + val extraPorts: Seq[MacroExtraPort] = json.get("extra ports") match { + case Some(x: JsArray) => + x.as[List[Map[String, JsValue]]].map { a => + { + val b = MacroExtraPort.parseJSON(a); + if (b == None) { + return None + } else b.get + } + } + case _ => List() + } + Some(SRAMGroup(name, family, vt, mux, depth, width, ports, extraPorts)) + } +} + +case class SRAMCompiler( + name: String, + groups: Seq[SRAMGroup]) + extends Macro { + override def toJSON(): JsObject = { + val output = new ListBuffer[(String, JsValue)]() + output.appendAll( + Seq( + "type" -> Json.toJson("sramcompiler"), + "name" -> Json.toJson(name), + "groups" -> JsArray(groups.map { _.toJSON }) + ) + ) + + JsObject(output) + } + + override def typeStr = "sramcompiler" +} +object SRAMCompiler { + def parseJSON(json: Map[String, JsValue]): Option[SRAMCompiler] = { + val name: String = json.get("name") match { + case Some(x: JsString) => x.as[String] + case _ => return None + } + val groups: Seq[SRAMGroup] = json.get("groups") match { + case Some(x: JsArray) => + x.as[List[Map[String, JsValue]]].map { a => + { + val b = SRAMGroup.parseJSON(a); + if (b == None) { return None } + else b.get + } + } + case _ => List() + } + if (groups.length == 0) { + // Can't have portless memories. + return None + } + Some(SRAMCompiler(name, groups)) + } +} + +// Type of extra port +sealed abstract class MacroExtraPortType +case object Constant extends MacroExtraPortType +object MacroExtraPortType { + implicit def toMacroExtraPortType(s: Any): Option[MacroExtraPortType] = { + s match { + case "constant" => Some(Constant) + case _ => None + } + } + + implicit def toString(t: MacroExtraPortType): String = { + t match { + case Constant => "constant" + case _ => "" + } + } +} + +// Extra port in SRAM +case class MacroExtraPort( + name: String, + width: Int, + portType: MacroExtraPortType, + value: BigInt) { + def toJSON(): JsObject = { + JsObject( + Seq( + "name" -> Json.toJson(name), + "width" -> Json.toJson(width), + "type" -> JsString(MacroExtraPortType.toString(portType)), + "value" -> JsNumber(BigDecimal(value)) + ) + ) + } +} +object MacroExtraPort { + def parseJSON(json: Map[String, JsValue]): Option[MacroExtraPort] = { + val name = json.get("name") match { + case Some(x: JsString) => x.value + case _ => return None + } + val width = json.get("width") match { + case Some(x: JsNumber) => x.value.intValue + case _ => return None + } + val portType: MacroExtraPortType = json.get("type") match { + case Some(x: JsString) => + MacroExtraPortType.toMacroExtraPortType(x.value) match { + case Some(t: MacroExtraPortType) => t + case _ => return None + } + case _ => return None + } + val value = json.get("value") match { + case Some(x: JsNumber) => x.value.toBigInt + case _ => return None + } + Some(MacroExtraPort(name, width, portType, value)) + } +} + +// A named port that also has polarity. +case class PolarizedPort(name: String, polarity: PortPolarity) { + def toSeqMap(prefix: String): Seq[Tuple2[String, JsValue]] = { + Seq( + prefix + " port name" -> Json.toJson(name), + prefix + " port polarity" -> JsString(polarity) + ) + } +} +object PolarizedPort { + // Parse a pair of " port name" and " port polarity" keys into a + // polarized port definition. + def parseJSON(json: Map[String, JsValue], prefix: String): Option[PolarizedPort] = { + val name = json.get(prefix + " port name") match { + case Some(x: JsString) => Some(x.value) + case _ => None + } + val polarity: Option[PortPolarity] = json.get(prefix + " port polarity") match { + case Some(x: JsString) => Some(x.value) + case _ => None + } + + (name, polarity) match { + case (Some(n: String), Some(p: PortPolarity)) => Some(PolarizedPort(n, p)) + case _ => None + } + } +} + +// A SRAM memory port +case class MacroPort( + address: PolarizedPort, + clock: Option[PolarizedPort] = None, + writeEnable: Option[PolarizedPort] = None, + readEnable: Option[PolarizedPort] = None, + chipEnable: Option[PolarizedPort] = None, + output: Option[PolarizedPort] = None, + input: Option[PolarizedPort] = None, + maskPort: Option[PolarizedPort] = None, + maskGran: Option[Int] = None, + // For internal use only; these aren't port-specific. + width: Option[Int], + depth: Option[BigInt]) { + def effectiveMaskGran = maskGran.getOrElse(width.get) + + def toJSON(): JsObject = { + val keys: Seq[Tuple2[String, Option[Any]]] = Seq( + "address" -> Some(address), + "clock" -> clock, + "write enable" -> writeEnable, + "read enable" -> readEnable, + "chip enable" -> chipEnable, + "output" -> output, + "input" -> input, + "mask" -> maskPort, + "mask granularity" -> maskGran + ) + JsObject(keys.flatMap(k => { + val (key, value) = k + value match { + case Some(x: Int) => Seq(key -> JsNumber(x)) + case Some(x: PolarizedPort) => x.toSeqMap(key) + case _ => List() + } + })) + } + + // Check that all port names are unique. + private val polarizedPorts = + List(Some(address), clock, writeEnable, readEnable, chipEnable, output, input, maskPort).flatten + assert(polarizedPorts.distinct.size == polarizedPorts.size, "All port names must be unique") +} +object MacroPort { + def parseJSON(json: Map[String, JsValue]): Option[MacroPort] = parseJSON(json, None, None) + def parseJSON(json: Map[String, JsValue], width: Int, depth: BigInt): Option[MacroPort] = + parseJSON(json, Some(width), Some(depth)) + def parseJSON(json: Map[String, JsValue], width: Option[Int], depth: Option[BigInt]): Option[MacroPort] = { + val address = PolarizedPort.parseJSON(json, "address") + if (address == None) { + return None + } + + val clock = PolarizedPort.parseJSON(json, "clock") + // TODO: validate based on family (e.g. 1rw must have a write enable, etc) + val writeEnable = PolarizedPort.parseJSON(json, "write enable") + val readEnable = PolarizedPort.parseJSON(json, "read enable") + val chipEnable = PolarizedPort.parseJSON(json, "chip enable") + + val output = PolarizedPort.parseJSON(json, "output") + val input = PolarizedPort.parseJSON(json, "input") + + val maskPort = PolarizedPort.parseJSON(json, "mask") + val maskGran: Option[Int] = json.get("mask granularity") match { + case Some(x: JsNumber) => Some(x.value.intValue) + case _ => None + } + + if (maskPort.isDefined != maskGran.isDefined) { + return None + } + + Some( + MacroPort( + width = width, + depth = depth, + address = address.get, + clock = clock, + writeEnable = writeEnable, + readEnable = readEnable, + chipEnable = chipEnable, + output = output, + input = input, + maskPort = maskPort, + maskGran = maskGran + ) + ) + } +} + +// Port polarity +trait PortPolarity +case object ActiveLow extends PortPolarity +case object ActiveHigh extends PortPolarity +case object NegativeEdge extends PortPolarity +case object PositiveEdge extends PortPolarity +object PortPolarity { + implicit def toPortPolarity(s: String): PortPolarity = (s: @unchecked) match { + case "active low" => ActiveLow + case "active high" => ActiveHigh + case "negative edge" => NegativeEdge + case "positive edge" => PositiveEdge + } + implicit def toPortPolarity(s: Option[String]): Option[PortPolarity] = + s.map(toPortPolarity) + + implicit def toString(p: PortPolarity): String = { + p match { + case ActiveLow => "active low" + case ActiveHigh => "active high" + case NegativeEdge => "negative edge" + case PositiveEdge => "positive edge" + } + } +} diff --git a/src/main/scala/mdf/macrolib/Utils.scala b/src/main/scala/mdf/macrolib/Utils.scala new file mode 100644 index 00000000..547f910c --- /dev/null +++ b/src/main/scala/mdf/macrolib/Utils.scala @@ -0,0 +1,96 @@ +package mdf.macrolib + +import play.api.libs.json._ + +import java.io.FileNotFoundException +import scala.collection.mutable.ListBuffer +import scala.language.implicitConversions + +object Utils { + // Read a MDF file from a String. + def readMDFFromString(str: String): Option[Seq[Macro]] = { + Json.parse(str) match { + // Make sure that the document is a list. + case arr: JsArray => { + val result: List[Option[Macro]] = arr.as[List[Map[String, JsValue]]].map { obj => + // Check the type of object. + val objTypeStr: String = obj.get("type") match { + case Some(x: JsString) => x.as[String] + case _ => return None // error, no type found + } + objTypeStr match { + case "filler cell" | "metal filler cell" => FillerMacroBase.parseJSON(obj) + case "sram" => SRAMMacro.parseJSON(obj) + case "sramcompiler" => SRAMCompiler.parseJSON(obj) + case "io_properties" => IOProperties.parseJSON(obj) + case "flipchip" => FlipChipMacro.parseJSON(obj) + case _ => None // skip unknown macro types + } + } + // Remove all the Nones and convert back to Seq[Macro] + Some(result.filter { x => x != None }.map { x => x.get }) + } + case _ => None + } + } + + // Read a MDF file from a path. + def readMDFFromPath(path: Option[String]): Option[Seq[Macro]] = { + path match { + case None => None + // Read file into string and parse + case Some(p) => + try { + Utils.readMDFFromString(scala.io.Source.fromFile(p).mkString) + } catch { + case f: FileNotFoundException => + println(s"FILE NOT FOUND $p in dir ${os.pwd}") + throw f + } + } + } + + // Write a MDF file to a String. + def writeMDFToString(s: Seq[Macro]): String = { + Json.prettyPrint(JsArray(s.map(_.toJSON))) + } + + // Write a MDF file from a path. + // Returns true upon success. + def writeMDFToPath(path: Option[String], s: Seq[Macro]): Boolean = { + path match { + case None => false + // Read file into string and parse + case Some(p: String) => { + import java.io._ + val pw = new PrintWriter(new File(p)) + pw.write(writeMDFToString(s)) + val error = pw.checkError + pw.close() + !error + } + } + } + + // Write a macro file to a String. + def writeMacroToString(s: Macro): String = { + Json.prettyPrint(s.toJSON) + } + + // Write a Macro file from a path. + // Returns true upon success. + def writeMacroToPath(path: Option[String], s: Macro): Boolean = { + path match { + case None => false + // Read file into string and parse + case Some(p: String) => { + import java.io._ + val pw = new PrintWriter(new File(p)) + pw.write(writeMacroToString(s)) + val error = pw.checkError + pw.close() + !error + } + } + } +} diff --git a/src/test/resources/PadAnnotationVerilogPart.v b/src/test/resources/PadAnnotationVerilogPart.v new file mode 100644 index 00000000..9e4b257f --- /dev/null +++ b/src/test/resources/PadAnnotationVerilogPart.v @@ -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 \ No newline at end of file diff --git a/src/test/resources/bumps.json b/src/test/resources/bumps.json new file mode 100644 index 00000000..21b93381 --- /dev/null +++ b/src/test/resources/bumps.json @@ -0,0 +1,41 @@ +[ + { + "name" : "example", + "type" : "flipchip", + "bump_dimensions" : [27,27], + "bump_locations" : [ + ["-", "GND", "VDDC0_SEL[0]", "VDDC0_SEL[1]", "VDDC1_SEL[0]", "VDDC1_SEL[1]", "VDDC2_SEL[0]", "VDDC2_SEL[1]", "VDDC3_SEL[0]", "VDDC3_SEL[1]", "VDDC0_EN", "VDDC1_EN", "VDDC2_EN", "VDDC3_EN", "CCLK0", "CCLK1", "CCLK2", "RESET", "BOOT", "I2C_SDA", "I2C_SCL", "SPI_SCLK", "SPI_MOSI", "SPI_MISO", "SPI_SS_L", "GND", "-"], + [ "GND", "", "", "", "GND", "GND","GPIO[1]", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8","UART_RX","UART_TX", "GND", "GND", "GND", "GND", "", "", "", "GND"], + + ["TXP0", "VDDA", "VDDA", "GND", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "GND", "VDDA", "VDDA", "TXP4"], + ["TXN0", "VDDA", "VDDA", "GND", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "GND", "VDDA", "VDDA", "TXN4"], + [ "GND", "", "", "", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "", "", "", "GND"], + ["RXP0", "VDDA", "VDDA", "GND", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "GND", "VDDA", "VDDA", "RXP4"], + ["RXN0", "VDDA", "VDDA", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "VDDA", "VDDA", "RXN4"], + [ "GND", "", "", "", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "", "", "", "GND"], + + ["TXP1", "VDDA", "VDDA", "GND", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "GND", "VDDA", "VDDA", "TXP5"], + ["TXN1", "VDDA", "VDDA", "GND", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "GND", "VDDA", "VDDA", "TXN5"], + [ "GND", "", "", "", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "", "", "", "GND"], + ["RXP1", "VDDA", "VDDA", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "VDDA", "VDDA", "RXP5"], + ["RXN1", "VDDA", "VDDA", "GND", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "GND", "GND", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "GND", "VDDA", "VDDA", "RXN5"], + [ "GND", "", "", "", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "VDDC1", "GND", "GND", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC0", "VDDC0", "VDDC0", "VDDC0", "", "", "", "GND"], + + ["TXP2", "VDDA", "VDDA", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "VDDA", "VDDA", "TXP6"], + ["TXN2", "VDDA", "VDDA", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "VDDA", "VDDA", "TXN6"], + [ "GND", "", "", "", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "", "", "", "GND"], + ["RXP2", "VDDA", "VDDA", "GND", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC2", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "VDDC3", "GND", "VDDA", "VDDA", "RXP6"], + ["RXN2", "VDDA", "VDDA", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "VDDA", "VDDA", "RXN6"], + [ "GND", "", "", "", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "GND", "", "", "", "GND"], + + ["TXP3", "VDDA", "VDDA", "GND", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "GND", "VDDA", "VDDA", "TXP7"], + ["TXN3", "VDDA", "VDDA", "GND", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "VDD0V8", "GND", "VDDA", "VDDA", "TXN7"], + [ "GND", "", "", "", "-", "SERIAL_IN_READY", "-", "-", "SERIAL_IN_VALID", "-", "-", "-", "-", "", "-", "SERIAL_OUT_VALID", "-", "-", "SERIAL_OUT_READY", "-", "-", "GPIO[0]", "-", "", "", "", "GND"], + ["RXP3", "VDDA", "VDDA", "GND", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "GND", "VDDA", "VDDA", "RXP7"], + ["RXN3", "VDDA", "VDDA", "GND", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "GND", "VDDA", "VDDA", "RXN7"], + + [ "GND", "", "", "", "GND", "GND", "GND", "GND", "GND", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "VDD1V8", "GND", "GND", "GND", "GND", "GND", "", "", "", "GND"], + ["-", "GND", "REFCLK0P", "REFCLK0N", "GND", "SERIAL_OUT[0]", "SERIAL_OUT[1]", "SERIAL_OUT[2]", "SERIAL_OUT[3]", "SERIAL_IN[0]", "SERIAL_IN[1]", "SERIAL_IN[2]", "SERIAL_IN[3]", "JTAG_TMS", "JTAG_TCK", "JTAG_TDO", "JTAG_TDI", "CLKSEL", "PLLCLK_OUT", "GND", "PLLREFCLKP", "PLLREFCLKN", "GND", "REFCLK1P", "REFCLK1N", "GND", "-"] + ] + } +] diff --git a/src/test/resources/io_properties.json b/src/test/resources/io_properties.json new file mode 100644 index 00000000..93b945a3 --- /dev/null +++ b/src/test/resources/io_properties.json @@ -0,0 +1,663 @@ +[ + { + "name": "My IOs", + "type": "io_properties", + "top": "EAGLE", + "ios": [ + { + "name": "GND", + "type": "ground" + }, + { + "name": "VDD0V8", + "type": "power" + }, + { + "name": "VDD1V8", + "type": "power" + }, + { + "name": "VDDC0", + "type": "power" + }, + { + "name": "VDDC1", + "type": "power" + }, + { + "name": "VDDC2", + "type": "power" + }, + { + "name": "VDDC3", + "type": "power" + }, + { + "name": "VDDA", + "type": "power" + }, + { + "name": "VDDC0_SEL[1:0]", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDC1_SEL[1:0]", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDC2_SEL[1:0]", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDC3_SEL[1:0]", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDDC0_EN", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDDC1_EN", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDDC2_EN", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "VDDDC3_EN", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "CCLK0", + "type": "digital", + "direction": "input", + "termination": 50, + "termination_type": "single", + "termination_reference": "GND" + }, + { + "name": "CCLK1", + "type": "digital", + "direction": "input", + "termination": 50, + "termination_type": "single", + "termination_reference": "GND" + }, + { + "name": "CCLK2", + "type": "digital", + "direction": "input", + "termination": 50, + "termination_type": "single", + "termination_reference": "GND" + }, + { + "name": "RESET", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "BOOT", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "I2C_SDA", + "type": "digital", + "direction": "inout", + "termination": "open-drain" + }, + { + "name": "I2C_SCL", + "type": "digital", + "direction": "inout", + "termination": "open-drain" + }, + { + "name": "SPI_SCLK", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "SPI_MOSI", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "SPI_MISO", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "SPI_SS_L", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "GPIO[1:0]", + "type": "digital", + "direction": "inout", + "termination": "CMOS" + }, + { + "name": "UART_RX", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "UART_TX", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "SERIAL_IN_READY", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "SERIAL_IN_VALID", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "SERIAL_OUT_READY", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "SERIAL_OUT_VALID", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "SERIAL_OUT[3:0]", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "SERIAL_IN[3:0]", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "REFCLK0P", + "type": "analog", + "direction": "input", + "match": [ + "REFCLK0N" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "REFCLK0N", + "type": "analog", + "direction": "input", + "match": [ + "REFCLK0P" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "REFCLK1N", + "type": "analog", + "direction": "input", + "match": [ + "REFCLK1P" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "REFCLK1P", + "type": "analog", + "direction": "input", + "match": [ + "REFCLK1N" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "PLLREFCLKP", + "type": "analog", + "direction": "input", + "match": [ + "PLLREFCLKP" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "PLLREFCLKN", + "type": "analog", + "direction": "input", + "match": [ + "PLLREFCLKP" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "JTAG_TMS", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "JTAG_TCK", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "JTAG_TDI", + "type": "digital", + "direction": "input", + "termination": "CMOS" + }, + { + "name": "JTAG_TDO", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "PLLCLK_OUT", + "type": "digital", + "direction": "output", + "termination": "CMOS" + }, + { + "name": "TXP0", + "type": "analog", + "direction": "output", + "match": [ + "TXN0" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN0", + "type": "analog", + "direction": "output", + "match": [ + "TXP0" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP0", + "type": "analog", + "direction": "output", + "match": [ + "RXN0" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN1", + "type": "analog", + "direction": "input", + "match": [ + "RXP1" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP1", + "type": "analog", + "direction": "output", + "match": [ + "TXN1" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN1", + "type": "analog", + "direction": "output", + "match": [ + "TXP1" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP1", + "type": "analog", + "direction": "output", + "match": [ + "RXN1" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN1", + "type": "analog", + "direction": "input", + "match": [ + "RXP1" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP2", + "type": "analog", + "direction": "output", + "match": [ + "TXN2" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN2", + "type": "analog", + "direction": "output", + "match": [ + "TXP2" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP2", + "type": "analog", + "direction": "output", + "match": [ + "RXN2" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN2", + "type": "analog", + "direction": "input", + "match": [ + "RXP2" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP3", + "type": "analog", + "direction": "output", + "match": [ + "TXN3" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN3", + "type": "analog", + "direction": "output", + "match": [ + "TXP3" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP3", + "type": "analog", + "direction": "output", + "match": [ + "RXN3" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN3", + "type": "analog", + "direction": "input", + "match": [ + "RXP3" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP4", + "type": "analog", + "direction": "output", + "match": [ + "TXN4" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN4", + "type": "analog", + "direction": "output", + "match": [ + "TXP4" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP4", + "type": "analog", + "direction": "output", + "match": [ + "RXN4" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN4", + "type": "analog", + "direction": "input", + "match": [ + "RXP4" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP5", + "type": "analog", + "direction": "output", + "match": [ + "TXN5" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN5", + "type": "analog", + "direction": "output", + "match": [ + "TXP5" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP5", + "type": "analog", + "direction": "output", + "match": [ + "RXN5" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN5", + "type": "analog", + "direction": "input", + "match": [ + "RXP5" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP6", + "type": "analog", + "direction": "output", + "match": [ + "TXN6" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN6", + "type": "analog", + "direction": "output", + "match": [ + "TXP6" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP6", + "type": "analog", + "direction": "output", + "match": [ + "RXN6" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN6", + "type": "analog", + "direction": "input", + "match": [ + "RXP6" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXP7", + "type": "analog", + "direction": "output", + "match": [ + "TXN7" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "TXN7", + "type": "analog", + "direction": "output", + "match": [ + "TXP7" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXP7", + "type": "analog", + "direction": "output", + "match": [ + "RXN7" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + }, + { + "name": "RXN7", + "type": "analog", + "direction": "input", + "match": [ + "RXP7" + ], + "termination": 100, + "termination_type": "differential", + "termination_reference": "GND" + } + ] + } +] diff --git a/src/test/resources/lib-BOOMTest.json b/src/test/resources/lib-BOOMTest.json new file mode 100644 index 00000000..1d2e5f69 --- /dev/null +++ b/src/test/resources/lib-BOOMTest.json @@ -0,0 +1,1165 @@ +[ + { + "family": "1rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_1024x8", + "type": "sram", + "depth": "1024" + }, + { + "family": "1rw", + "width": 46, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_128x46", + "type": "sram", + "depth": "128" + }, + { + "family": "1rw", + "width": 48, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_128x48", + "type": "sram", + "depth": "128" + }, + { + "family": "1rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_128x8", + "type": "sram", + "depth": "128" + }, + { + "family": "1rw", + "width": 128, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_256x128", + "type": "sram", + "depth": "256" + }, + { + "family": "1rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_256x32", + "type": "sram", + "depth": "256" + }, + { + "family": "1rw", + "width": 46, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_256x46", + "type": "sram", + "depth": "256" + }, + { + "family": "1rw", + "width": 48, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_256x48", + "type": "sram", + "depth": "256" + }, + { + "family": "1rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_256x8", + "type": "sram", + "depth": "256" + }, + { + "family": "1rw", + "width": 50, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_32x50", + "type": "sram", + "depth": "32" + }, + { + "family": "1rw", + "width": 128, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_512x128", + "type": "sram", + "depth": "512" + }, + { + "family": "1rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_512x32", + "type": "sram", + "depth": "512" + }, + { + "family": "1rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_512x8", + "type": "sram", + "depth": "512" + }, + { + "family": "1rw", + "width": 128, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_64x128", + "type": "sram", + "depth": "64" + }, + { + "family": "1rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_64x32", + "type": "sram", + "depth": "64" + }, + { + "family": "1rw", + "width": 34, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_64x34", + "type": "sram", + "depth": "64" + }, + { + "family": "1rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB", + "write enable port name": "WEB", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE", + "chip enable port polarity": "active low", + "address port name": "A", + "read enable port name": "OEB", + "input port name": "I", + "input port polarity": "active high" + } + ], + "name": "my_sram_1rw_64x8", + "type": "sram", + "depth": "64" + }, + { + "family": "2rw", + "width": 16, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_128x16", + "type": "sram", + "depth": "128" + }, + { + "family": "2rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_128x32", + "type": "sram", + "depth": "128" + }, + { + "family": "2rw", + "width": 4, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_128x4", + "type": "sram", + "depth": "128" + }, + { + "family": "2rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_128x8", + "type": "sram", + "depth": "128" + }, + { + "family": "2rw", + "width": 16, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_16x16", + "type": "sram", + "depth": "16" + }, + { + "family": "2rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_16x32", + "type": "sram", + "depth": "16" + }, + { + "family": "2rw", + "width": 4, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_16x4", + "type": "sram", + "depth": "16" + }, + { + "family": "2rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_16x8", + "type": "sram", + "depth": "16" + }, + { + "family": "2rw", + "width": 16, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_32x16", + "type": "sram", + "depth": "32" + }, + { + "family": "2rw", + "width": 22, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_32x22", + "type": "sram", + "depth": "32" + }, + { + "family": "2rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_32x32", + "type": "sram", + "depth": "32" + }, + { + "family": "2rw", + "width": 39, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_32x39", + "type": "sram", + "depth": "32" + }, + { + "family": "2rw", + "width": 4, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_32x4", + "type": "sram", + "depth": "32" + }, + { + "family": "2rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_32x8", + "type": "sram", + "depth": "32" + }, + { + "family": "2rw", + "width": 16, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_64x16", + "type": "sram", + "depth": "64" + }, + { + "family": "2rw", + "width": 32, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_64x32", + "type": "sram", + "depth": "64" + }, + { + "family": "2rw", + "width": 4, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_64x4", + "type": "sram", + "depth": "64" + }, + { + "family": "2rw", + "width": 8, + "ports": [ + { + "chip enable port name": "CSB1", + "write enable port name": "WEB1", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O1", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE1", + "chip enable port polarity": "active low", + "address port name": "A1", + "read enable port name": "OEB1", + "input port name": "I1", + "input port polarity": "active high" + }, + { + "chip enable port name": "CSB2", + "write enable port name": "WEB2", + "address port polarity": "active high", + "output port polarity": "active high", + "output port name": "O2", + "write enable port polarity": "active low", + "read enable port polarity": "active low", + "clock port polarity": "positive edge", + "clock port name": "CE2", + "chip enable port polarity": "active low", + "address port name": "A2", + "read enable port name": "OEB2", + "input port name": "I2", + "input port polarity": "active high" + } + ], + "name": "my_sram_2rw_64x8", + "type": "sram", + "depth": "64" + } +] diff --git a/src/test/resources/lib-MaskPortTest.json b/src/test/resources/lib-MaskPortTest.json new file mode 100644 index 00000000..784aeafb --- /dev/null +++ b/src/test/resources/lib-MaskPortTest.json @@ -0,0 +1,29 @@ +[ + { + "type": "sram", + "name": "fake_mem", + "width": 64, + "depth": "512", + "mux": 4, + "family": "1rw", + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "positive edge", + "write enable port name": "wen", + "write enable port polarity": "active high", + "read enable port name": "ren", + "read enable port polarity": "active high", + "output port name": "dataout", + "output port polarity": "active high", + "input port name": "datain", + "input port polarity": "active high", + "mask port name": "mport", + "mask port polarity": "active low", + "mask granularity": 1 + } + ] + } +] diff --git a/src/test/resources/lib-WriteEnableTest.json b/src/test/resources/lib-WriteEnableTest.json new file mode 100644 index 00000000..50acef41 --- /dev/null +++ b/src/test/resources/lib-WriteEnableTest.json @@ -0,0 +1,26 @@ +[ + { + "type": "sram", + "name": "fake_mem", + "width": 64, + "depth": "4096", + "mux": 4, + "family": "1rw", + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "positive edge", + "write enable port name": "wen", + "write enable port polarity": "active high", + "read enable port name": "ren", + "read enable port polarity": "active high", + "output port name": "dataout", + "output port polarity": "active high", + "input port name": "datain", + "input port polarity": "active high" + } + ] + } +] diff --git a/src/test/scala/barstools/macros/CostFunction.scala b/src/test/scala/barstools/macros/CostFunction.scala new file mode 100644 index 00000000..62ebfcdf --- /dev/null +++ b/src/test/scala/barstools/macros/CostFunction.scala @@ -0,0 +1,114 @@ +package barstools.macros + +import mdf.macrolib.SRAMMacro + +/** Tests to check that the cost function mechanism is working properly. */ + +/** A test metric that simply favours memories with smaller widths, to test that + * the metric is chosen properly. + */ +object TestMinWidthMetric extends CostMetric with CostMetricCompanion { + // Smaller width = lower cost = favoured + override def cost(mem: Macro, lib: Macro): Option[Double] = Some(lib.src.width) + + override def commandLineParams() = Map() + override def name() = "TestMinWidthMetric" + override def construct(m: Map[String, String]): CostMetric = TestMinWidthMetric +} + +/** Test that cost metric selection is working. */ +class SelectCostMetric extends MacroCompilerSpec with HasSRAMGenerator { + val mem = s"mem-SelectCostMetric.json" + val lib = s"lib-SelectCostMetric.json" + val v = s"SelectCostMetric.v" + + // Cost metrics must be registered for them to work with the command line. + CostMetric.registerCostMetric(TestMinWidthMetric) + + override val costMetric: Option[CostMetric] = Some(TestMinWidthMetric) + + val libSRAMs = Seq( + SRAMMacro( + name = "SRAM_WIDTH_128", + depth = BigInt(1024), + width = 128, + family = "1rw", + ports = Seq( + generateReadWritePort("", 128, BigInt(1024)) + ) + ), + SRAMMacro( + name = "SRAM_WIDTH_64", + depth = BigInt(1024), + width = 64, + family = "1rw", + ports = Seq( + generateReadWritePort("", 64, BigInt(1024)) + ) + ), + SRAMMacro( + name = "SRAM_WIDTH_32", + depth = BigInt(1024), + width = 32, + family = "1rw", + ports = Seq( + generateReadWritePort("", 32, BigInt(1024)) + ) + ) + ) + + val memSRAMs = Seq(generateSRAM("target_memory", "", 128, BigInt(1024))) + + writeToLib(lib, libSRAMs) + writeToMem(mem, memSRAMs) + + // Check that the min width SRAM was chosen, even though it is less efficient. + val output = + """ +circuit target_memory : + module target_memory : + input addr : UInt<10> + input clk : Clock + input din : UInt<128> + output dout : UInt<128> + input write_en : UInt<1> + + inst mem_0_0 of SRAM_WIDTH_32 + inst mem_0_1 of SRAM_WIDTH_32 + inst mem_0_2 of SRAM_WIDTH_32 + inst mem_0_3 of SRAM_WIDTH_32 + mem_0_0.clk <= clk + mem_0_0.addr <= addr + node dout_0_0 = bits(mem_0_0.dout, 31, 0) + mem_0_0.din <= bits(din, 31, 0) + mem_0_0.write_en <= and(and(and(write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_1.clk <= clk + mem_0_1.addr <= addr + node dout_0_1 = bits(mem_0_1.dout, 31, 0) + mem_0_1.din <= bits(din, 63, 32) + mem_0_1.write_en <= and(and(and(write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_2.clk <= clk + mem_0_2.addr <= addr + node dout_0_2 = bits(mem_0_2.dout, 31, 0) + mem_0_2.din <= bits(din, 95, 64) + mem_0_2.write_en <= and(and(and(write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_3.clk <= clk + mem_0_3.addr <= addr + node dout_0_3 = bits(mem_0_3.dout, 31, 0) + mem_0_3.din <= bits(din, 127, 96) + mem_0_3.write_en <= and(and(and(write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + node dout_0 = cat(dout_0_3, cat(dout_0_2, cat(dout_0_1, dout_0_0))) + dout <= mux(UInt<1>("h1"), dout_0, UInt<128>("h0")) + + extmodule SRAM_WIDTH_32 : + input addr : UInt<10> + input clk : Clock + input din : UInt<32> + output dout : UInt<32> + input write_en : UInt<1> + + defname = SRAM_WIDTH_32 +""" + + compileExecuteAndTest(mem, lib, v, output) +} diff --git a/src/test/scala/barstools/macros/Functional.scala b/src/test/scala/barstools/macros/Functional.scala new file mode 100644 index 00000000..ddc33477 --- /dev/null +++ b/src/test/scala/barstools/macros/Functional.scala @@ -0,0 +1,120 @@ +package barstools.macros + +// import firrtl.ir.Circuit +// import firrtl_interpreter.InterpretiveTester + +// // Functional tests on memory compiler outputs. + +// // Synchronous write and read back. +// class SynchronousReadAndWrite extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { +// override lazy val width = 12 +// override lazy val memDepth = BigInt(2048) +// override lazy val libDepth = BigInt(1024) + +// compile(mem, lib, v, synflops = true) +// val result: Circuit = execute(mem, lib, synflops = true) + +// it should "run with InterpretedTester" in { +// pending // Enable this when https://github.com/freechipsproject/firrtl-interpreter/pull/88 is snapshot-published + +// val addr1 = 0 +// val addr1val = 0xff +// val addr2 = 42 +// val addr2val = 0xf0 +// val addr3 = 1 << 10 +// val addr3val = 1 << 10 + +// val tester = new InterpretiveTester(result.serialize) +// //~ tester.setVerbose() + +// tester.poke("outer_write_en", 0) +// tester.step() + +// // Write addresses and read them. +// tester.poke("outer_addr", addr1) +// tester.poke("outer_din", addr1val) +// tester.poke("outer_write_en", 1) +// tester.step() +// tester.poke("outer_write_en", 0) +// tester.step() +// tester.poke("outer_addr", addr2) +// tester.poke("outer_din", addr2val) +// tester.poke("outer_write_en", 1) +// tester.step() +// tester.poke("outer_write_en", 0) +// tester.step() +// tester.poke("outer_addr", addr3) +// tester.poke("outer_din", addr3val) +// tester.poke("outer_write_en", 1) +// tester.step() +// tester.poke("outer_write_en", 0) +// tester.step() + +// tester.poke("outer_addr", addr1) +// tester.step() +// tester.expect("outer_dout", addr1val) + +// tester.poke("outer_addr", addr2) +// tester.step() +// tester.expect("outer_dout", addr2val) + +// tester.poke("outer_addr", addr3) +// tester.step() +// tester.expect("outer_dout", addr3val) +// } +// } + +// // Test to verify that the circuit doesn't read combinationally based on addr +// // between two submemories. +// class DontReadCombinationally extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { +// override lazy val width = 8 +// override lazy val memDepth = BigInt(2048) +// override lazy val libDepth = BigInt(1024) + +// compile(mem, lib, v, synflops = true) +// val result: Circuit = execute(mem, lib, synflops = true) + +// it should "run with InterpretedTester" in { +// pending // Enable this when https://github.com/freechipsproject/firrtl-interpreter/pull/88 is snapshot-published + +// val addr1 = 0 +// val addr1a = 1 +// val addr2 = 1 << 10 + +// val tester = new InterpretiveTester(result.serialize) +// //~ tester.setVerbose() + +// tester.poke("outer_write_en", 0) +// tester.step() + +// // Write two addresses, one in the lower submemory and the other in the +// // higher submemory. +// tester.poke("outer_addr", addr1) +// tester.poke("outer_din", 0x11) +// tester.poke("outer_write_en", 1) +// tester.step() +// tester.poke("outer_addr", addr1a) +// tester.poke("outer_din", 0x1a) +// tester.poke("outer_write_en", 1) +// tester.step() +// tester.poke("outer_addr", addr2) +// tester.poke("outer_din", 0xaa) +// tester.poke("outer_write_en", 1) +// tester.step() +// tester.poke("outer_write_en", 0) +// tester.poke("outer_addr", addr1) +// tester.step() + +// // Test that there is no combinational read. +// tester.poke("outer_addr", addr1) +// tester.expect("outer_dout", 0x11) +// tester.poke("outer_addr", addr1a) +// tester.expect("outer_dout", 0x11) +// tester.poke("outer_addr", addr2) +// tester.expect("outer_dout", 0x11) + +// // And upon step it should work again. +// tester.step() +// tester.expect("outer_addr", 0xaa) +// } +// } diff --git a/src/test/scala/barstools/macros/MacroCompilerSpec.scala b/src/test/scala/barstools/macros/MacroCompilerSpec.scala new file mode 100644 index 00000000..2cfcaed5 --- /dev/null +++ b/src/test/scala/barstools/macros/MacroCompilerSpec.scala @@ -0,0 +1,546 @@ +// See LICENSE for license details. + +package barstools.macros + +import firrtl.Parser.parse +import firrtl.ir.{Circuit, NoInfo} +import firrtl.passes.RemoveEmpty +import mdf.macrolib.SRAMMacro +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import java.io.File + +abstract class MacroCompilerSpec extends AnyFlatSpec with Matchers { + import scala.language.implicitConversions + implicit def String2SomeString(i: String): Option[String] = Some(i) + val testDir: String = "test_run_dir/macros" + new File(testDir).mkdirs // Make sure the testDir exists + + // Override these to change the prefixing of macroDir and testDir + val memPrefix: String = testDir + val libPrefix: String = testDir + val vPrefix: String = testDir + + // Override this to use a different cost metric. + // If this is None, the compile() call will not have any -c/-cp arguments, and + // execute() will use CostMetric.default. + val costMetric: Option[CostMetric] = None + private def getCostMetric: CostMetric = costMetric.getOrElse(CostMetric.default) + + private def costMetricCmdLine = { + costMetric match { + case None => Nil + case Some(m) => + val name = m.name() + val params = m.commandLineParams() + List("-c", name) ++ params.flatMap { case (key, value) => List("-cp", key, value) } + } + } + + private def args(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean) = + List("-m", mem, "-v", v) ++ + (lib match { + case None => Nil + case Some(l) => List("-l", l) + }) ++ + costMetricCmdLine ++ + (if (synflops) List("--mode", "synflops") else Nil) ++ + (if (useCompiler) List("--use-compiler") else Nil) + + // Run the full compiler as if from the command line interface. + // Generates the Verilog; useful in testing since an error will throw an + // exception. + def compile(mem: String, lib: String, v: String, synflops: Boolean): Unit = { + compile(mem, Some(lib), v, synflops) + } + def compile(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean = false): Unit = { + val mem_full = concat(memPrefix, mem) + val lib_full = concat(libPrefix, lib) + val v_full = concat(vPrefix, v) + + MacroCompiler.run(args(mem_full, lib_full, v_full, synflops, useCompiler)) + } + + // Helper functions to write macro libraries to the given files. + def writeToLib(lib: String, libs: Seq[mdf.macrolib.Macro]): Boolean = { + mdf.macrolib.Utils.writeMDFToPath(Some(concat(libPrefix, lib)), libs) + } + + def writeToMem(mem: String, mems: Seq[mdf.macrolib.Macro]): Boolean = { + mdf.macrolib.Utils.writeMDFToPath(Some(concat(memPrefix, mem)), mems) + } + + // Convenience function for running both compile, execute, and test at once. + def compileExecuteAndTest( + mem: String, + lib: Option[String], + v: String, + output: String, + synflops: Boolean = false, + useCompiler: Boolean = false + ): Unit = { + compile(mem, lib, v, synflops, useCompiler) + val result = execute(mem, lib, synflops, useCompiler) + test(result, output) + } + + // Compare FIRRTL outputs after reparsing output with ScalaTest ("should be"). + def test(result: Circuit, output: String): Unit = { + val gold = RemoveEmpty.run(parse(output)) + result.serialize should be(gold.serialize) + } + + // Execute the macro compiler and returns a Circuit containing the output of + // the memory compiler. + def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean): Circuit = + execute(memFile, libFile, synflops, useCompiler = false) + def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean, useCompiler: Boolean): Circuit = { + val mem_full = concat(memPrefix, memFile) + val lib_full = concat(libPrefix, libFile) + + require(memFile.isDefined) + val mems: Seq[Macro] = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(mem_full)).get.map(new Macro(_)) + val libs: Option[Seq[Macro]] = if (useCompiler) { + Utils.findSRAMCompiler(mdf.macrolib.Utils.readMDFFromPath(lib_full)).map { x => + Utils.buildSRAMMacros(x).map(new Macro(_)) + } + } else { + Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(lib_full)) match { + case Some(x) => Some(x.map(new Macro(_))) + case None => None + } + } + val macros = mems.map(_.blackbox) + val circuit = Circuit(NoInfo, macros, macros.last.name) + val passes = Seq( + new MacroCompilerPass( + Some(mems), + libs, + None, + None, + getCostMetric, + if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default + ), + new SynFlopsPass(synflops, libs.getOrElse(mems)), + RemoveEmpty + ) + val result: Circuit = passes.foldLeft(circuit)((c, pass) => pass.run(c)) + result + } + + // Helper method to deal with String + Option[String] + private def concat(a: String, b: String): String = { a + "/" + b } + private def concat(a: String, b: Option[String]): Option[String] = { + b match { + case Some(b2: String) => Some(a + "/" + b2) + case _ => None + } + } +} + +// A collection of standard SRAM generators. +trait HasSRAMGenerator { + import mdf.macrolib._ + + import scala.language.implicitConversions + implicit def Int2SomeInt(i: Int): Option[Int] = Some(i) + implicit def BigInt2SomeBigInt(i: BigInt): Option[BigInt] = Some(i) + + // Generate a standard (read/write/combo) port for testing. + // Helper methods for optional width argument + def generateTestPort( + prefix: String, + width: Option[Int], + depth: Option[BigInt], + maskGran: Option[Int] = None, + read: Boolean, + readEnable: Boolean = false, + write: Boolean, + writeEnable: Boolean = false + ): MacroPort = { + val realPrefix = if (prefix == "") "" else prefix + "_" + + MacroPort( + address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh), + clock = Some(PolarizedPort(name = realPrefix + "clk", polarity = PositiveEdge)), + readEnable = if (readEnable) Some(PolarizedPort(name = realPrefix + "read_en", polarity = ActiveHigh)) else None, + writeEnable = + if (writeEnable) Some(PolarizedPort(name = realPrefix + "write_en", polarity = ActiveHigh)) else None, + output = if (read) Some(PolarizedPort(name = realPrefix + "dout", polarity = ActiveHigh)) else None, + input = if (write) Some(PolarizedPort(name = realPrefix + "din", polarity = ActiveHigh)) else None, + maskPort = maskGran match { + case Some(_: Int) => Some(PolarizedPort(name = realPrefix + "mask", polarity = ActiveHigh)) + case _ => None + }, + maskGran = maskGran, + width = width, + depth = depth // These numbers don't matter here. + ) + } + + // Generate a read port for testing. + def generateReadPort( + prefix: String, + width: Option[Int], + depth: Option[BigInt], + readEnable: Boolean = false + ): MacroPort = { + generateTestPort(prefix, width, depth, write = false, read = true, readEnable = readEnable) + } + + // Generate a write port for testing. + def generateWritePort( + prefix: String, + width: Option[Int], + depth: Option[BigInt], + maskGran: Option[Int] = None, + writeEnable: Boolean = true + ): MacroPort = { + generateTestPort(prefix, width, depth, maskGran = maskGran, write = true, read = false, writeEnable = writeEnable) + } + + // Generate a simple read-write port for testing. + def generateReadWritePort( + prefix: String, + width: Option[Int], + depth: Option[BigInt], + maskGran: Option[Int] = None + ): MacroPort = { + generateTestPort(prefix, width, depth, maskGran = maskGran, write = true, writeEnable = true, read = true) + } + + // Generate a "simple" SRAM (active high/positive edge, 1 read-write port). + def generateSRAM( + name: String, + prefix: String, + width: Int, + depth: BigInt, + maskGran: Option[Int] = None, + extraPorts: Seq[MacroExtraPort] = List() + ): SRAMMacro = { + SRAMMacro( + name = name, + width = width, + depth = depth, + family = "1rw", + ports = Seq(generateReadWritePort(prefix, width, depth, maskGran)), + extraPorts = extraPorts + ) + } + + // Generate a "simple" SRAM group (active high/positive edge, 1 read-write port). + def generateSimpleSRAMGroup( + prefix: String, + mux: Int, + depth: Range, + width: Range, + maskGran: Option[Int] = None + ): SRAMGroup = { + SRAMGroup( + Seq("mygroup_", "width", "x", "depth", "_", "VT"), + "1rw", + Seq("svt", "lvt", "ulvt"), + mux, + depth, + width, + Seq(generateReadWritePort(prefix, None, None, maskGran)) + ) + } + + // 'vt': ('svt','lvt','ulvt'), 'mux': 2, 'depth': range(16,513,8), 'width': range(8,289,2), 'ports': 1 + // 'vt': ('svt','lvt','ulvt'), 'mux': 4, 'depth': range(32,1025,16), 'width': range(4,145), 'ports': 1} + def generateSRAMCompiler(name: String, prefix: String): mdf.macrolib.SRAMCompiler = { + SRAMCompiler( + name, + Seq( + generateSimpleSRAMGroup(prefix, 2, Range(16, 512, 8), Range(8, 288, 2)), + generateSimpleSRAMGroup(prefix, 4, Range(32, 1024, 16), Range(4, 144, 1)) + ) + ) + } +} + +// Generic "simple" test generator. +// Set up scaffolding for generating memories, files, etc. +// Override this generator to specify the expected FIRRTL output. +trait HasSimpleTestGenerator { + this: MacroCompilerSpec with HasSRAMGenerator => + // Override these with "override lazy val". + // Why lazy? These are used in the constructor here so overriding non-lazily + // would be too late. + def useCompiler: Boolean = false + def memWidth: Int + def libWidth: Int + def memDepth: BigInt + def libDepth: BigInt + def memMaskGran: Option[Int] = None + def libMaskGran: Option[Int] = None + def extraPorts: Seq[mdf.macrolib.MacroExtraPort] = List() + def extraTag: String = "" + + // "Effective" libMaskGran by considering write_enable. + val effectiveLibMaskGran: Int = libMaskGran.getOrElse(libWidth) + + // Override this in the sub-generator if you need a more specific name. + // Defaults to using reflection to pull the name of the test using this + // generator. + def generatorType: String = this.getClass.getSimpleName + + //require (memDepth >= libDepth) + + // Convenience variables to check if a mask exists. + val memHasMask: Boolean = memMaskGran.isDefined + val libHasMask: Boolean = libMaskGran.isDefined + // We need to figure out how many mask bits there are in the mem. + val memMaskBits: Int = if (memHasMask) memWidth / memMaskGran.get else 0 + val libMaskBits: Int = if (libHasMask) libWidth / libMaskGran.get else 0 + + val extraTagPrefixed: String = if (extraTag == "") "" else "-" + extraTag + + val mem = s"mem-$generatorType$extraTagPrefixed.json" + val lib = s"lib-$generatorType$extraTagPrefixed.json" + val v = s"$generatorType$extraTagPrefixed.v" + + lazy val mem_name = "target_memory" + val mem_addr_width: Int = MacroCompilerMath.ceilLog2(memDepth) + + lazy val lib_name = "awesome_lib_mem" + val lib_addr_width: Int = MacroCompilerMath.ceilLog2(libDepth) + + // Override these to change the port prefixes if needed. + def libPortPrefix: String = "lib" + def memPortPrefix: String = "outer" + + // These generate "simple" SRAMs (1 masked read-write port) by default, + // but can be overridden if need be. + def generateLibSRAM(): SRAMMacro = generateSRAM(lib_name, libPortPrefix, libWidth, libDepth, libMaskGran, extraPorts) + def generateMemSRAM(): SRAMMacro = generateSRAM(mem_name, memPortPrefix, memWidth, memDepth, memMaskGran) + + def libSRAM: SRAMMacro = generateLibSRAM() + def memSRAM: SRAMMacro = generateMemSRAM() + + def libSRAMs: Seq[SRAMMacro] = Seq(libSRAM) + def memSRAMs: Seq[SRAMMacro] = Seq(memSRAM) + + writeToLib(lib, libSRAMs) + writeToMem(mem, memSRAMs) + + // For masks, width it's a bit tricky since we have to consider cases like + // memMaskGran = 4 and libMaskGran = 8. + // Consider the actually usable libWidth in cases like the above. + val usableLibWidth: Int = + if (memMaskGran.getOrElse(Int.MaxValue) < effectiveLibMaskGran) memMaskGran.get else libWidth + + // Number of lib instances needed to hold the mem, in both directions. + // Round up (e.g. 1.5 instances = effectively 2 instances) + val depthInstances: Int = math.ceil(memDepth.toFloat / libDepth.toFloat).toInt + val widthInstances: Int = math.ceil(memWidth.toFloat / usableLibWidth).toInt + + // Number of width bits in the last width-direction memory. + // e.g. if memWidth = 16 and libWidth = 8, this would be 8 since the last memory 0_1 has 8 bits of input width. + // e.g. if memWidth = 9 and libWidth = 8, this would be 1 since the last memory 0_1 has 1 bit of input width. + lazy val lastWidthBits: Int = if (memWidth % usableLibWidth == 0) usableLibWidth else memWidth % usableLibWidth + lazy val selectBits: Int = mem_addr_width - lib_addr_width + + /** Convenience function to generate a mask statement. + * @param widthInst Width instance (mem_0_x) + * @param depthInst Depth instance (mem_x_0) + */ + def generateMaskStatement(widthInst: Int, depthInst: Int): String = { + // Width of this submemory. + val myMemWidth = if (widthInst == widthInstances - 1) lastWidthBits else usableLibWidth + // Base bit of this submemory. + // e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this + // would be 16. + val myBaseBit = usableLibWidth * widthInst + + if (libMaskGran.isDefined) { + if (memMaskGran.isEmpty) { + // If there is no memory mask, we should just turn all the lib mask + // bits high. + s"""mem_${depthInst}_$widthInst.lib_mask <= UInt<$libMaskBits>("h${((1 << libMaskBits) - 1).toHexString}")""" + } else { + // Calculate which bit of outer_mask contains the given bit. + // e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then + // calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1 + def calculateMaskBit(bit: Int): Int = bit / memMaskGran.getOrElse(memWidth) + + val bitsArr = (libMaskBits - 1 to 0 by -1).map(x => { + if (x * libMaskGran.get > myMemWidth) { + // If we have extra mask bits leftover after the effective width, + // disable those bits. + """UInt<1>("h0")""" + } else { + val outerMaskBit = calculateMaskBit(x * libMaskGran.get + myBaseBit) + s"bits(outer_mask, $outerMaskBit, $outerMaskBit)" + } + }) + val maskVal = bitsArr.reduceRight((bit, rest) => s"cat($bit, $rest)") + s"mem_${depthInst}_$widthInst.lib_mask <= $maskVal" + } + } else "" + } + + /** Helper function to generate a port. + * + * @param prefix Memory port prefix (e.g. "x" for ports like "x_clk") + * @param addrWidth Address port width + * @param width data width + * @param write Has a write port? + * @param writeEnable Has a write enable port? + * @param read Has a read port? + * @param readEnable Has a read enable port? + * @param mask Mask granularity (# bits) of the port or None. + * @param extraPorts Extra ports (name, # bits) + */ + def generatePort( + prefix: String, + addrWidth: Int, + width: Int, + write: Boolean, + writeEnable: Boolean, + read: Boolean, + readEnable: Boolean, + mask: Option[Int], + extraPorts: Seq[(String, Int)] = Seq() + ): String = { + val realPrefix = if (prefix == "") "" else prefix + "_" + + val readStr = if (read) s"output ${realPrefix}dout : UInt<$width>" else "" + val writeStr = if (write) s"input ${realPrefix}din : UInt<$width>" else "" + val readEnableStr = if (readEnable) s"input ${realPrefix}read_en : UInt<1>" else "" + val writeEnableStr = if (writeEnable) s"input ${realPrefix}write_en : UInt<1>" else "" + val maskStr = mask match { + case Some(maskBits: Int) => s"input ${realPrefix}mask : UInt<$maskBits>" + case _ => "" + } + val extraPortsStr = extraPorts.map { case (name, bits) => s" input $name : UInt<$bits>" }.mkString("\n") + s""" + input ${realPrefix}addr : UInt<$addrWidth> + input ${realPrefix}clk : Clock + $writeStr + $readStr + $readEnableStr + $writeEnableStr + $maskStr +$extraPortsStr + """ + } + + /** Helper function to generate a RW footer port. + * + * @param prefix Memory port prefix (e.g. "x" for ports like "x_clk") + * @param readEnable Has a read enable port? + * @param mask Mask granularity (# bits) of the port or None. + * @param extraPorts Extra ports (name, # bits) + */ + def generateReadWriteFooterPort( + prefix: String, + readEnable: Boolean, + mask: Option[Int], + extraPorts: Seq[(String, Int)] = Seq() + ): String = { + generatePort( + prefix, + lib_addr_width, + libWidth, + write = true, + writeEnable = true, + read = true, + readEnable = readEnable, + mask = mask, + extraPorts = extraPorts + ) + } + + /** Helper function to generate a RW header port. + * @param prefix Memory port prefix (e.g. "x" for ports like "x_clk") + * @param readEnable Has a read enable port? + * @param mask Mask granularity (# bits) of the port or None. + */ + def generateReadWriteHeaderPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = { + generatePort( + prefix, + mem_addr_width, + memWidth, + write = true, + writeEnable = true, + read = true, + readEnable = readEnable, + mask + ) + } + + // Generate the header memory ports. + def generateHeaderPorts(): String = { + require(memSRAM.ports.size == 1, "Header generator only supports single RW port mem") + generateReadWriteHeaderPort( + memPortPrefix, + memSRAM.ports.head.readEnable.isDefined, + if (memHasMask) Some(memMaskBits) else None + ) + } + + // Generate the header (contains the circuit statement and the target memory + // module. + def generateHeader(): String = { + s""" +circuit $mem_name : + module $mem_name : +${generateHeaderPorts()} + """ + } + + // Generate the target memory ports. + def generateFooterPorts(): String = { + require(libSRAM.ports.size == 1, "Footer generator only supports single RW port mem") + generateReadWriteFooterPort( + libPortPrefix, + libSRAM.ports.head.readEnable.isDefined, + if (libHasMask) Some(libMaskBits) else None, + extraPorts.map(p => (p.name, p.width)) + ) + } + + // Generate the footer (contains the target memory extmodule declaration by default). + def generateFooter(): String = { + s""" + extmodule $lib_name : +${generateFooterPorts()} + + defname = $lib_name + """ + } + + // Abstract method to generate body; to be overridden by specific generator type. + def generateBody(): String + + // Generate the entire output from header, body, and footer. + def generateOutput(): String = { + s""" +${generateHeader()} +${generateBody()} +${generateFooter()} + """ + } + + val output: String = generateOutput() +} + +// Use this trait for tests that invoke the memory compiler without lib. +trait HasNoLibTestGenerator extends HasSimpleTestGenerator { + this: MacroCompilerSpec with HasSRAMGenerator => + + // If there isn't a lib, then the "lib" will become a FIRRTL "mem", which + // in turn becomes synthesized flops. + // Therefore, make "lib" width/depth equal to the mem. + override lazy val libDepth: BigInt = memDepth + override lazy val libWidth: Int = memWidth + override lazy val lib_name: String = mem_name + // Do the same for port names. + override lazy val libPortPrefix: String = memPortPrefix + + // If there is no lib, don't generate a body. + override def generateBody() = "" +} diff --git a/src/test/scala/barstools/macros/Masks.scala b/src/test/scala/barstools/macros/Masks.scala new file mode 100644 index 00000000..5854eea1 --- /dev/null +++ b/src/test/scala/barstools/macros/Masks.scala @@ -0,0 +1,383 @@ +package barstools.macros + +// Test the ability of the compiler to deal with various mask combinations. + +trait MasksTestSettings { + this: MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator => + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) +} + +// Try all four different kinds of mask config: +/** Non-masked mem Masked mem + * --------------------------------- + * Non-masked lib | | | + * --------------------------------- + * Masked lib | | | + * --------------------------------- + */ + +class Masks_FourTypes_NonMaskedMem_NonMaskedLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val memMaskGran: Option[Int] = None + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = None + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_FourTypes_NonMaskedMem_MaskedLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val memMaskGran: Option[Int] = None + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = Some(2) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_FourTypes_MaskedMem_NonMaskedLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = None + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val memMaskGran: Option[Int] = Some(4) + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = None + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_FourTypes_MaskedMem_MaskedLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libWidth = 16 + override lazy val libMaskGran: Option[Int] = Some(4) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libWidth = 16 + override lazy val libMaskGran: Option[Int] = Some(8) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_FourTypes_MaskedMem_MaskedLib_SmallerMaskGran + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val memMaskGran: Option[Int] = Some(4) + override lazy val libWidth = 32 + override lazy val libMaskGran: Option[Int] = Some(8) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +// Bit-mask memories to non-masked libs whose width is larger than 1. + +class Masks_BitMaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val memMaskGran: Option[Int] = Some(1) + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = None + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +// FPGA-style byte-masked memories. + +class Masks_FPGAStyle_32_8 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 32 + override lazy val memMaskGran: Option[Int] = Some(32) + override lazy val libMaskGran: Option[Int] = Some(8) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +// Simple powers of two with bit-masked lib. + +class Masks_PowersOfTwo_8_1 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 64 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_PowersOfTwo_16_1 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 64 + override lazy val memMaskGran: Option[Int] = Some(16) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_PowersOfTwo_32_1 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 64 + override lazy val memMaskGran: Option[Int] = Some(32) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_PowersOfTwo_64_1 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 64 + override lazy val memMaskGran: Option[Int] = Some(64) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +// Simple powers of two with non bit-masked lib. + +class Masks_PowersOfTwo_32_4 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 128 + override lazy val memMaskGran: Option[Int] = Some(32) + override lazy val libMaskGran: Option[Int] = Some(4) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_PowersOfTwo_32_8 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 128 + override lazy val memMaskGran: Option[Int] = Some(32) + override lazy val libMaskGran: Option[Int] = Some(8) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_PowersOfTwo_8_8 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 128 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(8) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +// Width as a multiple of the mask, bit-masked lib + +class Masks_IntegerMaskMultiple_20_10 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 20 + override lazy val memMaskGran: Option[Int] = Some(10) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_IntegerMaskMultiple_21_7 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 21 + override lazy val memMaskGran: Option[Int] = Some(21) + override lazy val libMaskGran: Option[Int] = Some(7) + + (it should "be enabled when non-power of two masks are supported").is(pending) + //~ compileExecuteAndTest(mem, lib, v, output) +} + +class Masks_IntegerMaskMultiple_21_21 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 21 + override lazy val memMaskGran: Option[Int] = Some(21) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_IntegerMaskMultiple_84_21 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 84 + override lazy val memMaskGran: Option[Int] = Some(21) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_IntegerMaskMultiple_92_23 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 92 + override lazy val memMaskGran: Option[Int] = Some(23) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_IntegerMaskMultiple_117_13 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 117 + override lazy val memMaskGran: Option[Int] = Some(13) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_IntegerMaskMultiple_160_20 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 160 + override lazy val memMaskGran: Option[Int] = Some(20) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class Masks_IntegerMaskMultiple_184_23 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 184 + override lazy val memMaskGran: Option[Int] = Some(23) + override lazy val libMaskGran: Option[Int] = Some(1) + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +// Width as an non-integer multiple of the mask, bit-masked lib + +class Masks_NonIntegerMaskMultiple_32_3 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with MasksTestSettings { + override lazy val width = 32 + override lazy val memMaskGran: Option[Int] = Some(3) + override lazy val libMaskGran: Option[Int] = Some(1) + + (it should "be enabled when non-power of two masks are supported").is(pending) + //~ compileExecuteAndTest(mem, lib, v, output) +} diff --git a/src/test/scala/barstools/macros/MultiPort.scala b/src/test/scala/barstools/macros/MultiPort.scala new file mode 100644 index 00000000..07903e0a --- /dev/null +++ b/src/test/scala/barstools/macros/MultiPort.scala @@ -0,0 +1,500 @@ +package barstools.macros + +// Test that the memory compiler works fine for compiling multi-port memories. +// TODO: extend test generator to also automatically generate multi-ported memories. + +class SplitWidth_2rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + import mdf.macrolib._ + + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val memMaskGran: Option[Int] = Some(16) + override lazy val libWidth = 16 + + override def generateMemSRAM(): SRAMMacro = { + SRAMMacro( + name = mem_name, + width = memWidth, + depth = memDepth, + family = "2rw", + ports = Seq( + generateTestPort( + "portA", + memWidth, + Some(memDepth), + maskGran = memMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ), + generateTestPort( + "portB", + memWidth, + Some(memDepth), + maskGran = memMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateLibSRAM(): SRAMMacro = { + SRAMMacro( + name = lib_name, + width = libWidth, + depth = libDepth, + family = "2rw", + ports = Seq( + generateTestPort( + "portA", + libWidth, + libDepth, + write = true, + writeEnable = true, + read = true, + readEnable = true + ), + generateTestPort( + "portB", + libWidth, + libDepth, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateHeaderPorts(): String = { + generateReadWriteHeaderPort("portA", readEnable = true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort( + "portB", + readEnable = true, + Some(memMaskBits) + ) + } + + override def generateFooterPorts(): String = { + generateReadWriteFooterPort("portA", readEnable = true, None) + "\n" + generateReadWriteFooterPort( + "portB", + readEnable = true, + None + ) + } + + override def generateBody() = + """ + inst mem_0_0 of awesome_lib_mem + inst mem_0_1 of awesome_lib_mem + inst mem_0_2 of awesome_lib_mem + inst mem_0_3 of awesome_lib_mem + mem_0_0.portA_clk <= portA_clk + mem_0_0.portA_addr <= portA_addr + node portA_dout_0_0 = bits(mem_0_0.portA_dout, 15, 0) + mem_0_0.portA_din <= bits(portA_din, 15, 0) + mem_0_0.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_0.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 0, 0)), UInt<1>("h1")) + mem_0_1.portA_clk <= portA_clk + mem_0_1.portA_addr <= portA_addr + node portA_dout_0_1 = bits(mem_0_1.portA_dout, 15, 0) + mem_0_1.portA_din <= bits(portA_din, 31, 16) + mem_0_1.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_1.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 1, 1)), UInt<1>("h1")) + mem_0_2.portA_clk <= portA_clk + mem_0_2.portA_addr <= portA_addr + node portA_dout_0_2 = bits(mem_0_2.portA_dout, 15, 0) + mem_0_2.portA_din <= bits(portA_din, 47, 32) + mem_0_2.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_2.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 2, 2)), UInt<1>("h1")) + mem_0_3.portA_clk <= portA_clk + mem_0_3.portA_addr <= portA_addr + node portA_dout_0_3 = bits(mem_0_3.portA_dout, 15, 0) + mem_0_3.portA_din <= bits(portA_din, 63, 48) + mem_0_3.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_3.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 3, 3)), UInt<1>("h1")) + node portA_dout_0 = cat(portA_dout_0_3, cat(portA_dout_0_2, cat(portA_dout_0_1, portA_dout_0_0))) + mem_0_0.portB_clk <= portB_clk + mem_0_0.portB_addr <= portB_addr + node portB_dout_0_0 = bits(mem_0_0.portB_dout, 15, 0) + mem_0_0.portB_din <= bits(portB_din, 15, 0) + mem_0_0.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_0.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 0, 0)), UInt<1>("h1")) + mem_0_1.portB_clk <= portB_clk + mem_0_1.portB_addr <= portB_addr + node portB_dout_0_1 = bits(mem_0_1.portB_dout, 15, 0) + mem_0_1.portB_din <= bits(portB_din, 31, 16) + mem_0_1.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_1.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 1, 1)), UInt<1>("h1")) + mem_0_2.portB_clk <= portB_clk + mem_0_2.portB_addr <= portB_addr + node portB_dout_0_2 = bits(mem_0_2.portB_dout, 15, 0) + mem_0_2.portB_din <= bits(portB_din, 47, 32) + mem_0_2.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_2.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 2, 2)), UInt<1>("h1")) + mem_0_3.portB_clk <= portB_clk + mem_0_3.portB_addr <= portB_addr + node portB_dout_0_3 = bits(mem_0_3.portB_dout, 15, 0) + mem_0_3.portB_din <= bits(portB_din, 63, 48) + mem_0_3.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_3.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 3, 3)), UInt<1>("h1")) + node portB_dout_0 = cat(portB_dout_0_3, cat(portB_dout_0_2, cat(portB_dout_0_1, portB_dout_0_0))) + portA_dout <= mux(UInt<1>("h1"), portA_dout_0, UInt<64>("h0")) + portB_dout <= mux(UInt<1>("h1"), portB_dout_0, UInt<64>("h0")) +""" + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth_1r_1w extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + import mdf.macrolib._ + + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val memMaskGran: Option[Int] = Some(16) + override lazy val libWidth = 16 + + override def generateMemSRAM(): SRAMMacro = { + SRAMMacro( + name = mem_name, + width = memWidth, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateTestPort( + "portA", + memWidth, + Some(memDepth), + maskGran = memMaskGran, + write = false, + read = true, + readEnable = true + ), + generateTestPort( + "portB", + memWidth, + Some(memDepth), + maskGran = memMaskGran, + write = true, + writeEnable = true, + read = false + ) + ) + ) + } + + override def generateLibSRAM(): SRAMMacro = { + SRAMMacro( + name = lib_name, + width = libWidth, + depth = libDepth, + family = "1r1w", + ports = Seq( + generateTestPort( + "portA", + libWidth, + libDepth, + write = false, + read = true, + readEnable = true + ), + generateTestPort("portB", libWidth, libDepth, write = true, writeEnable = true, read = false) + ) + ) + } + + override def generateHeaderPorts(): String = { + generatePort( + "portA", + mem_addr_width, + memWidth, + write = false, + writeEnable = false, + read = true, + readEnable = true, + Some(memMaskBits) + ) + "\n" + + generatePort( + "portB", + mem_addr_width, + memWidth, + write = true, + writeEnable = true, + read = false, + readEnable = false, + Some(memMaskBits) + ) + } + + override def generateFooterPorts(): String = { + generatePort( + "portA", + lib_addr_width, + libWidth, + write = false, + writeEnable = false, + read = true, + readEnable = true, + None + ) + "\n" + + generatePort( + "portB", + lib_addr_width, + libWidth, + write = true, + writeEnable = true, + read = false, + readEnable = false, + None + ) + } + + override def generateBody() = + """ + inst mem_0_0 of awesome_lib_mem + inst mem_0_1 of awesome_lib_mem + inst mem_0_2 of awesome_lib_mem + inst mem_0_3 of awesome_lib_mem + mem_0_0.portB_clk <= portB_clk + mem_0_0.portB_addr <= portB_addr + mem_0_0.portB_din <= bits(portB_din, 15, 0) + mem_0_0.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 0, 0)), UInt<1>("h1")) + mem_0_1.portB_clk <= portB_clk + mem_0_1.portB_addr <= portB_addr + mem_0_1.portB_din <= bits(portB_din, 31, 16) + mem_0_1.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 1, 1)), UInt<1>("h1")) + mem_0_2.portB_clk <= portB_clk + mem_0_2.portB_addr <= portB_addr + mem_0_2.portB_din <= bits(portB_din, 47, 32) + mem_0_2.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 2, 2)), UInt<1>("h1")) + mem_0_3.portB_clk <= portB_clk + mem_0_3.portB_addr <= portB_addr + mem_0_3.portB_din <= bits(portB_din, 63, 48) + mem_0_3.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 3, 3)), UInt<1>("h1")) + mem_0_0.portA_clk <= portA_clk + mem_0_0.portA_addr <= portA_addr + node portA_dout_0_0 = bits(mem_0_0.portA_dout, 15, 0) + mem_0_0.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_1.portA_clk <= portA_clk + mem_0_1.portA_addr <= portA_addr + node portA_dout_0_1 = bits(mem_0_1.portA_dout, 15, 0) + mem_0_1.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_2.portA_clk <= portA_clk + mem_0_2.portA_addr <= portA_addr + node portA_dout_0_2 = bits(mem_0_2.portA_dout, 15, 0) + mem_0_2.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_3.portA_clk <= portA_clk + mem_0_3.portA_addr <= portA_addr + node portA_dout_0_3 = bits(mem_0_3.portA_dout, 15, 0) + mem_0_3.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + node portA_dout_0 = cat(portA_dout_0_3, cat(portA_dout_0_2, cat(portA_dout_0_1, portA_dout_0_0))) + portA_dout <= mux(UInt<1>("h1"), portA_dout_0, UInt<64>("h0")) +""" + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth_2rw_differentMasks extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + import mdf.macrolib._ + + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val memMaskGran: Option[Int] = Some(16) + override lazy val libWidth = 16 + + lazy val memMaskGranB = 8 // these generators are run at constructor time + + override def generateMemSRAM(): SRAMMacro = { + SRAMMacro( + name = mem_name, + width = memWidth, + depth = memDepth, + family = "2rw", + ports = Seq( + generateTestPort( + "portA", + memWidth, + Some(memDepth), + maskGran = memMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ), + generateTestPort( + "portB", + memWidth, + Some(memDepth), + maskGran = Some(memMaskGranB), + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateLibSRAM(): SRAMMacro = { + SRAMMacro( + name = lib_name, + width = libWidth, + depth = libDepth, + family = "2rw", + ports = Seq( + generateTestPort( + "portA", + libWidth, + libDepth, + write = true, + writeEnable = true, + read = true, + readEnable = true + ), + generateTestPort( + "portB", + libWidth, + libDepth, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateHeaderPorts(): String = { + generateReadWriteHeaderPort("portA", readEnable = true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort( + "portB", + readEnable = true, + Some(memWidth / memMaskGranB) + ) + } + + override def generateFooterPorts(): String = { + generateReadWriteFooterPort("portA", readEnable = true, None) + "\n" + generateReadWriteFooterPort( + "portB", + readEnable = true, + None + ) + } + + override def generateBody() = + """ + inst mem_0_0 of awesome_lib_mem + inst mem_0_1 of awesome_lib_mem + inst mem_0_2 of awesome_lib_mem + inst mem_0_3 of awesome_lib_mem + inst mem_0_4 of awesome_lib_mem + inst mem_0_5 of awesome_lib_mem + inst mem_0_6 of awesome_lib_mem + inst mem_0_7 of awesome_lib_mem + mem_0_0.portA_clk <= portA_clk + mem_0_0.portA_addr <= portA_addr + node portA_dout_0_0 = bits(mem_0_0.portA_dout, 7, 0) + mem_0_0.portA_din <= bits(portA_din, 7, 0) + mem_0_0.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_0.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 0, 0)), UInt<1>("h1")) + mem_0_1.portA_clk <= portA_clk + mem_0_1.portA_addr <= portA_addr + node portA_dout_0_1 = bits(mem_0_1.portA_dout, 7, 0) + mem_0_1.portA_din <= bits(portA_din, 15, 8) + mem_0_1.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_1.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 0, 0)), UInt<1>("h1")) + mem_0_2.portA_clk <= portA_clk + mem_0_2.portA_addr <= portA_addr + node portA_dout_0_2 = bits(mem_0_2.portA_dout, 7, 0) + mem_0_2.portA_din <= bits(portA_din, 23, 16) + mem_0_2.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_2.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 1, 1)), UInt<1>("h1")) + mem_0_3.portA_clk <= portA_clk + mem_0_3.portA_addr <= portA_addr + node portA_dout_0_3 = bits(mem_0_3.portA_dout, 7, 0) + mem_0_3.portA_din <= bits(portA_din, 31, 24) + mem_0_3.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_3.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 1, 1)), UInt<1>("h1")) + mem_0_4.portA_clk <= portA_clk + mem_0_4.portA_addr <= portA_addr + node portA_dout_0_4 = bits(mem_0_4.portA_dout, 7, 0) + mem_0_4.portA_din <= bits(portA_din, 39, 32) + mem_0_4.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_4.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 2, 2)), UInt<1>("h1")) + mem_0_5.portA_clk <= portA_clk + mem_0_5.portA_addr <= portA_addr + node portA_dout_0_5 = bits(mem_0_5.portA_dout, 7, 0) + mem_0_5.portA_din <= bits(portA_din, 47, 40) + mem_0_5.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_5.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 2, 2)), UInt<1>("h1")) + mem_0_6.portA_clk <= portA_clk + mem_0_6.portA_addr <= portA_addr + node portA_dout_0_6 = bits(mem_0_6.portA_dout, 7, 0) + mem_0_6.portA_din <= bits(portA_din, 55, 48) + mem_0_6.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_6.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 3, 3)), UInt<1>("h1")) + mem_0_7.portA_clk <= portA_clk + mem_0_7.portA_addr <= portA_addr + node portA_dout_0_7 = bits(mem_0_7.portA_dout, 7, 0) + mem_0_7.portA_din <= bits(portA_din, 63, 56) + mem_0_7.portA_read_en <= and(portA_read_en, UInt<1>("h1")) + mem_0_7.portA_write_en <= and(and(and(portA_write_en, UInt<1>("h1")), bits(portA_mask, 3, 3)), UInt<1>("h1")) + node portA_dout_0 = cat(portA_dout_0_7, cat(portA_dout_0_6, cat(portA_dout_0_5, cat(portA_dout_0_4, cat(portA_dout_0_3, cat(portA_dout_0_2, cat(portA_dout_0_1, portA_dout_0_0))))))) + mem_0_0.portB_clk <= portB_clk + mem_0_0.portB_addr <= portB_addr + node portB_dout_0_0 = bits(mem_0_0.portB_dout, 7, 0) + mem_0_0.portB_din <= bits(portB_din, 7, 0) + mem_0_0.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_0.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 0, 0)), UInt<1>("h1")) + mem_0_1.portB_clk <= portB_clk + mem_0_1.portB_addr <= portB_addr + node portB_dout_0_1 = bits(mem_0_1.portB_dout, 7, 0) + mem_0_1.portB_din <= bits(portB_din, 15, 8) + mem_0_1.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_1.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 1, 1)), UInt<1>("h1")) + mem_0_2.portB_clk <= portB_clk + mem_0_2.portB_addr <= portB_addr + node portB_dout_0_2 = bits(mem_0_2.portB_dout, 7, 0) + mem_0_2.portB_din <= bits(portB_din, 23, 16) + mem_0_2.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_2.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 2, 2)), UInt<1>("h1")) + mem_0_3.portB_clk <= portB_clk + mem_0_3.portB_addr <= portB_addr + node portB_dout_0_3 = bits(mem_0_3.portB_dout, 7, 0) + mem_0_3.portB_din <= bits(portB_din, 31, 24) + mem_0_3.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_3.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 3, 3)), UInt<1>("h1")) + mem_0_4.portB_clk <= portB_clk + mem_0_4.portB_addr <= portB_addr + node portB_dout_0_4 = bits(mem_0_4.portB_dout, 7, 0) + mem_0_4.portB_din <= bits(portB_din, 39, 32) + mem_0_4.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_4.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 4, 4)), UInt<1>("h1")) + mem_0_5.portB_clk <= portB_clk + mem_0_5.portB_addr <= portB_addr + node portB_dout_0_5 = bits(mem_0_5.portB_dout, 7, 0) + mem_0_5.portB_din <= bits(portB_din, 47, 40) + mem_0_5.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_5.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 5, 5)), UInt<1>("h1")) + mem_0_6.portB_clk <= portB_clk + mem_0_6.portB_addr <= portB_addr + node portB_dout_0_6 = bits(mem_0_6.portB_dout, 7, 0) + mem_0_6.portB_din <= bits(portB_din, 55, 48) + mem_0_6.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_6.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 6, 6)), UInt<1>("h1")) + mem_0_7.portB_clk <= portB_clk + mem_0_7.portB_addr <= portB_addr + node portB_dout_0_7 = bits(mem_0_7.portB_dout, 7, 0) + mem_0_7.portB_din <= bits(portB_din, 63, 56) + mem_0_7.portB_read_en <= and(portB_read_en, UInt<1>("h1")) + mem_0_7.portB_write_en <= and(and(and(portB_write_en, UInt<1>("h1")), bits(portB_mask, 7, 7)), UInt<1>("h1")) + node portB_dout_0 = cat(portB_dout_0_7, cat(portB_dout_0_6, cat(portB_dout_0_5, cat(portB_dout_0_4, cat(portB_dout_0_3, cat(portB_dout_0_2, cat(portB_dout_0_1, portB_dout_0_0))))))) + portA_dout <= mux(UInt<1>("h1"), portA_dout_0, UInt<64>("h0")) + portB_dout <= mux(UInt<1>("h1"), portB_dout_0, UInt<64>("h0")) +""" + + compileExecuteAndTest(mem, lib, v, output) +} diff --git a/src/test/scala/barstools/macros/SRAMCompiler.scala b/src/test/scala/barstools/macros/SRAMCompiler.scala new file mode 100644 index 00000000..750283ce --- /dev/null +++ b/src/test/scala/barstools/macros/SRAMCompiler.scala @@ -0,0 +1,21 @@ +package barstools.macros + +import mdf.macrolib + +class SRAMCompiler extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + val compiler: macrolib.SRAMCompiler = generateSRAMCompiler("awesome", "A") + val verilog = s"v-SRAMCompiler.v" + override lazy val depth = BigInt(16) + override lazy val memWidth = 8 + override lazy val libWidth = 8 + override lazy val mem_name = "mymem" + override lazy val memPortPrefix = "X" + override lazy val lib_name = "mygroup_8x16_SVT" + override lazy val libPortPrefix = "A" + + writeToLib(lib, Seq(compiler)) + + writeToMem(mem, Seq(generateSRAM("mymem", "X", 8, 16))) + + compileExecuteAndTest(mem, Some(lib), verilog, output = output, useCompiler = true) +} diff --git a/src/test/scala/barstools/macros/SimpleSplitDepth.scala b/src/test/scala/barstools/macros/SimpleSplitDepth.scala new file mode 100644 index 00000000..7e02bc1f --- /dev/null +++ b/src/test/scala/barstools/macros/SimpleSplitDepth.scala @@ -0,0 +1,638 @@ +package barstools.macros + +// Test the depth splitting aspect of the memory compiler. +// This file is for simple tests: one read-write port, powers of two sizes, etc. +// For example, implementing a 4096x32 memory using four 1024x32 memories. + +trait HasSimpleDepthTestGenerator extends HasSimpleTestGenerator { + this: MacroCompilerSpec with HasSRAMGenerator => + def width: Int + + override lazy val memWidth: Int = width + override lazy val libWidth: Int = width + + // Generate a depth-splitting body. + override def generateBody(): String = { + val output = new StringBuilder + + if (selectBits > 0) { + output.append( + s""" + node ${memPortPrefix}_addr_sel = bits(${memPortPrefix}_addr, ${mem_addr_width - 1}, $lib_addr_width) + reg ${memPortPrefix}_addr_sel_reg : UInt<$selectBits>, ${memPortPrefix}_clk with : + reset => (UInt<1>("h0"), ${memPortPrefix}_addr_sel_reg) + ${memPortPrefix}_addr_sel_reg <= mux(UInt<1>("h1"), ${memPortPrefix}_addr_sel, ${memPortPrefix}_addr_sel_reg) +""" + ) + } + + for (i <- 0 until depthInstances) { + + val maskStatement = generateMaskStatement(0, i) + val enableIdentifier = + if (selectBits > 0) s"""eq(${memPortPrefix}_addr_sel, UInt<$selectBits>("h${i.toHexString}"))""" + else "UInt<1>(\"h1\")" + val chipEnable = s"""UInt<1>("h1")""" + val writeEnable = + if (memMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, $chipEnable)" else s"${memPortPrefix}_write_en" + output.append( + s""" + inst mem_${i}_0 of $lib_name + mem_${i}_0.${libPortPrefix}_clk <= ${memPortPrefix}_clk + mem_${i}_0.${libPortPrefix}_addr <= ${memPortPrefix}_addr + node ${memPortPrefix}_dout_${i}_0 = bits(mem_${i}_0.${libPortPrefix}_dout, ${width - 1}, 0) + mem_${i}_0.${libPortPrefix}_din <= bits(${memPortPrefix}_din, ${width - 1}, 0) + $maskStatement + mem_${i}_0.${libPortPrefix}_write_en <= and(and($writeEnable, UInt<1>("h1")), $enableIdentifier) + node ${memPortPrefix}_dout_$i = ${memPortPrefix}_dout_${i}_0 + """ + ) + } + def generate_outer_dout_tree(i: Int, depthInstances: Int): String = { + if (i > depthInstances - 1) { + s"""UInt<$libWidth>("h0")""" + } else { + s"""mux(eq(${memPortPrefix}_addr_sel_reg, UInt<%d>("h%s")), ${memPortPrefix}_dout_%d, %s)""".format( + selectBits, + i.toHexString, + i, + generate_outer_dout_tree(i + 1, depthInstances) + ) + } + } + output.append(s" ${memPortPrefix}_dout <= ") + if (selectBits > 0) { + output.append(generate_outer_dout_tree(0, depthInstances)) + } else { + output.append(s"""mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<$libWidth>("h0"))""") + } + + output.toString + } +} + +// Try different widths +class SplitDepth4096x32_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(4096) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth4096x16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 16 + override lazy val memDepth = BigInt(4096) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth32768x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(32768) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth4096x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(4096) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth1024x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(1024) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Non power of two +class SplitDepth2000x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(2000) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2049x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(2049) + override lazy val libDepth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Masked RAMs + +// Test for mem mask == lib mask (i.e. mask is a write enable bit) +class SplitDepth2048x32_mrw_lib32 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(32) + override lazy val libMaskGran: Option[Int] = Some(32) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x8_mrw_lib8 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 8 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(8) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Non-bit level mask +class SplitDepth2048x64_mrw_mem32_lib8 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator { + override lazy val width = 64 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(32) + override lazy val libMaskGran: Option[Int] = Some(8) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Bit level mask +class SplitDepth2048x32_mrw_mem16_lib1 + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(16) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x32_mrw_mem8_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x32_mrw_mem4_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(4) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x32_mrw_mem2_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(2) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Non-powers of 2 mask sizes +class SplitDepth2048x32_mrw_mem3_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(3) + override lazy val libMaskGran: Option[Int] = Some(1) + + (it should "be enabled when non-power of two masks are supported").is(pending) + //compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x32_mrw_mem7_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(7) + override lazy val libMaskGran: Option[Int] = Some(1) + + (it should "be enabled when non-power of two masks are supported").is(pending) + //compileExecuteAndTest(mem, lib, v, output) +} + +class SplitDepth2048x32_mrw_mem9_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val memMaskGran: Option[Int] = Some(9) + override lazy val libMaskGran: Option[Int] = Some(1) + + (it should "be enabled when non-power of two masks are supported").is(pending) + //compileExecuteAndTest(mem, lib, v, output) +} + +// Try an extra port +class SplitDepth2048x8_extraPort extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + import mdf.macrolib._ + + override lazy val width = 8 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val extraPorts = List( + MacroExtraPort(name = "extra_port", width = 8, portType = Constant, value = 0xff) + ) + override lazy val extraTag = "extraPort" + + override def generateOutput(): String = + """ +circuit target_memory : + module target_memory : + input outer_addr : UInt<11> + input outer_clk : Clock + input outer_din : UInt<8> + output outer_dout : UInt<8> + input outer_write_en : UInt<1> + + node outer_addr_sel = bits(outer_addr, 10, 10) + reg outer_addr_sel_reg : UInt<1>, outer_clk with : + reset => (UInt<1>("h0"), outer_addr_sel_reg) + outer_addr_sel_reg <= mux(UInt<1>("h1"), outer_addr_sel, outer_addr_sel_reg) + + inst mem_0_0 of awesome_lib_mem + mem_0_0.extra_port <= UInt<8>("hff") + mem_0_0.lib_clk <= outer_clk + mem_0_0.lib_addr <= outer_addr + node outer_dout_0_0 = bits(mem_0_0.lib_dout, 7, 0) + mem_0_0.lib_din <= bits(outer_din, 7, 0) + + mem_0_0.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), eq(outer_addr_sel, UInt<1>("h0"))) + node outer_dout_0 = outer_dout_0_0 + + inst mem_1_0 of awesome_lib_mem + mem_1_0.extra_port <= UInt<8>("hff") + mem_1_0.lib_clk <= outer_clk + mem_1_0.lib_addr <= outer_addr + node outer_dout_1_0 = bits(mem_1_0.lib_dout, 7, 0) + mem_1_0.lib_din <= bits(outer_din, 7, 0) + + mem_1_0.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), eq(outer_addr_sel, UInt<1>("h1"))) + node outer_dout_1 = outer_dout_1_0 + outer_dout <= mux(eq(outer_addr_sel_reg, UInt<1>("h0")), outer_dout_0, mux(eq(outer_addr_sel_reg, UInt<1>("h1")), outer_dout_1, UInt<8>("h0"))) + extmodule awesome_lib_mem : + input lib_addr : UInt<10> + input lib_clk : Clock + input lib_din : UInt<8> + output lib_dout : UInt<8> + input lib_write_en : UInt<1> + input extra_port : UInt<8> + + defname = awesome_lib_mem + """ + + compileExecuteAndTest(mem, lib, v, output) +} + +// Split read and (non-masked) write ports (r+w). +class SplitDepth_SplitPortsNonMasked extends MacroCompilerSpec with HasSRAMGenerator { + lazy val width = 8 + lazy val memDepth = BigInt(2048) + lazy val libDepth = BigInt(1024) + + override val memPrefix: String = testDir + override val libPrefix: String = testDir + + import mdf.macrolib._ + + "Non-masked split lib; split mem" should "split fine" in { + val lib = "lib-split_depth-r-w-split-lib-split-mem.json" + val mem = "mem-split_depth-r-w-split-lib-split-mem.json" + val v = "split_depth-r-w-split-lib-split-mem.v" + + val libMacro = SRAMMacro( + name = "awesome_lib_mem", + width = width, + depth = libDepth, + family = "1r1w", + ports = Seq( + generateReadPort("innerA", width, libDepth), + generateWritePort("innerB", width, libDepth) + ) + ) + + val memMacro = SRAMMacro( + name = "target_memory", + width = width, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateReadPort("outerB", width, memDepth), + generateWritePort("outerA", width, memDepth) + ) + ) + + writeToLib(mem, Seq(memMacro)) + writeToLib(lib, Seq(libMacro)) + + val output = + """ +circuit target_memory : + module target_memory : + input outerB_addr : UInt<11> + input outerB_clk : Clock + output outerB_dout : UInt<8> + input outerA_addr : UInt<11> + input outerA_clk : Clock + input outerA_din : UInt<8> + input outerA_write_en : UInt<1> + + node outerB_addr_sel = bits(outerB_addr, 10, 10) + reg outerB_addr_sel_reg : UInt<1>, outerB_clk with : + reset => (UInt<1>("h0"), outerB_addr_sel_reg) + outerB_addr_sel_reg <= mux(UInt<1>("h1"), outerB_addr_sel, outerB_addr_sel_reg) + node outerA_addr_sel = bits(outerA_addr, 10, 10) + inst mem_0_0 of awesome_lib_mem + mem_0_0.innerB_clk <= outerA_clk + mem_0_0.innerB_addr <= outerA_addr + mem_0_0.innerB_din <= bits(outerA_din, 7, 0) + mem_0_0.innerB_write_en <= and(and(and(outerA_write_en, UInt<1>("h1")), UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h0"))) + mem_0_0.innerA_clk <= outerB_clk + mem_0_0.innerA_addr <= outerB_addr + node outerB_dout_0_0 = bits(mem_0_0.innerA_dout, 7, 0) + node outerB_dout_0 = outerB_dout_0_0 + inst mem_1_0 of awesome_lib_mem + mem_1_0.innerB_clk <= outerA_clk + mem_1_0.innerB_addr <= outerA_addr + mem_1_0.innerB_din <= bits(outerA_din, 7, 0) + mem_1_0.innerB_write_en <= and(and(and(outerA_write_en, UInt<1>("h1")), UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h1"))) + mem_1_0.innerA_clk <= outerB_clk + mem_1_0.innerA_addr <= outerB_addr + node outerB_dout_1_0 = bits(mem_1_0.innerA_dout, 7, 0) + node outerB_dout_1 = outerB_dout_1_0 + outerB_dout <= mux(eq(outerB_addr_sel_reg, UInt<1>("h0")), outerB_dout_0, mux(eq(outerB_addr_sel_reg, UInt<1>("h1")), outerB_dout_1, UInt<8>("h0"))) + + extmodule awesome_lib_mem : + input innerA_addr : UInt<10> + input innerA_clk : Clock + output innerA_dout : UInt<8> + input innerB_addr : UInt<10> + input innerB_clk : Clock + input innerB_din : UInt<8> + input innerB_write_en : UInt<1> + + defname = awesome_lib_mem +""" + + compileExecuteAndTest(mem, lib, v, output) + } + + "Non-masked regular lib; split mem" should "split fine" in { + // Enable this test when the memory compiler can compile non-matched + // memories (e.g. mrw mem and r+mw lib). + // Right now all we can get is a "port count must match" error. + pending + + val lib = "lib-split_depth-r-w-regular-lib-split-mem.json" + val mem = "mem-split_depth-r-w-regular-lib-split-mem.json" + val v = "split_depth-r-w-regular-lib-split-mem.v" + + val memMacro = SRAMMacro( + name = "target_memory", + width = width, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateReadPort("outerB", width, memDepth), + generateWritePort("outerA", width, memDepth) + ) + ) + + writeToLib(mem, Seq(memMacro)) + writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth))) + + val output = + """ +TODO +""" + + compileExecuteAndTest(mem, lib, v, output) + } + + "Non-masked split lib; regular mem" should "split fine" in { + // Enable this test when the memory compiler can compile non-matched + // memories (e.g. mrw mem and r+mw lib). + // Right now all we can get is a "port count must match" error. + // [edwardw]: does this even make sense? Can we compile a 2-ported memory using 1-ported memories? + pending + + val lib = "lib-split_depth-r-w-split-lib-regular-mem.json" + val mem = "mem-split_depth-r-w-split-lib-regular-mem.json" + val v = "split_depth-r-w-split-lib-regular-mem.v" + + val libMacro = SRAMMacro( + name = "awesome_lib_mem", + width = width, + depth = libDepth, + family = "1rw", + ports = Seq( + generateReadPort("innerA", width, libDepth), + generateWritePort("innerB", width, libDepth) + ) + ) + + writeToLib(mem, Seq(generateSRAM("target_memory", "outer", width, memDepth))) + writeToLib(lib, Seq(libMacro)) + + val output = + """ +TODO +""" + + compileExecuteAndTest(mem, lib, v, output) + } +} + +// Split read and (masked) write ports (r+mw). +class SplitDepth_SplitPortsMasked extends MacroCompilerSpec with HasSRAMGenerator { + lazy val width = 8 + lazy val memDepth = BigInt(2048) + lazy val libDepth = BigInt(1024) + lazy val memMaskGran: Option[Int] = Some(8) + lazy val libMaskGran: Option[Int] = Some(1) + + override val memPrefix: String = testDir + override val libPrefix: String = testDir + + import mdf.macrolib._ + + "Masked split lib; split mem" should "split fine" in { + val lib = "lib-split_depth-r-mw-split-lib-split-mem.json" + val mem = "mem-split_depth-r-mw-split-lib-split-mem.json" + val v = "split_depth-r-mw-split-lib-split-mem.v" + + val libMacro = SRAMMacro( + name = "awesome_lib_mem", + width = width, + depth = libDepth, + family = "1r1w", + ports = Seq( + generateReadPort("innerA", width, libDepth), + generateWritePort("innerB", width, libDepth, libMaskGran) + ) + ) + + val memMacro = SRAMMacro( + name = "target_memory", + width = width, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateReadPort("outerB", width, memDepth), + generateWritePort("outerA", width, memDepth, memMaskGran) + ) + ) + + writeToLib(mem, Seq(memMacro)) + writeToLib(lib, Seq(libMacro)) + + val output = + """ +circuit target_memory : + module target_memory : + input outerB_addr : UInt<11> + input outerB_clk : Clock + output outerB_dout : UInt<8> + input outerA_addr : UInt<11> + input outerA_clk : Clock + input outerA_din : UInt<8> + input outerA_write_en : UInt<1> + input outerA_mask : UInt<1> + + node outerB_addr_sel = bits(outerB_addr, 10, 10) + reg outerB_addr_sel_reg : UInt<1>, outerB_clk with : + reset => (UInt<1>("h0"), outerB_addr_sel_reg) + outerB_addr_sel_reg <= mux(UInt<1>("h1"), outerB_addr_sel, outerB_addr_sel_reg) + node outerA_addr_sel = bits(outerA_addr, 10, 10) + inst mem_0_0 of awesome_lib_mem + mem_0_0.innerB_clk <= outerA_clk + mem_0_0.innerB_addr <= outerA_addr + mem_0_0.innerB_din <= bits(outerA_din, 7, 0) + mem_0_0.innerB_mask <= cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), bits(outerA_mask, 0, 0)))))))) + mem_0_0.innerB_write_en <= and(and(outerA_write_en, UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h0"))) + mem_0_0.innerA_clk <= outerB_clk + mem_0_0.innerA_addr <= outerB_addr + node outerB_dout_0_0 = bits(mem_0_0.innerA_dout, 7, 0) + node outerB_dout_0 = outerB_dout_0_0 + inst mem_1_0 of awesome_lib_mem + mem_1_0.innerB_clk <= outerA_clk + mem_1_0.innerB_addr <= outerA_addr + mem_1_0.innerB_din <= bits(outerA_din, 7, 0) + mem_1_0.innerB_mask <= cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), bits(outerA_mask, 0, 0)))))))) + mem_1_0.innerB_write_en <= and(and(outerA_write_en, UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h1"))) + mem_1_0.innerA_clk <= outerB_clk + mem_1_0.innerA_addr <= outerB_addr + node outerB_dout_1_0 = bits(mem_1_0.innerA_dout, 7, 0) + node outerB_dout_1 = outerB_dout_1_0 + outerB_dout <= mux(eq(outerB_addr_sel_reg, UInt<1>("h0")), outerB_dout_0, mux(eq(outerB_addr_sel_reg, UInt<1>("h1")), outerB_dout_1, UInt<8>("h0"))) + + extmodule awesome_lib_mem : + input innerA_addr : UInt<10> + input innerA_clk : Clock + output innerA_dout : UInt<8> + input innerB_addr : UInt<10> + input innerB_clk : Clock + input innerB_din : UInt<8> + input innerB_write_en : UInt<1> + input innerB_mask : UInt<8> + + defname = awesome_lib_mem +""" + + compileExecuteAndTest(mem, lib, v, output) + } + + "Non-masked regular lib; split mem" should "split fine" in { + // Enable this test when the memory compiler can compile non-matched + // memories (e.g. mrw mem and r+mw lib). + // Right now all we can get is a "port count must match" error. + pending + + val lib = "lib-split_depth-r-mw-regular-lib-split-mem.json" + val mem = "mem-split_depth-r-mw-regular-lib-split-mem.json" + val v = "split_depth-r-mw-regular-lib-split-mem.v" + + val memMacro = SRAMMacro( + name = "target_memory", + width = width, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateReadPort("outerB", width, memDepth), + generateWritePort("outerA", width, memDepth, memMaskGran) + ) + ) + + writeToLib(mem, Seq(memMacro)) + writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth, libMaskGran))) + + val output = + """ +TODO +""" + + compileExecuteAndTest(mem, lib, v, output) + } + + "Non-masked split lib; regular mem" should "split fine" in { + // Enable this test when the memory compiler can compile non-matched + // memories (e.g. mrw mem and r+mw lib). + // Right now all we can get is a "port count must match" error. + // [edwardw]: does this even make sense? Can we compile a 2-ported memory using 1-ported memories? + pending + + val lib = "lib-split_depth-r-mw-split-lib-regular-mem.json" + val mem = "mem-split_depth-r-mw-split-lib-regular-mem.json" + val v = "split_depth-r-mw-split-lib-regular-mem.v" + + val libMacro = SRAMMacro( + name = "awesome_lib_mem", + width = width, + depth = libDepth, + family = "1rw", + ports = Seq( + generateReadPort("innerA", width, libDepth), + generateWritePort("innerB", width, libDepth, libMaskGran) + ) + ) + + writeToLib(mem, Seq(generateSRAM("target_memory", "outer", width, memDepth, memMaskGran))) + writeToLib(lib, Seq(libMacro)) + + val output = + """ +TODO +""" + + compileExecuteAndTest(mem, lib, v, output) + } +} diff --git a/src/test/scala/barstools/macros/SimpleSplitWidth.scala b/src/test/scala/barstools/macros/SimpleSplitWidth.scala new file mode 100644 index 00000000..3dffc66f --- /dev/null +++ b/src/test/scala/barstools/macros/SimpleSplitWidth.scala @@ -0,0 +1,608 @@ +package barstools.macros + +// Test the width splitting aspect of the memory compiler. +// For example, implementing a 1024x32 memory using four 1024x8 memories. + +trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator { + this: MacroCompilerSpec with HasSRAMGenerator => + def depth: BigInt + + override lazy val memDepth: BigInt = depth + override lazy val libDepth: BigInt = depth + + override def generateBody(): String = { + val output = new StringBuilder + + // Generate mem_0_ lines for number of width instances. + output.append( + (0 until widthInstances).map { i: Int => + s""" + inst mem_0_$i of $lib_name +""" + }.reduceLeft(_ + _) + ) + + // Generate submemory connection blocks. + output.append((for (i <- 0 until widthInstances) yield { + // Width of this submemory. + val myMemWidth = if (i == widthInstances - 1) lastWidthBits else usableLibWidth + // Base bit of this submemory. + // e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this + // would be 16. + val myBaseBit = usableLibWidth * i + + val maskStatement = generateMaskStatement(i, 0) + + // We need to use writeEnable as a crude "mask" if mem has a mask but + // lib does not. + val writeEnableBit = if (libMaskGran.isEmpty && memMaskGran.isDefined) { + val outerMaskBit = myBaseBit / memMaskGran.get + s"bits(outer_mask, $outerMaskBit, $outerMaskBit)" + } else """UInt<1>("h1")""" + val chipEnable = s"""UInt<1>("h1")""" + val writeEnableExpr = + if (libMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, $chipEnable)" else s"${memPortPrefix}_write_en" + + s""" + mem_0_$i.${libPortPrefix}_clk <= ${memPortPrefix}_clk + mem_0_$i.${libPortPrefix}_addr <= ${memPortPrefix}_addr + node ${memPortPrefix}_dout_0_$i = bits(mem_0_$i.${libPortPrefix}_dout, ${myMemWidth - 1}, 0) + mem_0_$i.${libPortPrefix}_din <= bits(${memPortPrefix}_din, ${myBaseBit + myMemWidth - 1}, $myBaseBit) + $maskStatement + mem_0_$i.${libPortPrefix}_write_en <= and(and($writeEnableExpr, $writeEnableBit), UInt<1>("h1")) +""" + }).reduceLeft(_ + _)) + + // Generate final output that concats together the sub-memories. + // e.g. cat(outer_dout_0_2, cat(outer_dout_0_1, outer_dout_0_0)) + output.append { + val doutStatements = (widthInstances - 1 to 0 by -1).map(i => s"${memPortPrefix}_dout_0_$i") + val catStmt = doutStatements.init.foldRight(doutStatements.last)((l: String, r: String) => s"cat($l, $r)") + s""" + node ${memPortPrefix}_dout_0 = $catStmt +""" + } + + output.append(s""" + ${memPortPrefix}_dout <= mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<$memWidth>("h0")) +""") + output.toString + } +} + +// Try different widths against a base memory width of 8. +class SplitWidth1024x128_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 128 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x64_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x32_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 8 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +// Try different widths against a base memory width of 16. +class SplitWidth1024x128_lib16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 128 + override lazy val libWidth = 16 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x64_lib16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val libWidth = 16 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x32_lib16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val libWidth = 16 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_lib16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 16 + + compileExecuteAndTest(mem, lib, v, output) +} + +// Try different widths against a base memory width of 8 but depth 512 instead of 1024. +class SplitWidth512x128_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(512) + override lazy val memWidth = 128 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth512x64_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(512) + override lazy val memWidth = 64 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth512x32_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(512) + override lazy val memWidth = 32 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth512x16_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(512) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth512x8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(512) + override lazy val memWidth = 8 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +// Try non-power of two widths against a base memory width of 8. +class SplitWidth1024x67_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 67 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x60_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 60 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x42_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 42 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x20_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 20 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x17_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 17 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x15_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 15 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x9_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 9 + override lazy val libWidth = 8 + + compileExecuteAndTest(mem, lib, v, output) +} + +// Try against a non-power of two base memory width. +class SplitWidth1024x64_mem11_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 64 + override lazy val libWidth = 11 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x33_mem11_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 33 + override lazy val libWidth = 11 + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_mem11_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 11 + + compileExecuteAndTest(mem, lib, v, output) +} + +// Masked RAM + +class SplitWidth1024x8_memGran_8_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 8 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_memGran_8_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_memGran_8_libGran_8_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(8) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x128_memGran_8_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 128 + override lazy val libWidth = 32 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_memGran_4_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(4) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_memGran_2_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(2) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_memGran_16_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(16) + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Non-masked mem, masked lib + +class SplitWidth1024x16_libGran_8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = Some(8) + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x16_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val libMaskGran: Option[Int] = Some(1) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Non-memMask and non-1 libMask + +class SplitWidth1024x16_memGran_8_libGran_2_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(2) + + compileExecuteAndTest(mem, lib, v, output) +} + +// Non-power of two memGran + +class SplitWidth1024x16_memGran_9_libGran_1_rw + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + override lazy val depth = BigInt(1024) + override lazy val memWidth = 16 + override lazy val libWidth = 8 + override lazy val memMaskGran: Option[Int] = Some(9) + override lazy val libMaskGran: Option[Int] = Some(1) + + (it should "be enabled when non-power of two masks are supported").is(pending) + //~ compile(mem, lib, v, false) + //~ execute(mem, lib, false, output) +} + +// Read enable + +class SplitWidth1024x32_readEnable_Lib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + import mdf.macrolib._ + + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val libWidth = 8 + + override def generateLibSRAM(): SRAMMacro = { + SRAMMacro( + name = lib_name, + width = libWidth, + depth = libDepth, + family = "1rw", + ports = Seq( + generateTestPort( + "lib", + Some(libWidth), + Some(libDepth), + maskGran = libMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateBody() = + """ + inst mem_0_0 of awesome_lib_mem + inst mem_0_1 of awesome_lib_mem + inst mem_0_2 of awesome_lib_mem + inst mem_0_3 of awesome_lib_mem + mem_0_0.lib_clk <= outer_clk + mem_0_0.lib_addr <= outer_addr + node outer_dout_0_0 = bits(mem_0_0.lib_dout, 7, 0) + mem_0_0.lib_din <= bits(outer_din, 7, 0) + mem_0_0.lib_read_en <= and(and(not(outer_write_en), UInt<1>("h1")), UInt<1>("h1")) + mem_0_0.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_1.lib_clk <= outer_clk + mem_0_1.lib_addr <= outer_addr + node outer_dout_0_1 = bits(mem_0_1.lib_dout, 7, 0) + mem_0_1.lib_din <= bits(outer_din, 15, 8) + mem_0_1.lib_read_en <= and(and(not(outer_write_en), UInt<1>("h1")), UInt<1>("h1")) + mem_0_1.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_2.lib_clk <= outer_clk + mem_0_2.lib_addr <= outer_addr + node outer_dout_0_2 = bits(mem_0_2.lib_dout, 7, 0) + mem_0_2.lib_din <= bits(outer_din, 23, 16) + mem_0_2.lib_read_en <= and(and(not(outer_write_en), UInt<1>("h1")), UInt<1>("h1")) + mem_0_2.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_3.lib_clk <= outer_clk + mem_0_3.lib_addr <= outer_addr + node outer_dout_0_3 = bits(mem_0_3.lib_dout, 7, 0) + mem_0_3.lib_din <= bits(outer_din, 31, 24) + mem_0_3.lib_read_en <= and(and(not(outer_write_en), UInt<1>("h1")), UInt<1>("h1")) + mem_0_3.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + node outer_dout_0 = cat(outer_dout_0_3, cat(outer_dout_0_2, cat(outer_dout_0_1, outer_dout_0_0))) + outer_dout <= mux(UInt<1>("h1"), outer_dout_0, UInt<32>("h0")) +""" + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x32_readEnable_Mem + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + import mdf.macrolib._ + + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val libWidth = 8 + + override def generateMemSRAM(): SRAMMacro = { + SRAMMacro( + name = mem_name, + width = memWidth, + depth = memDepth, + family = "1rw", + ports = Seq( + generateTestPort( + "outer", + Some(memWidth), + Some(memDepth), + maskGran = memMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + // No need to override body here due to the lack of a readEnable in the lib. + + compileExecuteAndTest(mem, lib, v, output) +} + +class SplitWidth1024x32_readEnable_LibMem + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator { + import mdf.macrolib._ + + override lazy val depth = BigInt(1024) + override lazy val memWidth = 32 + override lazy val libWidth = 8 + + override def generateLibSRAM(): SRAMMacro = { + SRAMMacro( + name = lib_name, + width = libWidth, + depth = libDepth, + family = "1rw", + ports = Seq( + generateTestPort( + "lib", + Some(libWidth), + Some(libDepth), + maskGran = libMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateMemSRAM(): SRAMMacro = { + SRAMMacro( + name = mem_name, + width = memWidth, + depth = memDepth, + family = "1rw", + ports = Seq( + generateTestPort( + "outer", + Some(memWidth), + Some(memDepth), + maskGran = memMaskGran, + write = true, + writeEnable = true, + read = true, + readEnable = true + ) + ) + ) + } + + override def generateBody() = + """ + inst mem_0_0 of awesome_lib_mem + inst mem_0_1 of awesome_lib_mem + inst mem_0_2 of awesome_lib_mem + inst mem_0_3 of awesome_lib_mem + mem_0_0.lib_clk <= outer_clk + mem_0_0.lib_addr <= outer_addr + node outer_dout_0_0 = bits(mem_0_0.lib_dout, 7, 0) + mem_0_0.lib_din <= bits(outer_din, 7, 0) + mem_0_0.lib_read_en <= and(outer_read_en, UInt<1>("h1")) + mem_0_0.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_1.lib_clk <= outer_clk + mem_0_1.lib_addr <= outer_addr + node outer_dout_0_1 = bits(mem_0_1.lib_dout, 7, 0) + mem_0_1.lib_din <= bits(outer_din, 15, 8) + mem_0_1.lib_read_en <= and(outer_read_en, UInt<1>("h1")) + mem_0_1.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_2.lib_clk <= outer_clk + mem_0_2.lib_addr <= outer_addr + node outer_dout_0_2 = bits(mem_0_2.lib_dout, 7, 0) + mem_0_2.lib_din <= bits(outer_din, 23, 16) + mem_0_2.lib_read_en <= and(outer_read_en, UInt<1>("h1")) + mem_0_2.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_3.lib_clk <= outer_clk + mem_0_3.lib_addr <= outer_addr + node outer_dout_0_3 = bits(mem_0_3.lib_dout, 7, 0) + mem_0_3.lib_din <= bits(outer_din, 31, 24) + mem_0_3.lib_read_en <= and(outer_read_en, UInt<1>("h1")) + mem_0_3.lib_write_en <= and(and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + node outer_dout_0 = cat(outer_dout_0_3, cat(outer_dout_0_2, cat(outer_dout_0_1, outer_dout_0_0))) + outer_dout <= mux(UInt<1>("h1"), outer_dout_0, UInt<32>("h0")) +""" + + compileExecuteAndTest(mem, lib, v, output) +} diff --git a/src/test/scala/barstools/macros/SpecificExamples.scala b/src/test/scala/barstools/macros/SpecificExamples.scala new file mode 100644 index 00000000..b0e2467f --- /dev/null +++ b/src/test/scala/barstools/macros/SpecificExamples.scala @@ -0,0 +1,1762 @@ +// See LICENSE for license details. +package barstools.macros + +import firrtl.FileUtils +import mdf.macrolib.{Constant, MacroExtraPort, SRAMMacro} + +// Specific one-off tests to run, not created by a generator. + +// Check that verilog actually gets generated. +// TODO: check the actual verilog's correctness? +class GenerateSomeVerilog extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator { + override lazy val width = 32 + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + + it should "execute fine" in { + compileExecuteAndTest(mem, lib, v, output) + } + + it should "generate non-empty verilog" in { + val verilog = FileUtils.getText(vPrefix + "/" + v) + verilog.isEmpty shouldBe false + } +} + +class WriteEnableTest extends MacroCompilerSpec with HasSRAMGenerator { + val mem = s"mem-WriteEnableTest.json" // mem. you want to create + val lib = s"lib-WriteEnableTest.json" // lib. of mems to create it + val v = s"WriteEnableTest.json" + + override val libPrefix = "src/test/resources" + + val memSRAMs: Seq[mdf.macrolib.Macro] = mdf.macrolib.Utils + .readMDFFromString(""" +[ { + "type" : "sram", + "name" : "cc_banks_0_ext", + "width" : 64, + "depth" : "4096", + "mux" : 1, + "ports" : [ { + "address port name" : "RW0_addr", + "address port polarity" : "active high", + "clock port name" : "RW0_clk", + "clock port polarity" : "positive edge", + "write enable port name" : "RW0_wmode", + "write enable port polarity" : "active high", + "chip enable port name" : "RW0_en", + "chip enable port polarity" : "active high", + "output port name" : "RW0_rdata", + "output port polarity" : "active high", + "input port name" : "RW0_wdata", + "input port polarity" : "active high" + } ], + "family" : "1rw" +} ] +""").getOrElse(Seq()) + + writeToMem(mem, memSRAMs) + + val output = + """ +circuit cc_banks_0_ext : + module cc_banks_0_ext : + input RW0_addr : UInt<12> + input RW0_clk : Clock + input RW0_wdata : UInt<64> + output RW0_rdata : UInt<64> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + + inst mem_0_0 of fake_mem + mem_0_0.clk <= RW0_clk + mem_0_0.addr <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.dataout, 63, 0) + mem_0_0.datain <= bits(RW0_wdata, 63, 0) + mem_0_0.ren <= and(and(not(RW0_wmode), RW0_en), UInt<1>("h1")) + mem_0_0.wen <= and(and(and(RW0_wmode, RW0_en), UInt<1>("h1")), UInt<1>("h1")) + node RW0_rdata_0 = RW0_rdata_0_0 + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<64>("h0")) + + extmodule fake_mem : + input addr : UInt<12> + input clk : Clock + input datain : UInt<64> + output dataout : UInt<64> + input ren : UInt<1> + input wen : UInt<1> + + defname = fake_mem +""" + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator { + val mem = s"mem-MaskPortTest.json" // mem. you want to create + val lib = s"lib-MaskPortTest.json" // lib. of mems to create it + val v = s"MaskPortTest.json" + + override val libPrefix = "src/test/resources" + + val memSRAMs: Seq[mdf.macrolib.Macro] = mdf.macrolib.Utils + .readMDFFromString(""" +[ { + "type" : "sram", + "name" : "cc_dir_ext", + "width" : 128, + "depth" : "512", + "mux" : 1, + "ports" : [ { + "address port name" : "RW0_addr", + "address port polarity" : "active high", + "clock port name" : "RW0_clk", + "clock port polarity" : "positive edge", + "write enable port name" : "RW0_wmode", + "write enable port polarity" : "active high", + "chip enable port name" : "RW0_en", + "chip enable port polarity" : "active high", + "output port name" : "RW0_rdata", + "output port polarity" : "active high", + "input port name" : "RW0_wdata", + "input port polarity" : "active high", + "mask port name" : "RW0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 16 + } ], + "family" : "1rw" +} ] +""").getOrElse(List()) + + writeToMem(mem, memSRAMs) + + val output = + """ +circuit cc_dir_ext : + module cc_dir_ext : + input RW0_addr : UInt<9> + input RW0_clk : Clock + input RW0_wdata : UInt<128> + output RW0_rdata : UInt<128> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + input RW0_wmask : UInt<8> + + inst mem_0_0 of fake_mem + inst mem_0_1 of fake_mem + mem_0_0.clk <= RW0_clk + mem_0_0.addr <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.dataout, 63, 0) + mem_0_0.datain <= bits(RW0_wdata, 63, 0) + mem_0_0.ren <= and(and(not(RW0_wmode), RW0_en), UInt<1>("h1")) + mem_0_0.mport <= not(cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 3, 3), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 2, 2), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 1, 1), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), cat(bits(RW0_wmask, 0, 0), bits(RW0_wmask, 0, 0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + mem_0_0.wen <= and(and(RW0_wmode, RW0_en), UInt<1>("h1")) + mem_0_1.clk <= RW0_clk + mem_0_1.addr <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.dataout, 63, 0) + mem_0_1.datain <= bits(RW0_wdata, 127, 64) + mem_0_1.ren <= and(and(not(RW0_wmode), RW0_en), UInt<1>("h1")) + mem_0_1.mport <= not(cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 7, 7), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 6, 6), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 5, 5), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), cat(bits(RW0_wmask, 4, 4), bits(RW0_wmask, 4, 4))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + mem_0_1.wen <= and(and(RW0_wmode, RW0_en), UInt<1>("h1")) + node RW0_rdata_0 = cat(RW0_rdata_0_1, RW0_rdata_0_0) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<128>("h0")) + + extmodule fake_mem : + input addr : UInt<9> + input clk : Clock + input datain : UInt<64> + output dataout : UInt<64> + input ren : UInt<1> + input wen : UInt<1> + input mport : UInt<64> + + defname = fake_mem +""" + + it should "compile, execute, and test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator { + val mem = s"mem-BOOMTest.json" + val lib = s"lib-BOOMTest.json" + val v = s"BOOMTest.v" + + override val libPrefix = "src/test/resources" + + val memSRAMs: Seq[mdf.macrolib.Macro] = mdf.macrolib.Utils + .readMDFFromString(""" +[ { + "type" : "sram", + "name" : "_T_182_ext", + "width" : 88, + "depth" : "64", + "ports" : [ { + "address port name" : "R0_addr", + "address port polarity" : "active high", + "clock port name" : "R0_clk", + "clock port polarity" : "active high", + "chip enable port name" : "R0_en", + "chip enable port polarity" : "active high", + "output port name" : "R0_data", + "output port polarity" : "active high" + }, { + "address port name" : "W0_addr", + "address port polarity" : "active high", + "clock port name" : "W0_clk", + "clock port polarity" : "active high", + "chip enable port name" : "W0_en", + "chip enable port polarity" : "active high", + "input port name" : "W0_data", + "input port polarity" : "active high", + "mask port name" : "W0_mask", + "mask port polarity" : "active high", + "mask granularity" : 22 + } ] +}, { + "type" : "sram", + "name" : "_T_84_ext", + "width" : 64, + "depth" : "512", + "ports" : [ { + "address port name" : "R0_addr", + "address port polarity" : "active high", + "clock port name" : "R0_clk", + "clock port polarity" : "active high", + "chip enable port name" : "R0_en", + "chip enable port polarity" : "active high", + "output port name" : "R0_data", + "output port polarity" : "active high" + }, { + "address port name" : "W0_addr", + "address port polarity" : "active high", + "clock port name" : "W0_clk", + "clock port polarity" : "active high", + "chip enable port name" : "W0_en", + "chip enable port polarity" : "active high", + "input port name" : "W0_data", + "input port polarity" : "active high", + "mask port name" : "W0_mask", + "mask port polarity" : "active high", + "mask granularity" : 64 + } ] +}, { + "type" : "sram", + "name" : "tag_array_ext", + "width" : 80, + "depth" : "64", + "ports" : [ { + "address port name" : "RW0_addr", + "address port polarity" : "active high", + "clock port name" : "RW0_clk", + "clock port polarity" : "active high", + "write enable port name" : "RW0_wmode", + "write enable port polarity" : "active high", + "chip enable port name" : "RW0_en", + "chip enable port polarity" : "active high", + "output port name" : "RW0_rdata", + "output port polarity" : "active high", + "input port name" : "RW0_wdata", + "input port polarity" : "active high", + "mask port name" : "RW0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 20 + } ] +}, { + "type" : "sram", + "name" : "_T_886_ext", + "width" : 64, + "depth" : "512", + "ports" : [ { + "address port name" : "RW0_addr", + "address port polarity" : "active high", + "clock port name" : "RW0_clk", + "clock port polarity" : "active high", + "write enable port name" : "RW0_wmode", + "write enable port polarity" : "active high", + "chip enable port name" : "RW0_en", + "chip enable port polarity" : "active high", + "output port name" : "RW0_rdata", + "output port polarity" : "active high", + "input port name" : "RW0_wdata", + "input port polarity" : "active high" + } ] +}, { + "type" : "sram", + "name" : "entries_info_ext", + "width" : 40, + "depth" : "24", + "ports" : [ { + "address port name" : "R0_addr", + "address port polarity" : "active high", + "clock port name" : "R0_clk", + "clock port polarity" : "active high", + "chip enable port name" : "R0_en", + "chip enable port polarity" : "active high", + "output port name" : "R0_data", + "output port polarity" : "active high" + }, { + "address port name" : "W0_addr", + "address port polarity" : "active high", + "clock port name" : "W0_clk", + "clock port polarity" : "active high", + "chip enable port name" : "W0_en", + "chip enable port polarity" : "active high", + "input port name" : "W0_data", + "input port polarity" : "active high" + } ] +}, { + "type" : "sram", + "name" : "smem_ext", + "width" : 32, + "depth" : "32", + "ports" : [ { + "address port name" : "RW0_addr", + "address port polarity" : "active high", + "clock port name" : "RW0_clk", + "clock port polarity" : "active high", + "write enable port name" : "RW0_wmode", + "write enable port polarity" : "active high", + "chip enable port name" : "RW0_en", + "chip enable port polarity" : "active high", + "output port name" : "RW0_rdata", + "output port polarity" : "active high", + "input port name" : "RW0_wdata", + "input port polarity" : "active high", + "mask port name" : "RW0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 1 + } ] +}, { + "type" : "sram", + "name" : "smem_0_ext", + "width" : 32, + "depth" : "64", + "ports" : [ { + "address port name" : "RW0_addr", + "address port polarity" : "active high", + "clock port name" : "RW0_clk", + "clock port polarity" : "active high", + "write enable port name" : "RW0_wmode", + "write enable port polarity" : "active high", + "chip enable port name" : "RW0_en", + "chip enable port polarity" : "active high", + "output port name" : "RW0_rdata", + "output port polarity" : "active high", + "input port name" : "RW0_wdata", + "input port polarity" : "active high", + "mask port name" : "RW0_wmask", + "mask port polarity" : "active high", + "mask granularity" : 1 + } ] +} ] +""").getOrElse(List()) + + writeToMem(mem, memSRAMs) + + val output = // TODO: check correctness... + """ +circuit smem_0_ext : + module _T_182_ext : + input R0_addr : UInt<6> + input R0_clk : Clock + output R0_data : UInt<88> + input R0_en : UInt<1> + input W0_addr : UInt<6> + input W0_clk : Clock + input W0_data : UInt<88> + input W0_en : UInt<1> + input W0_mask : UInt<4> + + node R0_addr_sel = bits(R0_addr, 5, 5) + reg R0_addr_sel_reg : UInt<1>, R0_clk with : + reset => (UInt<1>("h0"), R0_addr_sel_reg) + R0_addr_sel_reg <= mux(R0_en, R0_addr_sel, R0_addr_sel_reg) + node W0_addr_sel = bits(W0_addr, 5, 5) + inst mem_0_0 of my_sram_2rw_32x22 + inst mem_0_1 of my_sram_2rw_32x22 + inst mem_0_2 of my_sram_2rw_32x22 + inst mem_0_3 of my_sram_2rw_32x22 + mem_0_0.CE1 <= W0_clk + mem_0_0.A1 <= W0_addr + mem_0_0.I1 <= bits(W0_data, 21, 0) + mem_0_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_0.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_0.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_1.CE1 <= W0_clk + mem_0_1.A1 <= W0_addr + mem_0_1.I1 <= bits(W0_data, 43, 22) + mem_0_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_1.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 1, 1)), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_1.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_2.CE1 <= W0_clk + mem_0_2.A1 <= W0_addr + mem_0_2.I1 <= bits(W0_data, 65, 44) + mem_0_2.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_2.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 2, 2)), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_2.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_3.CE1 <= W0_clk + mem_0_3.A1 <= W0_addr + mem_0_3.I1 <= bits(W0_data, 87, 66) + mem_0_3.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_3.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 3, 3)), eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_3.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h0")))) + mem_0_0.CE2 <= R0_clk + mem_0_0.A2 <= R0_addr + node R0_data_0_0 = bits(mem_0_0.O2, 21, 0) + mem_0_0.I2 is invalid + mem_0_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_0.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_1.CE2 <= R0_clk + mem_0_1.A2 <= R0_addr + node R0_data_0_1 = bits(mem_0_1.O2, 21, 0) + mem_0_1.I2 is invalid + mem_0_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_1.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_2.CE2 <= R0_clk + mem_0_2.A2 <= R0_addr + node R0_data_0_2 = bits(mem_0_2.O2, 21, 0) + mem_0_2.I2 is invalid + mem_0_2.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_2.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_2.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_3.CE2 <= R0_clk + mem_0_3.A2 <= R0_addr + node R0_data_0_3 = bits(mem_0_3.O2, 21, 0) + mem_0_3.I2 is invalid + mem_0_3.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_3.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h0")))) + mem_0_3.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h0")))) + node R0_data_0 = cat(R0_data_0_3, cat(R0_data_0_2, cat(R0_data_0_1, R0_data_0_0))) + inst mem_1_0 of my_sram_2rw_32x22 + inst mem_1_1 of my_sram_2rw_32x22 + inst mem_1_2 of my_sram_2rw_32x22 + inst mem_1_3 of my_sram_2rw_32x22 + mem_1_0.CE1 <= W0_clk + mem_1_0.A1 <= W0_addr + mem_1_0.I1 <= bits(W0_data, 21, 0) + mem_1_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_0.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_0.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_1.CE1 <= W0_clk + mem_1_1.A1 <= W0_addr + mem_1_1.I1 <= bits(W0_data, 43, 22) + mem_1_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_1.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 1, 1)), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_1.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_2.CE1 <= W0_clk + mem_1_2.A1 <= W0_addr + mem_1_2.I1 <= bits(W0_data, 65, 44) + mem_1_2.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_2.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 2, 2)), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_2.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_3.CE1 <= W0_clk + mem_1_3.A1 <= W0_addr + mem_1_3.I1 <= bits(W0_data, 87, 66) + mem_1_3.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_3.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 3, 3)), eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_3.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<1>("h1")))) + mem_1_0.CE2 <= R0_clk + mem_1_0.A2 <= R0_addr + node R0_data_1_0 = bits(mem_1_0.O2, 21, 0) + mem_1_0.I2 is invalid + mem_1_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_0.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_1.CE2 <= R0_clk + mem_1_1.A2 <= R0_addr + node R0_data_1_1 = bits(mem_1_1.O2, 21, 0) + mem_1_1.I2 is invalid + mem_1_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_1.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_2.CE2 <= R0_clk + mem_1_2.A2 <= R0_addr + node R0_data_1_2 = bits(mem_1_2.O2, 21, 0) + mem_1_2.I2 is invalid + mem_1_2.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_2.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_2.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_3.CE2 <= R0_clk + mem_1_3.A2 <= R0_addr + node R0_data_1_3 = bits(mem_1_3.O2, 21, 0) + mem_1_3.I2 is invalid + mem_1_3.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_3.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<1>("h1")))) + mem_1_3.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<1>("h1")))) + node R0_data_1 = cat(R0_data_1_3, cat(R0_data_1_2, cat(R0_data_1_1, R0_data_1_0))) + R0_data <= mux(eq(R0_addr_sel_reg, UInt<1>("h0")), R0_data_0, mux(eq(R0_addr_sel_reg, UInt<1>("h1")), R0_data_1, UInt<88>("h0"))) + + + module _T_84_ext : + input R0_addr : UInt<9> + input R0_clk : Clock + output R0_data : UInt<64> + input R0_en : UInt<1> + input W0_addr : UInt<9> + input W0_clk : Clock + input W0_data : UInt<64> + input W0_en : UInt<1> + input W0_mask : UInt<1> + + node R0_addr_sel = bits(R0_addr, 8, 7) + reg R0_addr_sel_reg : UInt<2>, R0_clk with : + reset => (UInt<1>("h0"), R0_addr_sel_reg) + R0_addr_sel_reg <= mux(R0_en, R0_addr_sel, R0_addr_sel_reg) + node W0_addr_sel = bits(W0_addr, 8, 7) + inst mem_0_0 of my_sram_2rw_128x32 + inst mem_0_1 of my_sram_2rw_128x32 + mem_0_0.CE1 <= W0_clk + mem_0_0.A1 <= W0_addr + mem_0_0.I1 <= bits(W0_data, 31, 0) + mem_0_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h0")))) + mem_0_0.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h0")))) + mem_0_0.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h0")))) + mem_0_1.CE1 <= W0_clk + mem_0_1.A1 <= W0_addr + mem_0_1.I1 <= bits(W0_data, 63, 32) + mem_0_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h0")))) + mem_0_1.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h0")))) + mem_0_1.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h0")))) + mem_0_0.CE2 <= R0_clk + mem_0_0.A2 <= R0_addr + node R0_data_0_0 = bits(mem_0_0.O2, 31, 0) + mem_0_0.I2 is invalid + mem_0_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h0")))) + mem_0_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h0")))) + mem_0_0.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h0")))) + mem_0_1.CE2 <= R0_clk + mem_0_1.A2 <= R0_addr + node R0_data_0_1 = bits(mem_0_1.O2, 31, 0) + mem_0_1.I2 is invalid + mem_0_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h0")))) + mem_0_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h0")))) + mem_0_1.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h0")))) + node R0_data_0 = cat(R0_data_0_1, R0_data_0_0) + inst mem_1_0 of my_sram_2rw_128x32 + inst mem_1_1 of my_sram_2rw_128x32 + mem_1_0.CE1 <= W0_clk + mem_1_0.A1 <= W0_addr + mem_1_0.I1 <= bits(W0_data, 31, 0) + mem_1_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h1")))) + mem_1_0.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h1")))) + mem_1_0.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h1")))) + mem_1_1.CE1 <= W0_clk + mem_1_1.A1 <= W0_addr + mem_1_1.I1 <= bits(W0_data, 63, 32) + mem_1_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h1")))) + mem_1_1.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h1")))) + mem_1_1.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h1")))) + mem_1_0.CE2 <= R0_clk + mem_1_0.A2 <= R0_addr + node R0_data_1_0 = bits(mem_1_0.O2, 31, 0) + mem_1_0.I2 is invalid + mem_1_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h1")))) + mem_1_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h1")))) + mem_1_0.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h1")))) + mem_1_1.CE2 <= R0_clk + mem_1_1.A2 <= R0_addr + node R0_data_1_1 = bits(mem_1_1.O2, 31, 0) + mem_1_1.I2 is invalid + mem_1_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h1")))) + mem_1_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h1")))) + mem_1_1.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h1")))) + node R0_data_1 = cat(R0_data_1_1, R0_data_1_0) + inst mem_2_0 of my_sram_2rw_128x32 + inst mem_2_1 of my_sram_2rw_128x32 + mem_2_0.CE1 <= W0_clk + mem_2_0.A1 <= W0_addr + mem_2_0.I1 <= bits(W0_data, 31, 0) + mem_2_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h2")))) + mem_2_0.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h2")))) + mem_2_0.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h2")))) + mem_2_1.CE1 <= W0_clk + mem_2_1.A1 <= W0_addr + mem_2_1.I1 <= bits(W0_data, 63, 32) + mem_2_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h2")))) + mem_2_1.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h2")))) + mem_2_1.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h2")))) + mem_2_0.CE2 <= R0_clk + mem_2_0.A2 <= R0_addr + node R0_data_2_0 = bits(mem_2_0.O2, 31, 0) + mem_2_0.I2 is invalid + mem_2_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h2")))) + mem_2_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h2")))) + mem_2_0.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h2")))) + mem_2_1.CE2 <= R0_clk + mem_2_1.A2 <= R0_addr + node R0_data_2_1 = bits(mem_2_1.O2, 31, 0) + mem_2_1.I2 is invalid + mem_2_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h2")))) + mem_2_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h2")))) + mem_2_1.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h2")))) + node R0_data_2 = cat(R0_data_2_1, R0_data_2_0) + inst mem_3_0 of my_sram_2rw_128x32 + inst mem_3_1 of my_sram_2rw_128x32 + mem_3_0.CE1 <= W0_clk + mem_3_0.A1 <= W0_addr + mem_3_0.I1 <= bits(W0_data, 31, 0) + mem_3_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h3")))) + mem_3_0.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h3")))) + mem_3_0.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h3")))) + mem_3_1.CE1 <= W0_clk + mem_3_1.A1 <= W0_addr + mem_3_1.I1 <= bits(W0_data, 63, 32) + mem_3_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), eq(W0_addr_sel, UInt<2>("h3")))) + mem_3_1.WEB1 <= not(and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), eq(W0_addr_sel, UInt<2>("h3")))) + mem_3_1.CSB1 <= not(and(W0_en, eq(W0_addr_sel, UInt<2>("h3")))) + mem_3_0.CE2 <= R0_clk + mem_3_0.A2 <= R0_addr + node R0_data_3_0 = bits(mem_3_0.O2, 31, 0) + mem_3_0.I2 is invalid + mem_3_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h3")))) + mem_3_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h3")))) + mem_3_0.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h3")))) + mem_3_1.CE2 <= R0_clk + mem_3_1.A2 <= R0_addr + node R0_data_3_1 = bits(mem_3_1.O2, 31, 0) + mem_3_1.I2 is invalid + mem_3_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), eq(R0_addr_sel, UInt<2>("h3")))) + mem_3_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), eq(R0_addr_sel, UInt<2>("h3")))) + mem_3_1.CSB2 <= not(and(R0_en, eq(R0_addr_sel, UInt<2>("h3")))) + node R0_data_3 = cat(R0_data_3_1, R0_data_3_0) + R0_data <= mux(eq(R0_addr_sel_reg, UInt<2>("h0")), R0_data_0, mux(eq(R0_addr_sel_reg, UInt<2>("h1")), R0_data_1, mux(eq(R0_addr_sel_reg, UInt<2>("h2")), R0_data_2, mux(eq(R0_addr_sel_reg, UInt<2>("h3")), R0_data_3, UInt<64>("h0"))))) + + extmodule my_sram_2rw_128x32 : + input A1 : UInt<7> + input CE1 : Clock + input I1 : UInt<32> + output O1 : UInt<32> + input CSB1 : UInt<1> + input OEB1 : UInt<1> + input WEB1 : UInt<1> + input A2 : UInt<7> + input CE2 : Clock + input I2 : UInt<32> + output O2 : UInt<32> + input CSB2 : UInt<1> + input OEB2 : UInt<1> + input WEB2 : UInt<1> + + defname = my_sram_2rw_128x32 + + + module tag_array_ext : + input RW0_addr : UInt<6> + input RW0_clk : Clock + input RW0_wdata : UInt<80> + output RW0_rdata : UInt<80> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + input RW0_wmask : UInt<4> + + inst mem_0_0 of my_sram_1rw_64x32 + inst mem_0_1 of my_sram_1rw_64x32 + inst mem_0_2 of my_sram_1rw_64x32 + inst mem_0_3 of my_sram_1rw_64x32 + mem_0_0.CE <= RW0_clk + mem_0_0.A <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.O, 19, 0) + mem_0_0.I <= bits(RW0_wdata, 19, 0) + mem_0_0.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_0.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1"))) + mem_0_0.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_1.CE <= RW0_clk + mem_0_1.A <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.O, 19, 0) + mem_0_1.I <= bits(RW0_wdata, 39, 20) + mem_0_1.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_1.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))) + mem_0_1.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_2.CE <= RW0_clk + mem_0_2.A <= RW0_addr + node RW0_rdata_0_2 = bits(mem_0_2.O, 19, 0) + mem_0_2.I <= bits(RW0_wdata, 59, 40) + mem_0_2.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_2.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))) + mem_0_2.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_3.CE <= RW0_clk + mem_0_3.A <= RW0_addr + node RW0_rdata_0_3 = bits(mem_0_3.O, 19, 0) + mem_0_3.I <= bits(RW0_wdata, 79, 60) + mem_0_3.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_3.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))) + mem_0_3.CSB <= not(and(RW0_en, UInt<1>("h1"))) + node RW0_rdata_0 = cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0))) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<80>("h0")) + + extmodule my_sram_1rw_64x32 : + input A : UInt<6> + input CE : Clock + input I : UInt<32> + output O : UInt<32> + input CSB : UInt<1> + input OEB : UInt<1> + input WEB : UInt<1> + + defname = my_sram_1rw_64x32 + + + module _T_886_ext : + input RW0_addr : UInt<9> + input RW0_clk : Clock + input RW0_wdata : UInt<64> + output RW0_rdata : UInt<64> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + + inst mem_0_0 of my_sram_1rw_512x32 + inst mem_0_1 of my_sram_1rw_512x32 + mem_0_0.CE <= RW0_clk + mem_0_0.A <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.O, 31, 0) + mem_0_0.I <= bits(RW0_wdata, 31, 0) + mem_0_0.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_0.WEB <= not(and(and(RW0_wmode, UInt<1>("h1")), UInt<1>("h1"))) + mem_0_0.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_1.CE <= RW0_clk + mem_0_1.A <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.O, 31, 0) + mem_0_1.I <= bits(RW0_wdata, 63, 32) + mem_0_1.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_1.WEB <= not(and(and(RW0_wmode, UInt<1>("h1")), UInt<1>("h1"))) + mem_0_1.CSB <= not(and(RW0_en, UInt<1>("h1"))) + node RW0_rdata_0 = cat(RW0_rdata_0_1, RW0_rdata_0_0) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<64>("h0")) + + extmodule my_sram_1rw_512x32 : + input A : UInt<9> + input CE : Clock + input I : UInt<32> + output O : UInt<32> + input CSB : UInt<1> + input OEB : UInt<1> + input WEB : UInt<1> + + defname = my_sram_1rw_512x32 + + + module entries_info_ext : + input R0_addr : UInt<5> + input R0_clk : Clock + output R0_data : UInt<40> + input R0_en : UInt<1> + input W0_addr : UInt<5> + input W0_clk : Clock + input W0_data : UInt<40> + input W0_en : UInt<1> + + inst mem_0_0 of my_sram_2rw_32x22 + inst mem_0_1 of my_sram_2rw_32x22 + mem_0_0.CE1 <= W0_clk + mem_0_0.A1 <= W0_addr + mem_0_0.I1 <= bits(W0_data, 21, 0) + mem_0_0.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), UInt<1>("h1"))) + mem_0_0.WEB1 <= not(and(and(UInt<1>("h1"), UInt<1>("h1")), UInt<1>("h1"))) + mem_0_0.CSB1 <= not(and(W0_en, UInt<1>("h1"))) + mem_0_1.CE1 <= W0_clk + mem_0_1.A1 <= W0_addr + mem_0_1.I1 <= bits(W0_data, 39, 22) + mem_0_1.OEB1 <= not(and(and(not(UInt<1>("h1")), W0_en), UInt<1>("h1"))) + mem_0_1.WEB1 <= not(and(and(UInt<1>("h1"), UInt<1>("h1")), UInt<1>("h1"))) + mem_0_1.CSB1 <= not(and(W0_en, UInt<1>("h1"))) + mem_0_0.CE2 <= R0_clk + mem_0_0.A2 <= R0_addr + node R0_data_0_0 = bits(mem_0_0.O2, 21, 0) + mem_0_0.I2 is invalid + mem_0_0.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), UInt<1>("h1"))) + mem_0_0.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), UInt<1>("h1"))) + mem_0_0.CSB2 <= not(and(R0_en, UInt<1>("h1"))) + mem_0_1.CE2 <= R0_clk + mem_0_1.A2 <= R0_addr + node R0_data_0_1 = bits(mem_0_1.O2, 17, 0) + mem_0_1.I2 is invalid + mem_0_1.OEB2 <= not(and(and(not(UInt<1>("h0")), R0_en), UInt<1>("h1"))) + mem_0_1.WEB2 <= not(and(and(UInt<1>("h0"), UInt<1>("h1")), UInt<1>("h1"))) + mem_0_1.CSB2 <= not(and(R0_en, UInt<1>("h1"))) + node R0_data_0 = cat(R0_data_0_1, R0_data_0_0) + R0_data <= mux(UInt<1>("h1"), R0_data_0, UInt<40>("h0")) + + extmodule my_sram_2rw_32x22 : + input A1 : UInt<5> + input CE1 : Clock + input I1 : UInt<22> + output O1 : UInt<22> + input CSB1 : UInt<1> + input OEB1 : UInt<1> + input WEB1 : UInt<1> + input A2 : UInt<5> + input CE2 : Clock + input I2 : UInt<22> + output O2 : UInt<22> + input CSB2 : UInt<1> + input OEB2 : UInt<1> + input WEB2 : UInt<1> + + defname = my_sram_2rw_32x22 + + + module smem_ext : + input RW0_addr : UInt<5> + input RW0_clk : Clock + input RW0_wdata : UInt<32> + output RW0_rdata : UInt<32> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + input RW0_wmask : UInt<32> + + inst mem_0_0 of my_sram_1rw_64x8 + inst mem_0_1 of my_sram_1rw_64x8 + inst mem_0_2 of my_sram_1rw_64x8 + inst mem_0_3 of my_sram_1rw_64x8 + inst mem_0_4 of my_sram_1rw_64x8 + inst mem_0_5 of my_sram_1rw_64x8 + inst mem_0_6 of my_sram_1rw_64x8 + inst mem_0_7 of my_sram_1rw_64x8 + inst mem_0_8 of my_sram_1rw_64x8 + inst mem_0_9 of my_sram_1rw_64x8 + inst mem_0_10 of my_sram_1rw_64x8 + inst mem_0_11 of my_sram_1rw_64x8 + inst mem_0_12 of my_sram_1rw_64x8 + inst mem_0_13 of my_sram_1rw_64x8 + inst mem_0_14 of my_sram_1rw_64x8 + inst mem_0_15 of my_sram_1rw_64x8 + inst mem_0_16 of my_sram_1rw_64x8 + inst mem_0_17 of my_sram_1rw_64x8 + inst mem_0_18 of my_sram_1rw_64x8 + inst mem_0_19 of my_sram_1rw_64x8 + inst mem_0_20 of my_sram_1rw_64x8 + inst mem_0_21 of my_sram_1rw_64x8 + inst mem_0_22 of my_sram_1rw_64x8 + inst mem_0_23 of my_sram_1rw_64x8 + inst mem_0_24 of my_sram_1rw_64x8 + inst mem_0_25 of my_sram_1rw_64x8 + inst mem_0_26 of my_sram_1rw_64x8 + inst mem_0_27 of my_sram_1rw_64x8 + inst mem_0_28 of my_sram_1rw_64x8 + inst mem_0_29 of my_sram_1rw_64x8 + inst mem_0_30 of my_sram_1rw_64x8 + inst mem_0_31 of my_sram_1rw_64x8 + mem_0_0.CE <= RW0_clk + mem_0_0.A <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.O, 0, 0) + mem_0_0.I <= bits(RW0_wdata, 0, 0) + mem_0_0.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_0.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1"))) + mem_0_0.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_1.CE <= RW0_clk + mem_0_1.A <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.O, 0, 0) + mem_0_1.I <= bits(RW0_wdata, 1, 1) + mem_0_1.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_1.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))) + mem_0_1.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_2.CE <= RW0_clk + mem_0_2.A <= RW0_addr + node RW0_rdata_0_2 = bits(mem_0_2.O, 0, 0) + mem_0_2.I <= bits(RW0_wdata, 2, 2) + mem_0_2.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_2.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))) + mem_0_2.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_3.CE <= RW0_clk + mem_0_3.A <= RW0_addr + node RW0_rdata_0_3 = bits(mem_0_3.O, 0, 0) + mem_0_3.I <= bits(RW0_wdata, 3, 3) + mem_0_3.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_3.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))) + mem_0_3.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_4.CE <= RW0_clk + mem_0_4.A <= RW0_addr + node RW0_rdata_0_4 = bits(mem_0_4.O, 0, 0) + mem_0_4.I <= bits(RW0_wdata, 4, 4) + mem_0_4.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_4.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 4, 4)), UInt<1>("h1"))) + mem_0_4.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_5.CE <= RW0_clk + mem_0_5.A <= RW0_addr + node RW0_rdata_0_5 = bits(mem_0_5.O, 0, 0) + mem_0_5.I <= bits(RW0_wdata, 5, 5) + mem_0_5.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_5.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 5, 5)), UInt<1>("h1"))) + mem_0_5.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_6.CE <= RW0_clk + mem_0_6.A <= RW0_addr + node RW0_rdata_0_6 = bits(mem_0_6.O, 0, 0) + mem_0_6.I <= bits(RW0_wdata, 6, 6) + mem_0_6.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_6.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 6, 6)), UInt<1>("h1"))) + mem_0_6.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_7.CE <= RW0_clk + mem_0_7.A <= RW0_addr + node RW0_rdata_0_7 = bits(mem_0_7.O, 0, 0) + mem_0_7.I <= bits(RW0_wdata, 7, 7) + mem_0_7.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_7.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 7, 7)), UInt<1>("h1"))) + mem_0_7.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_8.CE <= RW0_clk + mem_0_8.A <= RW0_addr + node RW0_rdata_0_8 = bits(mem_0_8.O, 0, 0) + mem_0_8.I <= bits(RW0_wdata, 8, 8) + mem_0_8.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_8.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 8, 8)), UInt<1>("h1"))) + mem_0_8.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_9.CE <= RW0_clk + mem_0_9.A <= RW0_addr + node RW0_rdata_0_9 = bits(mem_0_9.O, 0, 0) + mem_0_9.I <= bits(RW0_wdata, 9, 9) + mem_0_9.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_9.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 9, 9)), UInt<1>("h1"))) + mem_0_9.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_10.CE <= RW0_clk + mem_0_10.A <= RW0_addr + node RW0_rdata_0_10 = bits(mem_0_10.O, 0, 0) + mem_0_10.I <= bits(RW0_wdata, 10, 10) + mem_0_10.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_10.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 10, 10)), UInt<1>("h1"))) + mem_0_10.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_11.CE <= RW0_clk + mem_0_11.A <= RW0_addr + node RW0_rdata_0_11 = bits(mem_0_11.O, 0, 0) + mem_0_11.I <= bits(RW0_wdata, 11, 11) + mem_0_11.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_11.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 11, 11)), UInt<1>("h1"))) + mem_0_11.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_12.CE <= RW0_clk + mem_0_12.A <= RW0_addr + node RW0_rdata_0_12 = bits(mem_0_12.O, 0, 0) + mem_0_12.I <= bits(RW0_wdata, 12, 12) + mem_0_12.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_12.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 12, 12)), UInt<1>("h1"))) + mem_0_12.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_13.CE <= RW0_clk + mem_0_13.A <= RW0_addr + node RW0_rdata_0_13 = bits(mem_0_13.O, 0, 0) + mem_0_13.I <= bits(RW0_wdata, 13, 13) + mem_0_13.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_13.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 13, 13)), UInt<1>("h1"))) + mem_0_13.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_14.CE <= RW0_clk + mem_0_14.A <= RW0_addr + node RW0_rdata_0_14 = bits(mem_0_14.O, 0, 0) + mem_0_14.I <= bits(RW0_wdata, 14, 14) + mem_0_14.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_14.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 14, 14)), UInt<1>("h1"))) + mem_0_14.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_15.CE <= RW0_clk + mem_0_15.A <= RW0_addr + node RW0_rdata_0_15 = bits(mem_0_15.O, 0, 0) + mem_0_15.I <= bits(RW0_wdata, 15, 15) + mem_0_15.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_15.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 15, 15)), UInt<1>("h1"))) + mem_0_15.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_16.CE <= RW0_clk + mem_0_16.A <= RW0_addr + node RW0_rdata_0_16 = bits(mem_0_16.O, 0, 0) + mem_0_16.I <= bits(RW0_wdata, 16, 16) + mem_0_16.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_16.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 16, 16)), UInt<1>("h1"))) + mem_0_16.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_17.CE <= RW0_clk + mem_0_17.A <= RW0_addr + node RW0_rdata_0_17 = bits(mem_0_17.O, 0, 0) + mem_0_17.I <= bits(RW0_wdata, 17, 17) + mem_0_17.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_17.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 17, 17)), UInt<1>("h1"))) + mem_0_17.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_18.CE <= RW0_clk + mem_0_18.A <= RW0_addr + node RW0_rdata_0_18 = bits(mem_0_18.O, 0, 0) + mem_0_18.I <= bits(RW0_wdata, 18, 18) + mem_0_18.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_18.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 18, 18)), UInt<1>("h1"))) + mem_0_18.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_19.CE <= RW0_clk + mem_0_19.A <= RW0_addr + node RW0_rdata_0_19 = bits(mem_0_19.O, 0, 0) + mem_0_19.I <= bits(RW0_wdata, 19, 19) + mem_0_19.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_19.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 19, 19)), UInt<1>("h1"))) + mem_0_19.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_20.CE <= RW0_clk + mem_0_20.A <= RW0_addr + node RW0_rdata_0_20 = bits(mem_0_20.O, 0, 0) + mem_0_20.I <= bits(RW0_wdata, 20, 20) + mem_0_20.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_20.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 20, 20)), UInt<1>("h1"))) + mem_0_20.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_21.CE <= RW0_clk + mem_0_21.A <= RW0_addr + node RW0_rdata_0_21 = bits(mem_0_21.O, 0, 0) + mem_0_21.I <= bits(RW0_wdata, 21, 21) + mem_0_21.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_21.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 21, 21)), UInt<1>("h1"))) + mem_0_21.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_22.CE <= RW0_clk + mem_0_22.A <= RW0_addr + node RW0_rdata_0_22 = bits(mem_0_22.O, 0, 0) + mem_0_22.I <= bits(RW0_wdata, 22, 22) + mem_0_22.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_22.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 22, 22)), UInt<1>("h1"))) + mem_0_22.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_23.CE <= RW0_clk + mem_0_23.A <= RW0_addr + node RW0_rdata_0_23 = bits(mem_0_23.O, 0, 0) + mem_0_23.I <= bits(RW0_wdata, 23, 23) + mem_0_23.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_23.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 23, 23)), UInt<1>("h1"))) + mem_0_23.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_24.CE <= RW0_clk + mem_0_24.A <= RW0_addr + node RW0_rdata_0_24 = bits(mem_0_24.O, 0, 0) + mem_0_24.I <= bits(RW0_wdata, 24, 24) + mem_0_24.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_24.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 24, 24)), UInt<1>("h1"))) + mem_0_24.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_25.CE <= RW0_clk + mem_0_25.A <= RW0_addr + node RW0_rdata_0_25 = bits(mem_0_25.O, 0, 0) + mem_0_25.I <= bits(RW0_wdata, 25, 25) + mem_0_25.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_25.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 25, 25)), UInt<1>("h1"))) + mem_0_25.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_26.CE <= RW0_clk + mem_0_26.A <= RW0_addr + node RW0_rdata_0_26 = bits(mem_0_26.O, 0, 0) + mem_0_26.I <= bits(RW0_wdata, 26, 26) + mem_0_26.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_26.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 26, 26)), UInt<1>("h1"))) + mem_0_26.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_27.CE <= RW0_clk + mem_0_27.A <= RW0_addr + node RW0_rdata_0_27 = bits(mem_0_27.O, 0, 0) + mem_0_27.I <= bits(RW0_wdata, 27, 27) + mem_0_27.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_27.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 27, 27)), UInt<1>("h1"))) + mem_0_27.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_28.CE <= RW0_clk + mem_0_28.A <= RW0_addr + node RW0_rdata_0_28 = bits(mem_0_28.O, 0, 0) + mem_0_28.I <= bits(RW0_wdata, 28, 28) + mem_0_28.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_28.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 28, 28)), UInt<1>("h1"))) + mem_0_28.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_29.CE <= RW0_clk + mem_0_29.A <= RW0_addr + node RW0_rdata_0_29 = bits(mem_0_29.O, 0, 0) + mem_0_29.I <= bits(RW0_wdata, 29, 29) + mem_0_29.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_29.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 29, 29)), UInt<1>("h1"))) + mem_0_29.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_30.CE <= RW0_clk + mem_0_30.A <= RW0_addr + node RW0_rdata_0_30 = bits(mem_0_30.O, 0, 0) + mem_0_30.I <= bits(RW0_wdata, 30, 30) + mem_0_30.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_30.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 30, 30)), UInt<1>("h1"))) + mem_0_30.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_31.CE <= RW0_clk + mem_0_31.A <= RW0_addr + node RW0_rdata_0_31 = bits(mem_0_31.O, 0, 0) + mem_0_31.I <= bits(RW0_wdata, 31, 31) + mem_0_31.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_31.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 31, 31)), UInt<1>("h1"))) + mem_0_31.CSB <= not(and(RW0_en, UInt<1>("h1"))) + node RW0_rdata_0 = cat(RW0_rdata_0_31, cat(RW0_rdata_0_30, cat(RW0_rdata_0_29, cat(RW0_rdata_0_28, cat(RW0_rdata_0_27, cat(RW0_rdata_0_26, cat(RW0_rdata_0_25, cat(RW0_rdata_0_24, cat(RW0_rdata_0_23, cat(RW0_rdata_0_22, cat(RW0_rdata_0_21, cat(RW0_rdata_0_20, cat(RW0_rdata_0_19, cat(RW0_rdata_0_18, cat(RW0_rdata_0_17, cat(RW0_rdata_0_16, cat(RW0_rdata_0_15, cat(RW0_rdata_0_14, cat(RW0_rdata_0_13, cat(RW0_rdata_0_12, cat(RW0_rdata_0_11, cat(RW0_rdata_0_10, cat(RW0_rdata_0_9, cat(RW0_rdata_0_8, cat(RW0_rdata_0_7, cat(RW0_rdata_0_6, cat(RW0_rdata_0_5, cat(RW0_rdata_0_4, cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0))))))))))))))))))))))))))))))) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<32>("h0")) + + module smem_0_ext : + input RW0_addr : UInt<6> + input RW0_clk : Clock + input RW0_wdata : UInt<32> + output RW0_rdata : UInt<32> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + input RW0_wmask : UInt<32> + + inst mem_0_0 of my_sram_1rw_64x8 + inst mem_0_1 of my_sram_1rw_64x8 + inst mem_0_2 of my_sram_1rw_64x8 + inst mem_0_3 of my_sram_1rw_64x8 + inst mem_0_4 of my_sram_1rw_64x8 + inst mem_0_5 of my_sram_1rw_64x8 + inst mem_0_6 of my_sram_1rw_64x8 + inst mem_0_7 of my_sram_1rw_64x8 + inst mem_0_8 of my_sram_1rw_64x8 + inst mem_0_9 of my_sram_1rw_64x8 + inst mem_0_10 of my_sram_1rw_64x8 + inst mem_0_11 of my_sram_1rw_64x8 + inst mem_0_12 of my_sram_1rw_64x8 + inst mem_0_13 of my_sram_1rw_64x8 + inst mem_0_14 of my_sram_1rw_64x8 + inst mem_0_15 of my_sram_1rw_64x8 + inst mem_0_16 of my_sram_1rw_64x8 + inst mem_0_17 of my_sram_1rw_64x8 + inst mem_0_18 of my_sram_1rw_64x8 + inst mem_0_19 of my_sram_1rw_64x8 + inst mem_0_20 of my_sram_1rw_64x8 + inst mem_0_21 of my_sram_1rw_64x8 + inst mem_0_22 of my_sram_1rw_64x8 + inst mem_0_23 of my_sram_1rw_64x8 + inst mem_0_24 of my_sram_1rw_64x8 + inst mem_0_25 of my_sram_1rw_64x8 + inst mem_0_26 of my_sram_1rw_64x8 + inst mem_0_27 of my_sram_1rw_64x8 + inst mem_0_28 of my_sram_1rw_64x8 + inst mem_0_29 of my_sram_1rw_64x8 + inst mem_0_30 of my_sram_1rw_64x8 + inst mem_0_31 of my_sram_1rw_64x8 + mem_0_0.CE <= RW0_clk + mem_0_0.A <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.O, 0, 0) + mem_0_0.I <= bits(RW0_wdata, 0, 0) + mem_0_0.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_0.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1"))) + mem_0_0.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_1.CE <= RW0_clk + mem_0_1.A <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.O, 0, 0) + mem_0_1.I <= bits(RW0_wdata, 1, 1) + mem_0_1.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_1.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))) + mem_0_1.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_2.CE <= RW0_clk + mem_0_2.A <= RW0_addr + node RW0_rdata_0_2 = bits(mem_0_2.O, 0, 0) + mem_0_2.I <= bits(RW0_wdata, 2, 2) + mem_0_2.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_2.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))) + mem_0_2.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_3.CE <= RW0_clk + mem_0_3.A <= RW0_addr + node RW0_rdata_0_3 = bits(mem_0_3.O, 0, 0) + mem_0_3.I <= bits(RW0_wdata, 3, 3) + mem_0_3.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_3.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))) + mem_0_3.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_4.CE <= RW0_clk + mem_0_4.A <= RW0_addr + node RW0_rdata_0_4 = bits(mem_0_4.O, 0, 0) + mem_0_4.I <= bits(RW0_wdata, 4, 4) + mem_0_4.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_4.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 4, 4)), UInt<1>("h1"))) + mem_0_4.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_5.CE <= RW0_clk + mem_0_5.A <= RW0_addr + node RW0_rdata_0_5 = bits(mem_0_5.O, 0, 0) + mem_0_5.I <= bits(RW0_wdata, 5, 5) + mem_0_5.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_5.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 5, 5)), UInt<1>("h1"))) + mem_0_5.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_6.CE <= RW0_clk + mem_0_6.A <= RW0_addr + node RW0_rdata_0_6 = bits(mem_0_6.O, 0, 0) + mem_0_6.I <= bits(RW0_wdata, 6, 6) + mem_0_6.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_6.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 6, 6)), UInt<1>("h1"))) + mem_0_6.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_7.CE <= RW0_clk + mem_0_7.A <= RW0_addr + node RW0_rdata_0_7 = bits(mem_0_7.O, 0, 0) + mem_0_7.I <= bits(RW0_wdata, 7, 7) + mem_0_7.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_7.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 7, 7)), UInt<1>("h1"))) + mem_0_7.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_8.CE <= RW0_clk + mem_0_8.A <= RW0_addr + node RW0_rdata_0_8 = bits(mem_0_8.O, 0, 0) + mem_0_8.I <= bits(RW0_wdata, 8, 8) + mem_0_8.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_8.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 8, 8)), UInt<1>("h1"))) + mem_0_8.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_9.CE <= RW0_clk + mem_0_9.A <= RW0_addr + node RW0_rdata_0_9 = bits(mem_0_9.O, 0, 0) + mem_0_9.I <= bits(RW0_wdata, 9, 9) + mem_0_9.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_9.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 9, 9)), UInt<1>("h1"))) + mem_0_9.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_10.CE <= RW0_clk + mem_0_10.A <= RW0_addr + node RW0_rdata_0_10 = bits(mem_0_10.O, 0, 0) + mem_0_10.I <= bits(RW0_wdata, 10, 10) + mem_0_10.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_10.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 10, 10)), UInt<1>("h1"))) + mem_0_10.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_11.CE <= RW0_clk + mem_0_11.A <= RW0_addr + node RW0_rdata_0_11 = bits(mem_0_11.O, 0, 0) + mem_0_11.I <= bits(RW0_wdata, 11, 11) + mem_0_11.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_11.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 11, 11)), UInt<1>("h1"))) + mem_0_11.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_12.CE <= RW0_clk + mem_0_12.A <= RW0_addr + node RW0_rdata_0_12 = bits(mem_0_12.O, 0, 0) + mem_0_12.I <= bits(RW0_wdata, 12, 12) + mem_0_12.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_12.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 12, 12)), UInt<1>("h1"))) + mem_0_12.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_13.CE <= RW0_clk + mem_0_13.A <= RW0_addr + node RW0_rdata_0_13 = bits(mem_0_13.O, 0, 0) + mem_0_13.I <= bits(RW0_wdata, 13, 13) + mem_0_13.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_13.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 13, 13)), UInt<1>("h1"))) + mem_0_13.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_14.CE <= RW0_clk + mem_0_14.A <= RW0_addr + node RW0_rdata_0_14 = bits(mem_0_14.O, 0, 0) + mem_0_14.I <= bits(RW0_wdata, 14, 14) + mem_0_14.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_14.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 14, 14)), UInt<1>("h1"))) + mem_0_14.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_15.CE <= RW0_clk + mem_0_15.A <= RW0_addr + node RW0_rdata_0_15 = bits(mem_0_15.O, 0, 0) + mem_0_15.I <= bits(RW0_wdata, 15, 15) + mem_0_15.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_15.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 15, 15)), UInt<1>("h1"))) + mem_0_15.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_16.CE <= RW0_clk + mem_0_16.A <= RW0_addr + node RW0_rdata_0_16 = bits(mem_0_16.O, 0, 0) + mem_0_16.I <= bits(RW0_wdata, 16, 16) + mem_0_16.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_16.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 16, 16)), UInt<1>("h1"))) + mem_0_16.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_17.CE <= RW0_clk + mem_0_17.A <= RW0_addr + node RW0_rdata_0_17 = bits(mem_0_17.O, 0, 0) + mem_0_17.I <= bits(RW0_wdata, 17, 17) + mem_0_17.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_17.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 17, 17)), UInt<1>("h1"))) + mem_0_17.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_18.CE <= RW0_clk + mem_0_18.A <= RW0_addr + node RW0_rdata_0_18 = bits(mem_0_18.O, 0, 0) + mem_0_18.I <= bits(RW0_wdata, 18, 18) + mem_0_18.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_18.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 18, 18)), UInt<1>("h1"))) + mem_0_18.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_19.CE <= RW0_clk + mem_0_19.A <= RW0_addr + node RW0_rdata_0_19 = bits(mem_0_19.O, 0, 0) + mem_0_19.I <= bits(RW0_wdata, 19, 19) + mem_0_19.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_19.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 19, 19)), UInt<1>("h1"))) + mem_0_19.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_20.CE <= RW0_clk + mem_0_20.A <= RW0_addr + node RW0_rdata_0_20 = bits(mem_0_20.O, 0, 0) + mem_0_20.I <= bits(RW0_wdata, 20, 20) + mem_0_20.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_20.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 20, 20)), UInt<1>("h1"))) + mem_0_20.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_21.CE <= RW0_clk + mem_0_21.A <= RW0_addr + node RW0_rdata_0_21 = bits(mem_0_21.O, 0, 0) + mem_0_21.I <= bits(RW0_wdata, 21, 21) + mem_0_21.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_21.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 21, 21)), UInt<1>("h1"))) + mem_0_21.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_22.CE <= RW0_clk + mem_0_22.A <= RW0_addr + node RW0_rdata_0_22 = bits(mem_0_22.O, 0, 0) + mem_0_22.I <= bits(RW0_wdata, 22, 22) + mem_0_22.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_22.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 22, 22)), UInt<1>("h1"))) + mem_0_22.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_23.CE <= RW0_clk + mem_0_23.A <= RW0_addr + node RW0_rdata_0_23 = bits(mem_0_23.O, 0, 0) + mem_0_23.I <= bits(RW0_wdata, 23, 23) + mem_0_23.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_23.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 23, 23)), UInt<1>("h1"))) + mem_0_23.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_24.CE <= RW0_clk + mem_0_24.A <= RW0_addr + node RW0_rdata_0_24 = bits(mem_0_24.O, 0, 0) + mem_0_24.I <= bits(RW0_wdata, 24, 24) + mem_0_24.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_24.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 24, 24)), UInt<1>("h1"))) + mem_0_24.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_25.CE <= RW0_clk + mem_0_25.A <= RW0_addr + node RW0_rdata_0_25 = bits(mem_0_25.O, 0, 0) + mem_0_25.I <= bits(RW0_wdata, 25, 25) + mem_0_25.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_25.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 25, 25)), UInt<1>("h1"))) + mem_0_25.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_26.CE <= RW0_clk + mem_0_26.A <= RW0_addr + node RW0_rdata_0_26 = bits(mem_0_26.O, 0, 0) + mem_0_26.I <= bits(RW0_wdata, 26, 26) + mem_0_26.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_26.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 26, 26)), UInt<1>("h1"))) + mem_0_26.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_27.CE <= RW0_clk + mem_0_27.A <= RW0_addr + node RW0_rdata_0_27 = bits(mem_0_27.O, 0, 0) + mem_0_27.I <= bits(RW0_wdata, 27, 27) + mem_0_27.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_27.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 27, 27)), UInt<1>("h1"))) + mem_0_27.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_28.CE <= RW0_clk + mem_0_28.A <= RW0_addr + node RW0_rdata_0_28 = bits(mem_0_28.O, 0, 0) + mem_0_28.I <= bits(RW0_wdata, 28, 28) + mem_0_28.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_28.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 28, 28)), UInt<1>("h1"))) + mem_0_28.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_29.CE <= RW0_clk + mem_0_29.A <= RW0_addr + node RW0_rdata_0_29 = bits(mem_0_29.O, 0, 0) + mem_0_29.I <= bits(RW0_wdata, 29, 29) + mem_0_29.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_29.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 29, 29)), UInt<1>("h1"))) + mem_0_29.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_30.CE <= RW0_clk + mem_0_30.A <= RW0_addr + node RW0_rdata_0_30 = bits(mem_0_30.O, 0, 0) + mem_0_30.I <= bits(RW0_wdata, 30, 30) + mem_0_30.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_30.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 30, 30)), UInt<1>("h1"))) + mem_0_30.CSB <= not(and(RW0_en, UInt<1>("h1"))) + mem_0_31.CE <= RW0_clk + mem_0_31.A <= RW0_addr + node RW0_rdata_0_31 = bits(mem_0_31.O, 0, 0) + mem_0_31.I <= bits(RW0_wdata, 31, 31) + mem_0_31.OEB <= not(and(and(not(RW0_wmode), RW0_en), UInt<1>("h1"))) + mem_0_31.WEB <= not(and(and(RW0_wmode, bits(RW0_wmask, 31, 31)), UInt<1>("h1"))) + mem_0_31.CSB <= not(and(RW0_en, UInt<1>("h1"))) + node RW0_rdata_0 = cat(RW0_rdata_0_31, cat(RW0_rdata_0_30, cat(RW0_rdata_0_29, cat(RW0_rdata_0_28, cat(RW0_rdata_0_27, cat(RW0_rdata_0_26, cat(RW0_rdata_0_25, cat(RW0_rdata_0_24, cat(RW0_rdata_0_23, cat(RW0_rdata_0_22, cat(RW0_rdata_0_21, cat(RW0_rdata_0_20, cat(RW0_rdata_0_19, cat(RW0_rdata_0_18, cat(RW0_rdata_0_17, cat(RW0_rdata_0_16, cat(RW0_rdata_0_15, cat(RW0_rdata_0_14, cat(RW0_rdata_0_13, cat(RW0_rdata_0_12, cat(RW0_rdata_0_11, cat(RW0_rdata_0_10, cat(RW0_rdata_0_9, cat(RW0_rdata_0_8, cat(RW0_rdata_0_7, cat(RW0_rdata_0_6, cat(RW0_rdata_0_5, cat(RW0_rdata_0_4, cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0))))))))))))))))))))))))))))))) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<32>("h0")) + + extmodule my_sram_1rw_64x8 : + input A : UInt<6> + input CE : Clock + input I : UInt<8> + output O : UInt<8> + input CSB : UInt<1> + input OEB : UInt<1> + input WEB : UInt<1> + + defname = my_sram_1rw_64x8 +""" + + it should "compile, execute and test the boom test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleTestGenerator { + // Test that mapping a smaller memory using a larger lib can still work. + override def memWidth: Int = 26 + override def memDepth: BigInt = BigInt(2) + override def memMaskGran: Option[Int] = Some(26) + override def memPortPrefix: String = "" + + override def libWidth: Int = 32 + override def libDepth: BigInt = BigInt(64) + override def libMaskGran: Option[Int] = Some(1) + override def libPortPrefix: String = "" + + override def extraPorts: Seq[MacroExtraPort] = Seq( + MacroExtraPort(name = "must_be_one", portType = Constant, width = 1, value = 1) + ) + + override def generateBody(): String = + s""" + | inst mem_0_0 of $lib_name + | mem_0_0.must_be_one <= UInt<1>("h1") + | mem_0_0.clk <= clk + | mem_0_0.addr <= addr + | node dout_0_0 = bits(mem_0_0.dout, 25, 0) + | mem_0_0.din <= bits(din, 25, 0) + | mem_0_0.mask <= cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), bits(mask, 0, 0)))))))))))))))))))))))))))))))) + | mem_0_0.write_en <= and(and(write_en, UInt<1>("h1")), UInt<1>("h1")) + | node dout_0 = dout_0_0 + | dout <= mux(UInt<1>("h1"), dout_0, UInt<26>("h0")) + """.stripMargin + + it should "compile, execute, and test, the small tag array test" in { + compileExecuteAndTest(mem, lib, v, output) + } +} + +class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator { + val mem = s"mem-RocketChipTest.json" + val lib = s"lib-RocketChipTest.json" + val v = s"RocketChipTest.v" + + val libSRAMs = Seq( + SRAMMacro( + name = "SRAM1RW1024x8", + depth = 1024, + width = 8, + family = "1rw", + ports = Seq( + generateReadWritePort("", 8, BigInt(1024)) + ) + ), + SRAMMacro( + name = "SRAM1RW512x32", + depth = 512, + width = 32, + family = "1rw", + ports = Seq( + generateReadWritePort("", 32, BigInt(512)) + ) + ), + SRAMMacro( + name = "SRAM1RW64x128", + depth = 64, + width = 128, + family = "1rw", + ports = Seq( + generateReadWritePort("", 128, BigInt(64)) + ) + ), + SRAMMacro( + name = "SRAM1RW64x32", + depth = 64, + width = 32, + family = "1rw", + ports = Seq( + generateReadWritePort("", 32, BigInt(64)) + ) + ), + SRAMMacro( + name = "SRAM1RW64x8", + depth = 64, + width = 8, + family = "1rw", + ports = Seq( + generateReadWritePort("", 8, BigInt(64)) + ) + ), + SRAMMacro( + name = "SRAM1RW512x8", + depth = 512, + width = 8, + family = "1rw", + ports = Seq( + generateReadWritePort("", 8, BigInt(512)) + ) + ), + SRAMMacro( + name = "SRAM2RW64x32", + depth = 64, + width = 32, + family = "1r1w", + ports = Seq( + generateReadPort("portA", 32, BigInt(64)), + generateWritePort("portB", 32, BigInt(64)) + ) + ) + ) + + val memSRAMs: Seq[mdf.macrolib.Macro] = mdf.macrolib.Utils + .readMDFFromString(""" +[ + { + "type": "sram", + "name": "tag_array_ext", + "depth": 64, + "width": 80, + "ports": [ + { + "clock port name": "RW0_clk", + "mask granularity": 20, + "output port name": "RW0_rdata", + "input port name": "RW0_wdata", + "address port name": "RW0_addr", + "mask port name": "RW0_wmask", + "chip enable port name": "RW0_en", + "write enable port name": "RW0_wmode" + } + ] + }, + { + "type": "sram", + "name": "T_1090_ext", + "depth": 512, + "width": 64, + "ports": [ + { + "clock port name": "RW0_clk", + "output port name": "RW0_rdata", + "input port name": "RW0_wdata", + "address port name": "RW0_addr", + "chip enable port name": "RW0_en", + "write enable port name": "RW0_wmode" + } + ] + }, + { + "type": "sram", + "name": "T_406_ext", + "depth": 512, + "width": 64, + "ports": [ + { + "clock port name": "RW0_clk", + "mask granularity": 8, + "output port name": "RW0_rdata", + "input port name": "RW0_wdata", + "address port name": "RW0_addr", + "mask port name": "RW0_wmask", + "chip enable port name": "RW0_en", + "write enable port name": "RW0_wmode" + } + ] + }, + { + "type": "sram", + "name": "T_2172_ext", + "depth": 64, + "width": 88, + "ports": [ + { + "clock port name": "W0_clk", + "mask granularity": 22, + "input port name": "W0_data", + "address port name": "W0_addr", + "chip enable port name": "W0_en", + "mask port name": "W0_mask" + }, + { + "clock port name": "R0_clk", + "output port name": "R0_data", + "address port name": "R0_addr", + "chip enable port name": "R0_en" + } + ] + } +] +""").getOrElse(List()) + + writeToLib(lib, libSRAMs) + writeToMem(mem, memSRAMs) + + val output = // TODO: check correctness... + """ +circuit T_2172_ext : + module tag_array_ext : + input RW0_addr : UInt<6> + input RW0_clk : Clock + input RW0_wdata : UInt<80> + output RW0_rdata : UInt<80> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + input RW0_wmask : UInt<4> + + inst mem_0_0 of SRAM1RW64x32 + inst mem_0_1 of SRAM1RW64x32 + inst mem_0_2 of SRAM1RW64x32 + inst mem_0_3 of SRAM1RW64x32 + mem_0_0.clk <= RW0_clk + mem_0_0.addr <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.dout, 19, 0) + mem_0_0.din <= bits(RW0_wdata, 19, 0) + mem_0_0.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1")) + mem_0_1.clk <= RW0_clk + mem_0_1.addr <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.dout, 19, 0) + mem_0_1.din <= bits(RW0_wdata, 39, 20) + mem_0_1.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1")) + mem_0_2.clk <= RW0_clk + mem_0_2.addr <= RW0_addr + node RW0_rdata_0_2 = bits(mem_0_2.dout, 19, 0) + mem_0_2.din <= bits(RW0_wdata, 59, 40) + mem_0_2.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1")) + mem_0_3.clk <= RW0_clk + mem_0_3.addr <= RW0_addr + node RW0_rdata_0_3 = bits(mem_0_3.dout, 19, 0) + mem_0_3.din <= bits(RW0_wdata, 79, 60) + mem_0_3.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1")) + node RW0_rdata_0 = cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0))) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<80>("h0")) + + extmodule SRAM1RW64x32 : + input addr : UInt<6> + input clk : Clock + input din : UInt<32> + output dout : UInt<32> + input write_en : UInt<1> + + defname = SRAM1RW64x32 + + module T_1090_ext : + input RW0_addr : UInt<9> + input RW0_clk : Clock + input RW0_wdata : UInt<64> + output RW0_rdata : UInt<64> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + + inst mem_0_0 of SRAM1RW512x32 + inst mem_0_1 of SRAM1RW512x32 + mem_0_0.clk <= RW0_clk + mem_0_0.addr <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.dout, 31, 0) + mem_0_0.din <= bits(RW0_wdata, 31, 0) + mem_0_0.write_en <= and(and(RW0_wmode, UInt<1>("h1")), UInt<1>("h1")) + mem_0_1.clk <= RW0_clk + mem_0_1.addr <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.dout, 31, 0) + mem_0_1.din <= bits(RW0_wdata, 63, 32) + mem_0_1.write_en <= and(and(RW0_wmode, UInt<1>("h1")), UInt<1>("h1")) + node RW0_rdata_0 = cat(RW0_rdata_0_1, RW0_rdata_0_0) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<64>("h0")) + + extmodule SRAM1RW512x32 : + input addr : UInt<9> + input clk : Clock + input din : UInt<32> + output dout : UInt<32> + input write_en : UInt<1> + + defname = SRAM1RW512x32 + + + module T_406_ext : + input RW0_addr : UInt<9> + input RW0_clk : Clock + input RW0_wdata : UInt<64> + output RW0_rdata : UInt<64> + input RW0_en : UInt<1> + input RW0_wmode : UInt<1> + input RW0_wmask : UInt<8> + + inst mem_0_0 of SRAM1RW512x8 + inst mem_0_1 of SRAM1RW512x8 + inst mem_0_2 of SRAM1RW512x8 + inst mem_0_3 of SRAM1RW512x8 + inst mem_0_4 of SRAM1RW512x8 + inst mem_0_5 of SRAM1RW512x8 + inst mem_0_6 of SRAM1RW512x8 + inst mem_0_7 of SRAM1RW512x8 + mem_0_0.clk <= RW0_clk + mem_0_0.addr <= RW0_addr + node RW0_rdata_0_0 = bits(mem_0_0.dout, 7, 0) + mem_0_0.din <= bits(RW0_wdata, 7, 0) + mem_0_0.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1")) + mem_0_1.clk <= RW0_clk + mem_0_1.addr <= RW0_addr + node RW0_rdata_0_1 = bits(mem_0_1.dout, 7, 0) + mem_0_1.din <= bits(RW0_wdata, 15, 8) + mem_0_1.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1")) + mem_0_2.clk <= RW0_clk + mem_0_2.addr <= RW0_addr + node RW0_rdata_0_2 = bits(mem_0_2.dout, 7, 0) + mem_0_2.din <= bits(RW0_wdata, 23, 16) + mem_0_2.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1")) + mem_0_3.clk <= RW0_clk + mem_0_3.addr <= RW0_addr + node RW0_rdata_0_3 = bits(mem_0_3.dout, 7, 0) + mem_0_3.din <= bits(RW0_wdata, 31, 24) + mem_0_3.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1")) + mem_0_4.clk <= RW0_clk + mem_0_4.addr <= RW0_addr + node RW0_rdata_0_4 = bits(mem_0_4.dout, 7, 0) + mem_0_4.din <= bits(RW0_wdata, 39, 32) + mem_0_4.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 4, 4)), UInt<1>("h1")) + mem_0_5.clk <= RW0_clk + mem_0_5.addr <= RW0_addr + node RW0_rdata_0_5 = bits(mem_0_5.dout, 7, 0) + mem_0_5.din <= bits(RW0_wdata, 47, 40) + mem_0_5.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 5, 5)), UInt<1>("h1")) + mem_0_6.clk <= RW0_clk + mem_0_6.addr <= RW0_addr + node RW0_rdata_0_6 = bits(mem_0_6.dout, 7, 0) + mem_0_6.din <= bits(RW0_wdata, 55, 48) + mem_0_6.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 6, 6)), UInt<1>("h1")) + mem_0_7.clk <= RW0_clk + mem_0_7.addr <= RW0_addr + node RW0_rdata_0_7 = bits(mem_0_7.dout, 7, 0) + mem_0_7.din <= bits(RW0_wdata, 63, 56) + mem_0_7.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 7, 7)), UInt<1>("h1")) + node RW0_rdata_0 = cat(RW0_rdata_0_7, cat(RW0_rdata_0_6, cat(RW0_rdata_0_5, cat(RW0_rdata_0_4, cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0))))))) + RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<64>("h0")) + + extmodule SRAM1RW512x8 : + input addr : UInt<9> + input clk : Clock + input din : UInt<8> + output dout : UInt<8> + input write_en : UInt<1> + + defname = SRAM1RW512x8 + + + module T_2172_ext : + input W0_addr : UInt<6> + input W0_clk : Clock + input W0_data : UInt<88> + input W0_en : UInt<1> + input W0_mask : UInt<4> + input R0_addr : UInt<6> + input R0_clk : Clock + output R0_data : UInt<88> + input R0_en : UInt<1> + + inst mem_0_0 of SRAM2RW64x32 + inst mem_0_1 of SRAM2RW64x32 + inst mem_0_2 of SRAM2RW64x32 + inst mem_0_3 of SRAM2RW64x32 + mem_0_0.portB_clk <= W0_clk + mem_0_0.portB_addr <= W0_addr + mem_0_0.portB_din <= bits(W0_data, 21, 0) + mem_0_0.portB_write_en <= and(and(UInt<1>("h1"), bits(W0_mask, 0, 0)), UInt<1>("h1")) + mem_0_1.portB_clk <= W0_clk + mem_0_1.portB_addr <= W0_addr + mem_0_1.portB_din <= bits(W0_data, 43, 22) + mem_0_1.portB_write_en <= and(and(UInt<1>("h1"), bits(W0_mask, 1, 1)), UInt<1>("h1")) + mem_0_2.portB_clk <= W0_clk + mem_0_2.portB_addr <= W0_addr + mem_0_2.portB_din <= bits(W0_data, 65, 44) + mem_0_2.portB_write_en <= and(and(UInt<1>("h1"), bits(W0_mask, 2, 2)), UInt<1>("h1")) + mem_0_3.portB_clk <= W0_clk + mem_0_3.portB_addr <= W0_addr + mem_0_3.portB_din <= bits(W0_data, 87, 66) + mem_0_3.portB_write_en <= and(and(UInt<1>("h1"), bits(W0_mask, 3, 3)), UInt<1>("h1")) + mem_0_0.portA_clk <= R0_clk + mem_0_0.portA_addr <= R0_addr + node R0_data_0_0 = bits(mem_0_0.portA_dout, 21, 0) + mem_0_1.portA_clk <= R0_clk + mem_0_1.portA_addr <= R0_addr + node R0_data_0_1 = bits(mem_0_1.portA_dout, 21, 0) + mem_0_2.portA_clk <= R0_clk + mem_0_2.portA_addr <= R0_addr + node R0_data_0_2 = bits(mem_0_2.portA_dout, 21, 0) + mem_0_3.portA_clk <= R0_clk + mem_0_3.portA_addr <= R0_addr + node R0_data_0_3 = bits(mem_0_3.portA_dout, 21, 0) + node R0_data_0 = cat(R0_data_0_3, cat(R0_data_0_2, cat(R0_data_0_1, R0_data_0_0))) + R0_data <= mux(UInt<1>("h1"), R0_data_0, UInt<88>("h0")) + + extmodule SRAM2RW64x32 : + input portA_addr : UInt<6> + input portA_clk : Clock + output portA_dout : UInt<32> + input portB_addr : UInt<6> + input portB_clk : Clock + input portB_din : UInt<32> + input portB_write_en : UInt<1> + + defname = SRAM2RW64x32 +""" + + // TODO FIXME: Enable this test when firrtl #644 https://github.com/freechipsproject/firrtl/issues/644 is fixed + "rocket example" should "work" in { + pending + } + //~ compileExecuteAndTest(mem, lib, v, output) +} diff --git a/src/test/scala/barstools/macros/SynFlops.scala b/src/test/scala/barstools/macros/SynFlops.scala new file mode 100644 index 00000000..16a3446c --- /dev/null +++ b/src/test/scala/barstools/macros/SynFlops.scala @@ -0,0 +1,455 @@ +package barstools.macros + +// Test flop synthesis of the memory compiler. + +trait HasSynFlopsTestGenerator extends HasSimpleTestGenerator { + this: MacroCompilerSpec with HasSRAMGenerator => + def generateFlops(): String = { + s""" + inst mem_0_0 of split_$lib_name + mem_0_0.${libPortPrefix}_clk <= ${libPortPrefix}_clk + mem_0_0.${libPortPrefix}_addr <= ${libPortPrefix}_addr + node ${libPortPrefix}_dout_0_0 = bits(mem_0_0.${libPortPrefix}_dout, ${libWidth - 1}, 0) + mem_0_0.${libPortPrefix}_din <= bits(${libPortPrefix}_din, ${libWidth - 1}, 0) + mem_0_0.${libPortPrefix}_write_en <= and(and(and(${libPortPrefix}_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + node ${libPortPrefix}_dout_0 = ${libPortPrefix}_dout_0_0 + ${libPortPrefix}_dout <= mux(UInt<1>("h1"), ${libPortPrefix}_dout_0, UInt<$libWidth>("h0")) + + module split_$lib_name : + input ${libPortPrefix}_addr : UInt<$lib_addr_width> + input ${libPortPrefix}_clk : Clock + input ${libPortPrefix}_din : UInt<$libWidth> + output ${libPortPrefix}_dout : UInt<$libWidth> + input ${libPortPrefix}_write_en : UInt<1> + + mem ram : + data-type => UInt<$libWidth> + depth => $libDepth + read-latency => 1 + write-latency => 1 + readwriter => RW_0 + read-under-write => undefined + ram.RW_0.clk <= ${libPortPrefix}_clk + ram.RW_0.addr <= ${libPortPrefix}_addr + ram.RW_0.en <= UInt<1>("h1") + ram.RW_0.wmode <= ${libPortPrefix}_write_en + ram.RW_0.wmask <= UInt<1>("h1") + ${libPortPrefix}_dout <= ram.RW_0.rdata + ram.RW_0.wdata <= ${libPortPrefix}_din +""" + } + + // If there is no lib, put the flops definition into the body. + abstract override def generateBody(): String = { + if (this.isInstanceOf[HasNoLibTestGenerator]) { + generateFlops() + } else { + super.generateBody() + } + } + + // If there is no lib, don't generate a footer, since the flops definition + // will be in the body. + override def generateFooter(): String = { + if (this.isInstanceOf[HasNoLibTestGenerator]) "" + else + s""" + module $lib_name : +${generateFooterPorts()} + +${generateFlops()} +""" + } + +} + +class Synflops2048x8_noLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasNoLibTestGenerator + with HasSynFlopsTestGenerator { + override lazy val memDepth = BigInt(2048) + override lazy val memWidth = 8 + + compileExecuteAndTest(mem, None, v, output, synflops = true) +} + +class Synflops2048x16_noLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasNoLibTestGenerator + with HasSynFlopsTestGenerator { + override lazy val memDepth = BigInt(2048) + override lazy val memWidth = 16 + + compileExecuteAndTest(mem, None, v, output, synflops = true) +} + +class Synflops8192x16_noLib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasNoLibTestGenerator + with HasSynFlopsTestGenerator { + override lazy val memDepth = BigInt(8192) + override lazy val memWidth = 16 + + compileExecuteAndTest(mem, None, v, output, synflops = true) +} + +class Synflops2048x16_depth_Lib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with HasSynFlopsTestGenerator { + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val width = 16 + + compileExecuteAndTest(mem, lib, v, output, synflops = true) +} + +class Synflops2048x64_width_Lib + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleWidthTestGenerator + with HasSynFlopsTestGenerator { + override lazy val memWidth = 64 + override lazy val libWidth = 8 + override lazy val depth = BigInt(1024) + + compileExecuteAndTest(mem, lib, v, output, synflops = true) +} + +class Synflops_SplitPorts_Read_Write + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with HasSynFlopsTestGenerator { + import mdf.macrolib._ + + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val width = 8 + + override def generateLibSRAM(): SRAMMacro = SRAMMacro( + name = lib_name, + width = width, + depth = libDepth, + family = "1r1w", + ports = Seq( + generateReadPort("innerA", width, libDepth), + generateWritePort("innerB", width, libDepth) + ) + ) + + override def generateMemSRAM(): SRAMMacro = SRAMMacro( + name = mem_name, + width = width, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateReadPort("outerB", width, memDepth), + generateWritePort("outerA", width, memDepth) + ) + ) + + override def generateHeader() = + """ +circuit target_memory : + module target_memory : + input outerB_addr : UInt<11> + input outerB_clk : Clock + output outerB_dout : UInt<8> + input outerA_addr : UInt<11> + input outerA_clk : Clock + input outerA_din : UInt<8> + input outerA_write_en : UInt<1> +""" + + override def generateBody() = + """ + node outerB_addr_sel = bits(outerB_addr, 10, 10) + reg outerB_addr_sel_reg : UInt<1>, outerB_clk with : + reset => (UInt<1>("h0"), outerB_addr_sel_reg) + outerB_addr_sel_reg <= mux(UInt<1>("h1"), outerB_addr_sel, outerB_addr_sel_reg) + node outerA_addr_sel = bits(outerA_addr, 10, 10) + inst mem_0_0 of awesome_lib_mem + mem_0_0.innerB_clk <= outerA_clk + mem_0_0.innerB_addr <= outerA_addr + mem_0_0.innerB_din <= bits(outerA_din, 7, 0) + mem_0_0.innerB_write_en <= and(and(and(outerA_write_en, UInt<1>("h1")), UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h0"))) + mem_0_0.innerA_clk <= outerB_clk + mem_0_0.innerA_addr <= outerB_addr + node outerB_dout_0_0 = bits(mem_0_0.innerA_dout, 7, 0) + node outerB_dout_0 = outerB_dout_0_0 + inst mem_1_0 of awesome_lib_mem + mem_1_0.innerB_clk <= outerA_clk + mem_1_0.innerB_addr <= outerA_addr + mem_1_0.innerB_din <= bits(outerA_din, 7, 0) + mem_1_0.innerB_write_en <= and(and(and(outerA_write_en, UInt<1>("h1")), UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h1"))) + mem_1_0.innerA_clk <= outerB_clk + mem_1_0.innerA_addr <= outerB_addr + node outerB_dout_1_0 = bits(mem_1_0.innerA_dout, 7, 0) + node outerB_dout_1 = outerB_dout_1_0 + outerB_dout <= mux(eq(outerB_addr_sel_reg, UInt<1>("h0")), outerB_dout_0, mux(eq(outerB_addr_sel_reg, UInt<1>("h1")), outerB_dout_1, UInt<8>("h0"))) +""" + + override def generateFooterPorts() = + """ + input innerA_addr : UInt<10> + input innerA_clk : Clock + output innerA_dout : UInt<8> + input innerB_addr : UInt<10> + input innerB_clk : Clock + input innerB_din : UInt<8> + input innerB_write_en : UInt<1> +""" + + override def generateFlops() = + """ + inst mem_0_0 of split_awesome_lib_mem + mem_0_0.innerB_clk <= innerB_clk + mem_0_0.innerB_addr <= innerB_addr + mem_0_0.innerB_din <= bits(innerB_din, 7, 0) + mem_0_0.innerB_write_en <= and(and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1")) + mem_0_0.innerA_clk <= innerA_clk + mem_0_0.innerA_addr <= innerA_addr + node innerA_dout_0_0 = bits(mem_0_0.innerA_dout, 7, 0) + node innerA_dout_0 = innerA_dout_0_0 + innerA_dout <= mux(UInt<1>("h1"), innerA_dout_0, UInt<8>("h0")) + + module split_awesome_lib_mem : + input innerA_addr : UInt<10> + input innerA_clk : Clock + output innerA_dout : UInt<8> + input innerB_addr : UInt<10> + input innerB_clk : Clock + input innerB_din : UInt<8> + input innerB_write_en : UInt<1> + + mem ram : + data-type => UInt<8> + depth => 1024 + read-latency => 1 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + ram.R_0.clk <= innerA_clk + ram.R_0.addr <= innerA_addr + ram.R_0.en <= UInt<1>("h1") + innerA_dout <= ram.R_0.data + ram.W_0.clk <= innerB_clk + ram.W_0.addr <= innerB_addr + ram.W_0.en <= innerB_write_en + ram.W_0.mask <= UInt<1>("h1") + ram.W_0.data <= innerB_din +""" + + "Non-masked split lib; split mem" should "syn flops fine" in { + compileExecuteAndTest(mem, lib, v, output, synflops = true) + } +} + +class Synflops_SplitPorts_MaskedMem_Read_MaskedWrite + extends MacroCompilerSpec + with HasSRAMGenerator + with HasSimpleDepthTestGenerator + with HasSynFlopsTestGenerator { + import mdf.macrolib._ + + override lazy val memDepth = BigInt(2048) + override lazy val libDepth = BigInt(1024) + override lazy val width = 8 + override lazy val memMaskGran: Option[Int] = Some(8) + override lazy val libMaskGran: Option[Int] = Some(1) + + override def generateLibSRAM(): SRAMMacro = SRAMMacro( + name = lib_name, + width = width, + depth = libDepth, + family = "1r1w", + ports = Seq( + generateReadPort("innerA", width, libDepth), + generateWritePort("innerB", width, libDepth, libMaskGran) + ) + ) + + override def generateMemSRAM(): SRAMMacro = SRAMMacro( + name = mem_name, + width = width, + depth = memDepth, + family = "1r1w", + ports = Seq( + generateReadPort("outerB", width, memDepth), + generateWritePort("outerA", width, memDepth, memMaskGran) + ) + ) + + override def generateHeader() = + """ +circuit target_memory : + module target_memory : + input outerB_addr : UInt<11> + input outerB_clk : Clock + output outerB_dout : UInt<8> + input outerA_addr : UInt<11> + input outerA_clk : Clock + input outerA_din : UInt<8> + input outerA_write_en : UInt<1> + input outerA_mask : UInt<1> +""" + + override def generateBody() = + """ + node outerB_addr_sel = bits(outerB_addr, 10, 10) + reg outerB_addr_sel_reg : UInt<1>, outerB_clk with : + reset => (UInt<1>("h0"), outerB_addr_sel_reg) + outerB_addr_sel_reg <= mux(UInt<1>("h1"), outerB_addr_sel, outerB_addr_sel_reg) + node outerA_addr_sel = bits(outerA_addr, 10, 10) + inst mem_0_0 of awesome_lib_mem + mem_0_0.innerB_clk <= outerA_clk + mem_0_0.innerB_addr <= outerA_addr + mem_0_0.innerB_din <= bits(outerA_din, 7, 0) + mem_0_0.innerB_mask <= cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), bits(outerA_mask, 0, 0)))))))) + mem_0_0.innerB_write_en <= and(and(outerA_write_en, UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h0"))) + mem_0_0.innerA_clk <= outerB_clk + mem_0_0.innerA_addr <= outerB_addr + node outerB_dout_0_0 = bits(mem_0_0.innerA_dout, 7, 0) + node outerB_dout_0 = outerB_dout_0_0 + inst mem_1_0 of awesome_lib_mem + mem_1_0.innerB_clk <= outerA_clk + mem_1_0.innerB_addr <= outerA_addr + mem_1_0.innerB_din <= bits(outerA_din, 7, 0) + mem_1_0.innerB_mask <= cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), cat(bits(outerA_mask, 0, 0), bits(outerA_mask, 0, 0)))))))) + mem_1_0.innerB_write_en <= and(and(outerA_write_en, UInt<1>("h1")), eq(outerA_addr_sel, UInt<1>("h1"))) + mem_1_0.innerA_clk <= outerB_clk + mem_1_0.innerA_addr <= outerB_addr + node outerB_dout_1_0 = bits(mem_1_0.innerA_dout, 7, 0) + node outerB_dout_1 = outerB_dout_1_0 + outerB_dout <= mux(eq(outerB_addr_sel_reg, UInt<1>("h0")), outerB_dout_0, mux(eq(outerB_addr_sel_reg, UInt<1>("h1")), outerB_dout_1, UInt<8>("h0"))) +""" + + override def generateFooterPorts() = + """ + input innerA_addr : UInt<10> + input innerA_clk : Clock + output innerA_dout : UInt<8> + input innerB_addr : UInt<10> + input innerB_clk : Clock + input innerB_din : UInt<8> + input innerB_write_en : UInt<1> + input innerB_mask : UInt<8> +""" + + override def generateFlops() = + """ + inst mem_0_0 of split_awesome_lib_mem + inst mem_0_1 of split_awesome_lib_mem + inst mem_0_2 of split_awesome_lib_mem + inst mem_0_3 of split_awesome_lib_mem + inst mem_0_4 of split_awesome_lib_mem + inst mem_0_5 of split_awesome_lib_mem + inst mem_0_6 of split_awesome_lib_mem + inst mem_0_7 of split_awesome_lib_mem + mem_0_0.innerB_clk <= innerB_clk + mem_0_0.innerB_addr <= innerB_addr + mem_0_0.innerB_din <= bits(innerB_din, 0, 0) + mem_0_0.innerB_mask <= bits(innerB_mask, 0, 0) + mem_0_0.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_1.innerB_clk <= innerB_clk + mem_0_1.innerB_addr <= innerB_addr + mem_0_1.innerB_din <= bits(innerB_din, 1, 1) + mem_0_1.innerB_mask <= bits(innerB_mask, 1, 1) + mem_0_1.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_2.innerB_clk <= innerB_clk + mem_0_2.innerB_addr <= innerB_addr + mem_0_2.innerB_din <= bits(innerB_din, 2, 2) + mem_0_2.innerB_mask <= bits(innerB_mask, 2, 2) + mem_0_2.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_3.innerB_clk <= innerB_clk + mem_0_3.innerB_addr <= innerB_addr + mem_0_3.innerB_din <= bits(innerB_din, 3, 3) + mem_0_3.innerB_mask <= bits(innerB_mask, 3, 3) + mem_0_3.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_4.innerB_clk <= innerB_clk + mem_0_4.innerB_addr <= innerB_addr + mem_0_4.innerB_din <= bits(innerB_din, 4, 4) + mem_0_4.innerB_mask <= bits(innerB_mask, 4, 4) + mem_0_4.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_5.innerB_clk <= innerB_clk + mem_0_5.innerB_addr <= innerB_addr + mem_0_5.innerB_din <= bits(innerB_din, 5, 5) + mem_0_5.innerB_mask <= bits(innerB_mask, 5, 5) + mem_0_5.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_6.innerB_clk <= innerB_clk + mem_0_6.innerB_addr <= innerB_addr + mem_0_6.innerB_din <= bits(innerB_din, 6, 6) + mem_0_6.innerB_mask <= bits(innerB_mask, 6, 6) + mem_0_6.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_7.innerB_clk <= innerB_clk + mem_0_7.innerB_addr <= innerB_addr + mem_0_7.innerB_din <= bits(innerB_din, 7, 7) + mem_0_7.innerB_mask <= bits(innerB_mask, 7, 7) + mem_0_7.innerB_write_en <= and(and(innerB_write_en, UInt<1>("h1")), UInt<1>("h1")) + mem_0_0.innerA_clk <= innerA_clk + mem_0_0.innerA_addr <= innerA_addr + node innerA_dout_0_0 = bits(mem_0_0.innerA_dout, 0, 0) + mem_0_1.innerA_clk <= innerA_clk + mem_0_1.innerA_addr <= innerA_addr + node innerA_dout_0_1 = bits(mem_0_1.innerA_dout, 0, 0) + mem_0_2.innerA_clk <= innerA_clk + mem_0_2.innerA_addr <= innerA_addr + node innerA_dout_0_2 = bits(mem_0_2.innerA_dout, 0, 0) + mem_0_3.innerA_clk <= innerA_clk + mem_0_3.innerA_addr <= innerA_addr + node innerA_dout_0_3 = bits(mem_0_3.innerA_dout, 0, 0) + mem_0_4.innerA_clk <= innerA_clk + mem_0_4.innerA_addr <= innerA_addr + node innerA_dout_0_4 = bits(mem_0_4.innerA_dout, 0, 0) + mem_0_5.innerA_clk <= innerA_clk + mem_0_5.innerA_addr <= innerA_addr + node innerA_dout_0_5 = bits(mem_0_5.innerA_dout, 0, 0) + mem_0_6.innerA_clk <= innerA_clk + mem_0_6.innerA_addr <= innerA_addr + node innerA_dout_0_6 = bits(mem_0_6.innerA_dout, 0, 0) + mem_0_7.innerA_clk <= innerA_clk + mem_0_7.innerA_addr <= innerA_addr + node innerA_dout_0_7 = bits(mem_0_7.innerA_dout, 0, 0) + node innerA_dout_0 = cat(innerA_dout_0_7, cat(innerA_dout_0_6, cat(innerA_dout_0_5, cat(innerA_dout_0_4, cat(innerA_dout_0_3, cat(innerA_dout_0_2, cat(innerA_dout_0_1, innerA_dout_0_0))))))) + innerA_dout <= mux(UInt<1>("h1"), innerA_dout_0, UInt<8>("h0")) + + + module split_awesome_lib_mem : + input innerA_addr : UInt<10> + input innerA_clk : Clock + output innerA_dout : UInt<1> + input innerB_addr : UInt<10> + input innerB_clk : Clock + input innerB_din : UInt<1> + input innerB_write_en : UInt<1> + input innerB_mask : UInt<1> + + mem ram : + data-type => UInt<1> + depth => 1024 + read-latency => 1 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + ram.R_0.clk <= innerA_clk + ram.R_0.addr <= innerA_addr + ram.R_0.en <= UInt<1>("h1") + innerA_dout <= ram.R_0.data + ram.W_0.clk <= innerB_clk + ram.W_0.addr <= innerB_addr + ram.W_0.en <= innerB_write_en + ram.W_0.mask <= innerB_mask + ram.W_0.data <= innerB_din +""" + + "masked split lib; masked split mem" should "syn flops fine" in { + compileExecuteAndTest(mem, lib, v, output, synflops = true) + } +} diff --git a/src/test/scala/barstools/tapeout/transforms/GenerateSpec.scala b/src/test/scala/barstools/tapeout/transforms/GenerateSpec.scala new file mode 100644 index 00000000..bb089de9 --- /dev/null +++ b/src/test/scala/barstools/tapeout/transforms/GenerateSpec.scala @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 + +package barstools.tapeout.transforms + +import chisel3._ +import chisel3.experimental.ExtModule +import chisel3.stage.ChiselStage +import firrtl.FileUtils +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers.be +import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper + +import java.io.{File, PrintWriter} + +class BlackBoxInverter extends ExtModule { + val in = IO(Input(Bool())) + val out = IO(Output(Bool())) +} + +class GenerateExampleModule extends Module { + val in = IO(Input(Bool())) + val out = IO(Output(Bool())) + + val inverter = Module(new BlackBoxInverter) + inverter.in := in + val inverted = inverter.out + + val reg = RegInit(0.U(8.W)) + reg := reg + inverted.asUInt + out := reg +} + +class ToBeMadeExternal extends Module { + val in = IO(Input(Bool())) + val out = IO(Output(Bool())) + + val reg = RegInit(0.U(8.W)) + reg := reg + in.asUInt + 2.U + out := reg +} + +class GenerateExampleTester extends Module { + val success = IO(Output(Bool())) + + val mod = Module(new GenerateExampleModule) + mod.in := 1.U + + val mod2 = Module(new ToBeMadeExternal) + mod2.in := 1.U + + val reg = RegInit(0.U(8.W)) + reg := reg + mod.out + mod2.out + + success := reg === 100.U + + when(reg === 100.U) { + stop() + } +} + +class GenerateSpec extends AnyFreeSpec { + + def generateTestData(targetDir: String): Unit = { + FileUtils.makeDirectory(targetDir) + + new ChiselStage().emitFirrtl(new GenerateExampleTester, Array("--target-dir", targetDir)) + + val blackBoxInverterText = + """ + |module BlackBoxInverter( + | input [0:0] in, + | output [0:0] out + |); + | assign out = !in; + |endmodule + |""".stripMargin + + val printWriter2 = new PrintWriter(new File(s"$targetDir/BlackBoxInverter.v")) + printWriter2.write(blackBoxInverterText) + printWriter2.close() + } + + "generate test data" in { + val targetDir = "test_run_dir/generate_spec_source" + generateTestData(targetDir) + + new File(s"$targetDir/GenerateExampleTester.fir").exists() should be(true) + } + + "generate top test" in { + val targetDir = "test_run_dir/generate_spec" + generateTestData(targetDir) + + GenerateModelStageMain.main( + Array( + "-i", + s"$targetDir/GenerateExampleTester.fir", + "-o", + s"$targetDir/GenerateExampleTester.v" + ) + ) + new File(s"$targetDir/GenerateExampleTester.v").exists() should be(true) + } +} diff --git a/src/test/scala/barstools/tapeout/transforms/retime/RetimeSpec.scala b/src/test/scala/barstools/tapeout/transforms/retime/RetimeSpec.scala new file mode 100644 index 00000000..a086b0b9 --- /dev/null +++ b/src/test/scala/barstools/tapeout/transforms/retime/RetimeSpec.scala @@ -0,0 +1,119 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms.retime + +import chisel3._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation, FileUtils} +import logger.Logger +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class RetimeSpec extends AnyFlatSpec with Matchers { + def normalized(s: String): String = { + require(!s.contains("\n")) + s.replaceAll("\\s+", " ").trim + } + def uniqueDirName[T](gen: => T, name: String): String = { + val genClassName = gen.getClass.getName + name + genClassName.hashCode.abs + } + def getLowFirrtl[T <: RawModule](gen: () => T, extraArgs: Array[String] = Array.empty): String = { + // generate low firrtl + (new ChiselStage) + .execute( + Array("-X", "low") ++ extraArgs, + Seq(ChiselGeneratorAnnotation(gen)) + ) + .collect { + case EmittedFirrtlCircuitAnnotation(a) => a + case EmittedFirrtlModuleAnnotation(a) => a + } + .map(_.value) + .mkString("") + } + + behavior.of("retime library") + + it should "pass simple retime module annotation" in { + val gen = () => new RetimeModule + val dir = uniqueDirName(gen, "RetimeModule") + + Logger.makeScope(Seq.empty) { + val captor = new Logger.OutputCaptor + Logger.setOutput(captor.printStream) + + // generate low firrtl + val firrtl = getLowFirrtl( + gen, + 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"""") + } + + it should "pass simple retime instance annotation" in { + val gen = () => new RetimeInstance + val dir = uniqueDirName(gen, "RetimeInstance") + + Logger.makeScope(Seq.empty) { + val captor = new Logger.OutputCaptor + Logger.setOutput(captor.printStream) + + // generate low firrtl + val firrtl = getLowFirrtl( + gen, + 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"""") + } +} + +class RetimeModule extends Module with RetimeLib { + val io = IO(new Bundle { + val in = Input(UInt(15.W)) + val out = Output(UInt(15.W)) + }) + io.out := io.in + retime(this) +} + +class MyModule extends Module with RetimeLib { + val io = IO(new Bundle { + val in = Input(UInt(15.W)) + val out = Output(UInt(15.W)) + }) + io.out := io.in +} + +class RetimeInstance extends Module with RetimeLib { + val io = IO(new Bundle { + val in = Input(UInt(15.W)) + val out = Output(UInt(15.W)) + }) + val instance = Module(new MyModule) + retime(instance) + instance.io.in := io.in + io.out := instance.io.out +} diff --git a/src/test/scala/mdf/macrolib/ConfReaderSpec.scala b/src/test/scala/mdf/macrolib/ConfReaderSpec.scala new file mode 100644 index 00000000..58680cd7 --- /dev/null +++ b/src/test/scala/mdf/macrolib/ConfReaderSpec.scala @@ -0,0 +1,101 @@ +package mdf.macrolib + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ConfReaderSpec extends AnyFlatSpec with Matchers { + + /** Generate a read port in accordance with RenameAnnotatedMemoryPorts. */ + def generateReadPort(num: Int, width: Int, depth: Int): MacroPort = { + MacroPort( + address = PolarizedPort(s"R${num}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"R${num}_clk", PositiveEdge)), + output = Some(PolarizedPort(s"R${num}_data", ActiveHigh)), + width = Some(width), + depth = Some(depth) + ) + } + + /** Generate a write port in accordance with RenameAnnotatedMemoryPorts. */ + def generateWritePort(num: Int, width: Int, depth: Int, maskGran: Option[Int] = None): MacroPort = { + MacroPort( + address = PolarizedPort(s"W${num}_addr", ActiveHigh), + clock = Some(PolarizedPort(s"W${num}_clk", PositiveEdge)), + input = Some(PolarizedPort(s"W${num}_data", ActiveHigh)), + maskPort = if (maskGran.isDefined) Some(PolarizedPort(s"W${num}_mask", ActiveHigh)) else None, + maskGran = maskGran, + width = Some(184), + depth = Some(128) + ) + } + + "ConfReader" should "read a 1rw conf line" in { + val confStr = "name Foo_Bar_mem123_ext depth 128 width 184 ports mrw mask_gran 23" + ConfReader.readSingleLine(confStr) shouldBe SRAMMacro( + name = "Foo_Bar_mem123_ext", + width = 184, + depth = 128, + family = "1rw", + ports = List( + MacroPort( + address = PolarizedPort("RW0_addr", ActiveHigh), + clock = Some(PolarizedPort("RW0_clk", PositiveEdge)), + writeEnable = Some(PolarizedPort("RW0_wmode", ActiveHigh)), + output = Some(PolarizedPort("RW0_wdata", ActiveHigh)), + input = Some(PolarizedPort("RW0_rdata", ActiveHigh)), + maskPort = Some(PolarizedPort("RW0_wmask", ActiveHigh)), + maskGran = Some(23), + width = Some(184), + depth = Some(128) + ) + ), + extraPorts = List() + ) + } + + "ConfReader" should "read a 1r1w conf line" in { + val confStr = "name Foo_Bar_mem321_ext depth 128 width 184 ports read,mwrite mask_gran 23" + ConfReader.readSingleLine(confStr) shouldBe SRAMMacro( + name = "Foo_Bar_mem321_ext", + width = 184, + depth = 128, + family = "1r1w", + ports = List( + generateReadPort(0, 184, 128), + generateWritePort(0, 184, 128, Some(23)) + ), + extraPorts = List() + ) + } + + "ConfReader" should "read a mixed 1r2w conf line" in { + val confStr = "name Foo_Bar_mem321_ext depth 128 width 184 ports read,mwrite,write mask_gran 23" + ConfReader.readSingleLine(confStr) shouldBe SRAMMacro( + name = "Foo_Bar_mem321_ext", + width = 184, + depth = 128, + family = "1r2w", + ports = List( + generateReadPort(0, 184, 128), + generateWritePort(0, 184, 128, Some(23)), + generateWritePort(1, 184, 128) + ), + extraPorts = List() + ) + } + + "ConfReader" should "read a 42r29w conf line" in { + val confStr = + "name Foo_Bar_mem321_ext depth 128 width 184 ports " + (Seq.fill(42)("read") ++ Seq.fill(29)("mwrite")) + .mkString(",") + " mask_gran 23" + ConfReader.readSingleLine(confStr) shouldBe SRAMMacro( + name = "Foo_Bar_mem321_ext", + width = 184, + depth = 128, + family = "42r29w", + ports = ((0 to 41).map((num: Int) => generateReadPort(num, 184, 128))) ++ + ((0 to 28).map((num: Int) => generateWritePort(num, 184, 128, Some(23)))), + extraPorts = List() + ) + } +} diff --git a/src/test/scala/mdf/macrolib/FlipChipMacroSpec.scala b/src/test/scala/mdf/macrolib/FlipChipMacroSpec.scala new file mode 100644 index 00000000..c6a9e7ce --- /dev/null +++ b/src/test/scala/mdf/macrolib/FlipChipMacroSpec.scala @@ -0,0 +1,15 @@ +package mdf.macrolib + +import firrtl.FileUtils +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class FlipChipMacroSpec extends AnyFlatSpec with Matchers { + "Parsing flipchipmacros" should "work" in { + val stream = FileUtils.getLinesResource("/bumps.json") + val mdf = Utils.readMDFFromString(stream.mkString("\n")) + mdf match { + case Some(Seq(fcp: FlipChipMacro)) => println(fcp.visualize) + } + } +} diff --git a/src/test/scala/mdf/macrolib/IOMacroSpec.scala b/src/test/scala/mdf/macrolib/IOMacroSpec.scala new file mode 100644 index 00000000..c6ab6e10 --- /dev/null +++ b/src/test/scala/mdf/macrolib/IOMacroSpec.scala @@ -0,0 +1,67 @@ +package mdf.macrolib + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class IOMacroSpec extends AnyFlatSpec with Matchers { + "Ground IOs" should "be detected" in { + val json = + """{ + | "name" : "GND", + | "type" : "ground" + |}""".stripMargin + val m = JSONUtils.readStringValueMap(json).get + IOMacro.parseJSON(m) shouldBe Some(IOMacro("GND", Ground)) + } + "Power IOs" should "be detected" in { + val json = + """{ + | "name" : "VDD0V8", + | "type" : "power" + |}""".stripMargin + val m = JSONUtils.readStringValueMap(json).get + IOMacro.parseJSON(m) shouldBe Some(IOMacro("VDD0V8", Power)) + } + "Digital IOs" should "be detected" in { + val json = + """{ + | "name" : "VDDC0_SEL[1:0]", + | "type" : "digital", + | "direction" : "output", + | "termination" : "CMOS" + |}""".stripMargin + val m = JSONUtils.readStringValueMap(json).get + IOMacro.parseJSON(m) shouldBe Some(IOMacro("VDDC0_SEL[1:0]", Digital, Some(Output), Some(CMOS))) + } + "Digital IOs with termination" should "be detected" in { + val json = + """{ + | "name" : "CCLK1", + | "type" : "digital", + | "direction" : "input", + | "termination" : 50, + | "terminationType" : "single", + | "terminationReference" : "GND" + |}""".stripMargin + val m = JSONUtils.readStringValueMap(json).get + IOMacro.parseJSON(m) shouldBe Some( + IOMacro("CCLK1", Digital, Some(Input), Some(Resistive(50)), Some(Single), Some("GND")) + ) + } + "Digital IOs with matching and termination" should "be detected" in { + val json = + """{ + | "name" : "REFCLK0P", + | "type" : "analog", + | "direction" : "input", + | "match" : ["REFCLK0N"], + | "termination" : 100, + | "terminationType" : "differential", + | "terminationReference" : "GND" + |}""".stripMargin + val m = JSONUtils.readStringValueMap(json).get + IOMacro.parseJSON(m) shouldBe Some( + IOMacro("REFCLK0P", Analog, Some(Input), Some(Resistive(100)), Some(Differential), Some("GND"), List("REFCLK0N")) + ) + } +} diff --git a/src/test/scala/mdf/macrolib/IOPropertiesSpec.scala b/src/test/scala/mdf/macrolib/IOPropertiesSpec.scala new file mode 100644 index 00000000..b09422a7 --- /dev/null +++ b/src/test/scala/mdf/macrolib/IOPropertiesSpec.scala @@ -0,0 +1,15 @@ +package mdf.macrolib + +import firrtl.FileUtils +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class IOPropertiesSpec extends AnyFlatSpec with Matchers { + "Parsing io_properties" should "work" in { + val stream = FileUtils.getLinesResource("/io_properties.json") + val mdf = Utils.readMDFFromString(stream.mkString("\n")) + mdf match { + case Some(Seq(fcp: IOProperties)) => + } + } +} diff --git a/src/test/scala/mdf/macrolib/MacroLibOutput.scala b/src/test/scala/mdf/macrolib/MacroLibOutput.scala new file mode 100644 index 00000000..85feaffa --- /dev/null +++ b/src/test/scala/mdf/macrolib/MacroLibOutput.scala @@ -0,0 +1,270 @@ +package mdf.macrolib + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import play.api.libs.json._ + +import java.io.File + +// Output tests (Scala -> JSON). +// TODO: unify these tests with the input tests? + +trait HasAwesomeMemData { + def getAwesomeMem() = { + SRAMMacro( + name = "awesome_mem", + width = 32, + depth = 1024, + family = "1rw", + ports = Seq( + MacroPort( + address = PolarizedPort(name = "addr", polarity = ActiveHigh), + clock = Some(PolarizedPort(name = "clk", polarity = PositiveEdge)), + writeEnable = Some(PolarizedPort(name = "write_enable", polarity = ActiveHigh)), + readEnable = Some(PolarizedPort(name = "read_enable", polarity = ActiveHigh)), + chipEnable = Some(PolarizedPort(name = "chip_enable", polarity = ActiveHigh)), + output = Some(PolarizedPort(name = "data_out", polarity = ActiveHigh)), + input = Some(PolarizedPort(name = "data_in", polarity = ActiveHigh)), + maskPort = Some(PolarizedPort(name = "mask", polarity = ActiveHigh)), + maskGran = Some(8), + width = Some(32), + depth = Some(1024) // These numbers don't matter. + ) + ), + extraPorts = List() + ) + } + + def getAwesomeMemJSON(): String = { + """ + | { + | "type": "sram", + | "name": "awesome_mem", + | "width": 32, + | "depth": "1024", + | "mux": 1, + | "mask":true, + | "family": "1rw", + | "ports": [ + | { + | "address port name": "addr", + | "address port polarity": "active high", + | "clock port name": "clk", + | "clock port polarity": "positive edge", + | "write enable port name": "write_enable", + | "write enable port polarity": "active high", + | "read enable port name": "read_enable", + | "read enable port polarity": "active high", + | "chip enable port name": "chip_enable", + | "chip enable port polarity": "active high", + | "output port name": "data_out", + | "output port polarity": "active high", + | "input port name": "data_in", + | "input port polarity": "active high", + | "mask port name": "mask", + | "mask port polarity": "active high", + | "mask granularity": 8 + | } + | ] + | } + |""".stripMargin + } +} + +// Tests for filler macros. +class FillerMacroOutput extends AnyFlatSpec with Matchers { + "Valid lvt macro" should "be generated" in { + val expected = """ + | { + | "type": "filler cell", + | "name": "MY_FILLER_CELL", + | "vt": "lvt" + | } + |""".stripMargin + FillerMacro("MY_FILLER_CELL", "lvt").toJSON shouldBe Json.parse(expected) + } + + "Valid metal macro" should "be generated" in { + val expected = """ + | { + | "type": "metal filler cell", + | "name": "METAL_FILLER_CELL", + | "vt": "lvt" + | } + |""".stripMargin + MetalFillerMacro("METAL_FILLER_CELL", "lvt").toJSON shouldBe Json.parse(expected) + } + + "Valid hvt macro" should "be generated" in { + val expected = """ + | { + | "type": "filler cell", + | "name": "HVT_CELL_PROP", + | "vt": "hvt" + | } + |""".stripMargin + FillerMacro("HVT_CELL_PROP", "hvt").toJSON shouldBe Json.parse(expected) + } +} + +class SRAMPortOutput extends AnyFlatSpec with Matchers { + "Extra port" should "be generated" in { + val m = MacroExtraPort( + name = "TIE_HIGH", + width = 8, + portType = Constant, + value = ((1 << 8) - 1) + ) + val expected = """ + | { + | "type": "constant", + | "name": "TIE_HIGH", + | "width": 8, + | "value": 255 + | } + |""".stripMargin + m.toJSON shouldBe Json.parse(expected) + } + + "Minimal write port" should "be generated" in { + val m = MacroPort( + address = PolarizedPort(name = "addr", polarity = ActiveHigh), + clock = Some(PolarizedPort(name = "clk", polarity = PositiveEdge)), + writeEnable = Some(PolarizedPort(name = "write_enable", polarity = ActiveHigh)), + input = Some(PolarizedPort(name = "data_in", polarity = ActiveHigh)), + width = Some(32), + depth = Some(1024) // These numbers don't matter. + ) + val expected = """ + | { + | "address port name": "addr", + | "address port polarity": "active high", + | "clock port name": "clk", + | "clock port polarity": "positive edge", + | "write enable port name": "write_enable", + | "write enable port polarity": "active high", + | "input port name": "data_in", + | "input port polarity": "active high" + | } + |""".stripMargin + m.toJSON shouldBe Json.parse(expected) + } + + "Minimal read port" should "be generated" in { + val m = MacroPort( + address = PolarizedPort(name = "addr", polarity = ActiveHigh), + clock = Some(PolarizedPort(name = "clk", polarity = PositiveEdge)), + output = Some(PolarizedPort(name = "data_out", polarity = ActiveHigh)), + width = Some(32), + depth = Some(1024) // These numbers don't matter. + ) + val expected = """ + | { + | "address port name": "addr", + | "address port polarity": "active high", + | "clock port name": "clk", + | "clock port polarity": "positive edge", + | "output port name": "data_out", + | "output port polarity": "active high" + | } + |""".stripMargin + m.toJSON shouldBe Json.parse(expected) + } + + "Masked read port" should "be generated" in { + val m = MacroPort( + address = PolarizedPort(name = "addr", polarity = ActiveHigh), + clock = Some(PolarizedPort(name = "clk", polarity = PositiveEdge)), + output = Some(PolarizedPort(name = "data_out", polarity = ActiveHigh)), + maskPort = Some(PolarizedPort(name = "mask", polarity = ActiveHigh)), + maskGran = Some(8), + width = Some(32), + depth = Some(1024) // These numbers don't matter. + ) + val expected = """ + | { + | "address port name": "addr", + | "address port polarity": "active high", + | "clock port name": "clk", + | "clock port polarity": "positive edge", + | "output port name": "data_out", + | "output port polarity": "active high", + | "mask port name": "mask", + | "mask port polarity": "active high", + | "mask granularity": 8 + | } + |""".stripMargin + m.toJSON shouldBe Json.parse(expected) + } + + "Everything port" should "be generated" in { + val m = MacroPort( + address = PolarizedPort(name = "addr", polarity = ActiveHigh), + clock = Some(PolarizedPort(name = "clk", polarity = PositiveEdge)), + writeEnable = Some(PolarizedPort(name = "write_enable", polarity = ActiveHigh)), + readEnable = Some(PolarizedPort(name = "read_enable", polarity = ActiveHigh)), + chipEnable = Some(PolarizedPort(name = "chip_enable", polarity = ActiveHigh)), + output = Some(PolarizedPort(name = "data_out", polarity = ActiveHigh)), + input = Some(PolarizedPort(name = "data_in", polarity = ActiveHigh)), + maskPort = Some(PolarizedPort(name = "mask", polarity = ActiveHigh)), + maskGran = Some(8), + width = Some(32), + depth = Some(1024) // These numbers don't matter. + ) + val expected = """ + | { + | "address port name": "addr", + | "address port polarity": "active high", + | "clock port name": "clk", + | "clock port polarity": "positive edge", + | "write enable port name": "write_enable", + | "write enable port polarity": "active high", + | "read enable port name": "read_enable", + | "read enable port polarity": "active high", + | "chip enable port name": "chip_enable", + | "chip enable port polarity": "active high", + | "output port name": "data_out", + | "output port polarity": "active high", + | "input port name": "data_in", + | "input port polarity": "active high", + | "mask port name": "mask", + | "mask port polarity": "active high", + | "mask granularity": 8 + | } + |""".stripMargin + m.toJSON shouldBe Json.parse(expected) + } +} + +class SRAMMacroOutput extends AnyFlatSpec with Matchers with HasAwesomeMemData { + "SRAM macro" should "be generated" in { + val m = getAwesomeMem + val expected = getAwesomeMemJSON + m.toJSON shouldBe Json.parse(expected) + } +} + +class InputOutput extends AnyFlatSpec with Matchers with HasAwesomeMemData { + "Read-write string" should "preserve data" in { + val mdf = List( + FillerMacro("MY_FILLER_CELL", "lvt"), + MetalFillerMacro("METAL_GEAR_FILLER", "hvt"), + getAwesomeMem + ) + Utils.readMDFFromString(Utils.writeMDFToString(mdf)) shouldBe Some(mdf) + } + + val testDir: String = "test_run_dir" + new File(testDir).mkdirs // Make sure the testDir exists + + "Read-write file" should "preserve data" in { + val mdf = List( + FillerMacro("MY_FILLER_CELL", "lvt"), + MetalFillerMacro("METAL_GEAR_FILLER", "hvt"), + getAwesomeMem + ) + val filename = testDir + "/" + "mdf_read_write_test.json" + Utils.writeMDFToPath(Some(filename), mdf) shouldBe true + Utils.readMDFFromPath(Some(filename)) shouldBe Some(mdf) + } +} diff --git a/src/test/scala/mdf/macrolib/MacroLibSpec.scala b/src/test/scala/mdf/macrolib/MacroLibSpec.scala new file mode 100644 index 00000000..fd3210bb --- /dev/null +++ b/src/test/scala/mdf/macrolib/MacroLibSpec.scala @@ -0,0 +1,406 @@ +package mdf.macrolib + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import play.api.libs.json._ + +object JSONUtils { + def readStringValueMap(str: String): Option[Map[String, JsValue]] = { + Json.parse(str) match { + case x: JsObject => Some(x.as[Map[String, JsValue]]) + case _ => None + } + } +} + +// Tests for filler macros +class FillerMacroSpec extends AnyFlatSpec with Matchers { + "Valid lvt macros" should "be detected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "filler cell", + | "name": "MY_FILLER_CELL", + | "vt": "lvt" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe Some(FillerMacro("MY_FILLER_CELL", "lvt")) + } + + "Valid metal macro" should "be detected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "metal filler cell", + | "name": "METAL_FILLER_CELL", + | "vt": "lvt" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe Some(MetalFillerMacro("METAL_FILLER_CELL", "lvt")) + } + + "Valid hvt macros" should "be detected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "filler cell", + | "name": "HVT_CELL_PROP", + | "vt": "hvt" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe Some(FillerMacro("HVT_CELL_PROP", "hvt")) + } + + "Empty name macros" should "be rejected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "filler cell", + | "name": "", + | "vt": "hvt" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe None + } + + "Empty vt macros" should "be rejected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "metal filler cell", + | "name": "DEAD_CELL", + | "vt": "" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe None + } + + "Missing vt macros" should "be rejected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "metal filler cell", + | "name": "DEAD_CELL" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe None + } + + "Missing name macros" should "be rejected" in { + val m = JSONUtils + .readStringValueMap(""" + | { + | "type": "filler cell", + | "vt": "" + | } + |""".stripMargin) + .get + FillerMacroBase.parseJSON(m) shouldBe None + } +} + +// Tests for SRAM type and associates. +class SRAMMacroSpec extends AnyFlatSpec with Matchers { + // Simple port which can be reused in tests + // Note: assume width=depth=simplePortConstant. + val simplePortConstant = 1024 + def simplePort( + postfix: String = "", + width: Int = simplePortConstant, + depth: Int = simplePortConstant + ): (String, MacroPort) = { + val json = s""" + { + "address port name": "A_${postfix}", + "address port polarity": "active high", + "clock port name": "CLK_${postfix}", + "clock port polarity": "positive edge", + "write enable port name": "WEN_${postfix}", + "write enable port polarity": "active high", + "read enable port name": "REN_${postfix}", + "read enable port polarity": "active high", + "chip enable port name": "CEN_${postfix}", + "chip enable port polarity": "active high", + "output port name": "OUT_${postfix}", + "output port polarity": "active high", + "input port name": "IN_${postfix}", + "input port polarity": "active high", + "mask granularity": 1, + "mask port name": "MASK_${postfix}", + "mask port polarity": "active high" + } + """ + val port = MacroPort( + address = PolarizedPort(s"A_${postfix}", ActiveHigh), + clock = Some(PolarizedPort(s"CLK_${postfix}", PositiveEdge)), + writeEnable = Some(PolarizedPort(s"WEN_${postfix}", ActiveHigh)), + readEnable = Some(PolarizedPort(s"REN_${postfix}", ActiveHigh)), + chipEnable = Some(PolarizedPort(s"CEN_${postfix}", ActiveHigh)), + output = Some(PolarizedPort(s"OUT_${postfix}", ActiveHigh)), + input = Some(PolarizedPort(s"IN_${postfix}", ActiveHigh)), + maskPort = Some(PolarizedPort(s"MASK_${postfix}", ActiveHigh)), + maskGran = Some(1), + width = Some(width), + depth = Some(depth) + ) + (json, port) + } + "Simple port" should "be valid" in { + { + val (json, port) = simplePort("Simple1") + MacroPort.parseJSON(JSONUtils.readStringValueMap(json).get, simplePortConstant, simplePortConstant) shouldBe Some( + port + ) + } + { + val (json, port) = simplePort("Simple2") + MacroPort.parseJSON(JSONUtils.readStringValueMap(json).get, simplePortConstant, simplePortConstant) shouldBe Some( + port + ) + } + { + val (json, port) = simplePort("bar") + MacroPort.parseJSON(JSONUtils.readStringValueMap(json).get, simplePortConstant, simplePortConstant) shouldBe Some( + port + ) + } + { + val (json, port) = simplePort("") + MacroPort.parseJSON(JSONUtils.readStringValueMap(json).get, simplePortConstant, simplePortConstant) shouldBe Some( + port + ) + } + } + + "Simple SRAM macro" should "be detected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "SRAMS_R_US", + "width": 2048, + "depth": "4096", + "family": "1rw", + "ports": [ + ${json} + ] +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe Some( + SRAMMacro("SRAMS_R_US", width = 2048, depth = 4096, family = "1rw", ports = List(port), extraPorts = List()) + ) + } + + "Non-power-of-two width & depth SRAM macro" should "be detected" in { + val (json, port) = simplePort("", 1234, 8888) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "SRAMS_R_US", + "width": 1234, + "depth": "8888", + "family": "1rw", + "ports": [ + ${json} + ] +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe Some( + SRAMMacro("SRAMS_R_US", width = 1234, depth = 8888, family = "1rw", ports = List(port), extraPorts = List()) + ) + } + + "Minimal memory port" should "be detected" in { + val (json, port) = simplePort("_A", 64, 1024) + val port2 = MacroPort( + address = PolarizedPort("A_B", ActiveHigh), + clock = Some(PolarizedPort("CLK_B", PositiveEdge)), + writeEnable = Some(PolarizedPort("WEN_B", ActiveHigh)), + readEnable = None, + chipEnable = None, + output = Some(PolarizedPort("OUT_B", ActiveHigh)), + input = Some(PolarizedPort("IN_B", ActiveHigh)), + maskPort = None, + maskGran = None, + width = Some(64), + depth = Some(1024) + ) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "SRAMS_R_US", + "width": 64, + "depth": "1024", + "family": "2rw", + "ports": [ + ${json}, + { + "address port name": "A_B", + "address port polarity": "active high", + "clock port name": "CLK_B", + "clock port polarity": "positive edge", + "write enable port name": "WEN_B", + "write enable port polarity": "active high", + "output port name": "OUT_B", + "output port polarity": "active high", + "input port name": "IN_B", + "input port polarity": "active high" + } + ] +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe Some( + SRAMMacro("SRAMS_R_US", width = 64, depth = 1024, family = "2rw", ports = List(port, port2), extraPorts = List()) + ) + } + + "Extra ports" should "be detected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "GOT_EXTRA", + "width": 2048, + "depth": "4096", + "family": "1rw", + "ports": [ + ${json} + ], + "extra ports": [ + { + "name": "TIE_DIE", + "width": 1, + "type": "constant", + "value": 1 + }, + { + "name": "TIE_MOO", + "width": 4, + "type": "constant", + "value": 0 + } + ] +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe Some( + SRAMMacro( + "GOT_EXTRA", + width = 2048, + depth = 4096, + family = "1rw", + ports = List(port), + extraPorts = List( + MacroExtraPort( + name = "TIE_DIE", + width = 1, + portType = Constant, + value = 1 + ), + MacroExtraPort( + name = "TIE_MOO", + width = 4, + portType = Constant, + value = 0 + ) + ) + ) + ) + } + + "Invalid port" should "be rejected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "SRAMS_R_US", + "width": 2048, + "depth": "4096", + "family": "1rw", + "ports": [ + { + "address port name": "missing_polarity", + "output port name": "missing_clock" + } + ] +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe None + } + + "No ports" should "be rejected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "SRAMS_R_US", + "width": 2048, + "depth": "4096", + "family": "1rw" +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe None + } + + "No family and ports" should "be rejected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "SRAMS_R_US", + "width": 2048, + "depth": "4096" +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe None + } + + "String width" should "be rejected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "BAD_BAD_SRAM", + "width": "wide", + "depth": "4096" +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe None + } + + "String depth" should "be rejected" in { + val (json, port) = simplePort("", 2048, 4096) + val m = JSONUtils + .readStringValueMap(s""" +{ + "type": "sram", + "name": "BAD_BAD_SRAM", + "width": 512, + "depth": "octopus_under_the_sea" +} + """) + .get + SRAMMacro.parseJSON(m) shouldBe None + } +}