Merge pull request #109 from ucb-bar/move-src-to-toplevel
Unify barstools into single src tree that matches package structure
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "mdf"]
|
||||
path = mdf
|
||||
url = https://github.com/ucb-bar/plsi-mdf.git
|
||||
25
build.sbt
25
build.sbt
@@ -1,8 +1,8 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
val defaultVersions = Map(
|
||||
"chisel3" -> "3.4.+",
|
||||
"chisel-iotesters" -> "1.5.+"
|
||||
"chisel3" -> "3.5-SNAPSHOT",
|
||||
"chisel-iotesters" -> "2.5-SNAPSHOT"
|
||||
)
|
||||
|
||||
lazy val commonSettings = Seq(
|
||||
@@ -14,7 +14,10 @@ lazy val commonSettings = Seq(
|
||||
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
|
||||
},
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalatest" %% "scalatest" % "3.2.2" % "test",
|
||||
"com.typesafe.play" %% "play-json" % "2.9.2",
|
||||
"org.scalatest" %% "scalatest" % "3.2.9" % "test",
|
||||
"org.apache.logging.log4j" % "log4j-api" % "2.11.2",
|
||||
"org.apache.logging.log4j" % "log4j-core" % "2.11.2"
|
||||
),
|
||||
resolvers ++= Seq(
|
||||
Resolver.sonatypeRepo("snapshots"),
|
||||
@@ -23,19 +26,17 @@ lazy val commonSettings = Seq(
|
||||
)
|
||||
)
|
||||
|
||||
disablePlugins(sbtassembly.AssemblyPlugin)
|
||||
//disablePlugins(sbtassembly.AssemblyPlugin)
|
||||
//
|
||||
//enablePlugins(sbtassembly.AssemblyPlugin)
|
||||
|
||||
lazy val mdf = (project in file("mdf/scalalib"))
|
||||
lazy val macros = (project in file("macros"))
|
||||
.dependsOn(mdf)
|
||||
lazy val tapeout = (project in file("."))
|
||||
.settings(commonSettings)
|
||||
.settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls"))
|
||||
.settings(fork := true)
|
||||
.settings(
|
||||
mainClass := Some("barstools.macros.MacroCompiler")
|
||||
)
|
||||
.enablePlugins(sbtassembly.AssemblyPlugin)
|
||||
|
||||
lazy val tapeout = (project in file("tapeout"))
|
||||
.settings(commonSettings)
|
||||
.settings(scalacOptions in Test ++= Seq("-language:reflectiveCalls"))
|
||||
|
||||
lazy val root = (project in file(".")).aggregate(macros, tapeout)
|
||||
//lazy val root = (project in file(".")).aggregate(tapeout)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
enablePlugins(sbtassembly.AssemblyPlugin)
|
||||
1
mdf
1
mdf
Submodule mdf deleted from e588024d70
@@ -141,7 +141,7 @@ object IOCell {
|
||||
* @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 = _.toBool
|
||||
val toSyncReset: (Reset) => Bool = _.asBool()
|
||||
val toAsyncReset: (Reset) => AsyncReset = _.asAsyncReset
|
||||
def generateFromSignal[T <: Data, R <: Reset](
|
||||
coreSignal: T,
|
||||
@@ -8,7 +8,7 @@
|
||||
package barstools.macros
|
||||
|
||||
import barstools.macros.Utils._
|
||||
import firrtl.Utils._
|
||||
import firrtl.Utils.{BoolType, one, zero}
|
||||
import firrtl.annotations._
|
||||
import firrtl.ir._
|
||||
import firrtl.stage.{FirrtlSourceAnnotation, FirrtlStage, Forms, OutputFileAnnotation, RunFirrtlTransformAnnotation}
|
||||
@@ -783,7 +783,6 @@ class MacroCompilerOptimizations extends SeqTransform with DependencyAPIMigratio
|
||||
new firrtl.transforms.ConstantPropagation,
|
||||
passes.memlib.VerilogMemDelays,
|
||||
new firrtl.transforms.ConstantPropagation,
|
||||
passes.Legalize,
|
||||
passes.SplitExpressions,
|
||||
passes.CommonSubexpressionElimination
|
||||
)
|
||||
@@ -3,7 +3,7 @@
|
||||
package barstools.macros
|
||||
|
||||
import barstools.macros.Utils._
|
||||
import firrtl.Utils._
|
||||
import firrtl.Utils.{zero, one}
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.passes.MemPortUtils.memPortField
|
||||
@@ -27,7 +27,7 @@ class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pa
|
||||
case Some(gran) => (UIntType(IntWidth(gran)), gran.intValue)
|
||||
}
|
||||
|
||||
val maxDepth = min(lib.src.depth, 1 << 26)
|
||||
val maxDepth = firrtl.Utils.min(lib.src.depth, 1 << 26)
|
||||
val numMems = lib.src.depth / maxDepth
|
||||
|
||||
// Change macro to be mapped onto to look like the below mem
|
||||
@@ -64,7 +64,7 @@ class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pa
|
||||
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 {
|
||||
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)
|
||||
@@ -60,7 +60,7 @@ class ResetInverterTransform extends Transform with DependencyAPIMigration {
|
||||
|
||||
trait ResetInverter {
|
||||
self: chisel3.Module =>
|
||||
def invert[T <: chisel3.internal.LegacyModule](module: T): Unit = {
|
||||
def invert[T <: chisel3.Module](module: T): Unit = {
|
||||
chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform {
|
||||
def transformClass: Class[_ <: Transform] = classOf[ResetInverterTransform]
|
||||
def toFirrtl: Annotation = ResetInverterAnnotation(module.toNamed)
|
||||
@@ -39,7 +39,7 @@ class RetimeTransform extends Transform with DependencyAPIMigration {
|
||||
trait RetimeLib {
|
||||
self: chisel3.Module =>
|
||||
|
||||
def retime[T <: chisel3.internal.LegacyModule](module: T): Unit = {
|
||||
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)
|
||||
@@ -1,5 +1,6 @@
|
||||
package barstools.tapeout.transforms.utils
|
||||
|
||||
import firrtl.FileUtils
|
||||
import net.jcazevedo.moultingyaml._
|
||||
|
||||
import java.io.File
|
||||
@@ -10,10 +11,10 @@ class YamlFileReader(resource: String) {
|
||||
val yamlString = file match {
|
||||
case f if f.isEmpty =>
|
||||
// Use example config if no file is provided
|
||||
val stream = getClass.getResourceAsStream(resource)
|
||||
io.Source.fromInputStream(stream).mkString
|
||||
val stream = FileUtils.getTextResource(resource)
|
||||
stream
|
||||
case f if new File(f).exists =>
|
||||
scala.io.Source.fromFile(f).getLines.mkString("\n")
|
||||
FileUtils.getText(f)
|
||||
case _ =>
|
||||
throw new Exception("No valid Yaml file found!")
|
||||
}
|
||||
95
src/main/scala/mdf/macrolib/ConfReader.scala
Normal file
95
src/main/scala/mdf/macrolib/ConfReader.scala
Normal file
@@ -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(_))
|
||||
}
|
||||
}
|
||||
61
src/main/scala/mdf/macrolib/FillerMacroBase.scala
Normal file
61
src/main/scala/mdf/macrolib/FillerMacroBase.scala
Normal file
@@ -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"
|
||||
}
|
||||
72
src/main/scala/mdf/macrolib/FlipChipMacro.scala
Normal file
72
src/main/scala/mdf/macrolib/FlipChipMacro.scala
Normal file
@@ -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) }
|
||||
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))
|
||||
}
|
||||
}
|
||||
147
src/main/scala/mdf/macrolib/IOMacro.scala
Normal file
147
src/main/scala/mdf/macrolib/IOMacro.scala
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
19
src/main/scala/mdf/macrolib/MacroLib.scala
Normal file
19
src/main/scala/mdf/macrolib/MacroLib.scala
Normal file
@@ -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
|
||||
}
|
||||
444
src/main/scala/mdf/macrolib/SRAM.scala
Normal file
444
src/main/scala/mdf/macrolib/SRAM.scala
Normal file
@@ -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 "<prefix> port name" and "<prefix> 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
96
src/main/scala/mdf/macrolib/Utils.scala
Normal file
96
src/main/scala/mdf/macrolib/Utils.scala
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/test/resources/bumps.json
Normal file
41
src/test/resources/bumps.json
Normal file
@@ -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", "-"]
|
||||
]
|
||||
}
|
||||
]
|
||||
663
src/test/resources/io_properties.json
Normal file
663
src/test/resources/io_properties.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -27,7 +27,9 @@ class Masks_FourTypes_NonMaskedMem_NonMaskedLib
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libMaskGran = None
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_FourTypes_NonMaskedMem_MaskedLib
|
||||
@@ -40,7 +42,9 @@ class Masks_FourTypes_NonMaskedMem_MaskedLib
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libMaskGran = Some(2)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_NonMaskedLib
|
||||
@@ -53,7 +57,9 @@ class Masks_FourTypes_MaskedMem_NonMaskedLib
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libMaskGran = None
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran
|
||||
@@ -66,7 +72,9 @@ class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libMaskGran = None
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib
|
||||
@@ -79,7 +87,9 @@ class Masks_FourTypes_MaskedMem_MaskedLib
|
||||
override lazy val libWidth = 16
|
||||
override lazy val libMaskGran = Some(4)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran
|
||||
@@ -92,7 +102,9 @@ class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran
|
||||
override lazy val libWidth = 16
|
||||
override lazy val libMaskGran = Some(8)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib_SmallerMaskGran
|
||||
@@ -105,7 +117,9 @@ class Masks_FourTypes_MaskedMem_MaskedLib_SmallerMaskGran
|
||||
override lazy val libWidth = 32
|
||||
override lazy val libMaskGran = Some(8)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
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.
|
||||
@@ -117,7 +131,9 @@ class Masks_BitMaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGene
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libMaskGran = None
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
// FPGA-style byte-masked memories.
|
||||
@@ -131,7 +147,9 @@ class Masks_FPGAStyle_32_8
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(8)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple powers of two with bit-masked lib.
|
||||
@@ -145,7 +163,9 @@ class Masks_PowersOfTwo_8_1
|
||||
override lazy val memMaskGran = Some(8)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_16_1
|
||||
@@ -157,7 +177,9 @@ class Masks_PowersOfTwo_16_1
|
||||
override lazy val memMaskGran = Some(16)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_32_1
|
||||
@@ -169,7 +191,9 @@ class Masks_PowersOfTwo_32_1
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_64_1
|
||||
@@ -181,7 +205,9 @@ class Masks_PowersOfTwo_64_1
|
||||
override lazy val memMaskGran = Some(64)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple powers of two with non bit-masked lib.
|
||||
@@ -195,7 +221,9 @@ class Masks_PowersOfTwo_32_4
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(4)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_32_8
|
||||
@@ -207,7 +235,9 @@ class Masks_PowersOfTwo_32_8
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(8)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_8_8
|
||||
@@ -219,7 +249,9 @@ class Masks_PowersOfTwo_8_8
|
||||
override lazy val memMaskGran = Some(8)
|
||||
override lazy val libMaskGran = Some(8)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
// Width as a multiple of the mask, bit-masked lib
|
||||
@@ -233,7 +265,9 @@ class Masks_IntegerMaskMultiple_20_10
|
||||
override lazy val memMaskGran = Some(10)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_21_7
|
||||
@@ -258,7 +292,9 @@ class Masks_IntegerMaskMultiple_21_21
|
||||
override lazy val memMaskGran = Some(21)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_84_21
|
||||
@@ -270,7 +306,9 @@ class Masks_IntegerMaskMultiple_84_21
|
||||
override lazy val memMaskGran = Some(21)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_92_23
|
||||
@@ -282,7 +320,9 @@ class Masks_IntegerMaskMultiple_92_23
|
||||
override lazy val memMaskGran = Some(23)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_117_13
|
||||
@@ -294,7 +334,9 @@ class Masks_IntegerMaskMultiple_117_13
|
||||
override lazy val memMaskGran = Some(13)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_160_20
|
||||
@@ -306,7 +348,9 @@ class Masks_IntegerMaskMultiple_160_20
|
||||
override lazy val memMaskGran = Some(20)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_184_23
|
||||
@@ -318,7 +362,9 @@ class Masks_IntegerMaskMultiple_184_23
|
||||
override lazy val memMaskGran = Some(23)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
// Width as an non-integer multiple of the mask, bit-masked lib
|
||||
@@ -316,7 +316,6 @@ class SplitWidth_2rw_differentMasks extends MacroCompilerSpec with HasSRAMGenera
|
||||
lazy val memMaskGranB = 8 // these generators are run at constructor time
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
println(memMaskGranB)
|
||||
SRAMMacro(
|
||||
name = mem_name,
|
||||
width = memWidth,
|
||||
@@ -27,7 +27,7 @@ class WriteEnableTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
val lib = s"lib-WriteEnableTest.json" // lib. of mems to create it
|
||||
val v = s"WriteEnableTest.json"
|
||||
|
||||
override val libPrefix = "macros/src/test/resources"
|
||||
override val libPrefix = "src/test/resources"
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
@@ -89,7 +89,9 @@ circuit cc_banks_0_ext :
|
||||
defname = fake_mem
|
||||
"""
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
@@ -97,7 +99,7 @@ class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
val lib = s"lib-MaskPortTest.json" // lib. of mems to create it
|
||||
val v = s"MaskPortTest.json"
|
||||
|
||||
override val libPrefix = "macros/src/test/resources"
|
||||
override val libPrefix = "src/test/resources"
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
@@ -173,7 +175,9 @@ circuit cc_dir_ext :
|
||||
defname = fake_mem
|
||||
"""
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, exectue, and test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
@@ -181,10 +185,9 @@ class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
val lib = s"lib-BOOMTest.json"
|
||||
val v = s"BOOMTest.v"
|
||||
|
||||
override val libPrefix = "macros/src/test/resources"
|
||||
override val libPrefix = "src/test/resources"
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
val memSRAMs = mdf.macrolib.Utils.readMDFFromString("""
|
||||
[ {
|
||||
"type" : "sram",
|
||||
"name" : "_T_182_ext",
|
||||
@@ -1345,7 +1348,9 @@ circuit smem_0_ext :
|
||||
defname = my_sram_1rw_64x8
|
||||
"""
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute and test the boom test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleTestGenerator {
|
||||
@@ -1378,7 +1383,9 @@ class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with Has
|
||||
| dout <= mux(UInt<1>("h1"), dout_0, UInt<26>("h0"))
|
||||
""".stripMargin
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
it should "compile, execute, and test, the small tag array test" in {
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
}
|
||||
|
||||
class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
@@ -7,6 +7,8 @@ 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}
|
||||
|
||||
@@ -57,37 +59,42 @@ class GenerateExampleTester extends MultiIOModule {
|
||||
}
|
||||
|
||||
class GenerateSpec extends AnyFreeSpec {
|
||||
"generate test data" in {
|
||||
val targetDir = "test_run_dir/generate_spec_source"
|
||||
|
||||
def generateTestData(targetDir: String): Unit = {
|
||||
FileUtils.makeDirectory(targetDir)
|
||||
|
||||
val printWriter = new PrintWriter(new File(s"$targetDir/GenerateExampleTester.fir"))
|
||||
printWriter.write((new ChiselStage()).emitFirrtl(new GenerateExampleTester))
|
||||
printWriter.close()
|
||||
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 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 sourceDir = "test_run_dir/generate_spec_source"
|
||||
val targetDir = "test_run_dir/generate_spec"
|
||||
generateTestData(targetDir)
|
||||
|
||||
GenerateTop.main(Array(
|
||||
"-i", s"$sourceDir/GenerateExampleTester.fir",
|
||||
"-i", s"$targetDir/GenerateExampleTester.fir",
|
||||
"-o", s"$targetDir/GenerateExampleTester.v"
|
||||
))
|
||||
new File(s"$targetDir/GenerateExampleTester.v").exists() should be (true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import chisel3.stage.ChiselStage
|
||||
import firrtl.FileUtils
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
import java.io.{File, PrintWriter}
|
||||
|
||||
class GenerateTopSpec extends AnyFreeSpec with Matchers {
|
||||
"Generate top and harness" - {
|
||||
"should include the following transforms" in {
|
||||
val targetDir = "test_run_dir/generate_top_and_harness"
|
||||
val transformListName = s"$targetDir/ExampleModuleNeesResetInvertTransforms.log"
|
||||
FileUtils.makeDirectory(targetDir)
|
||||
(new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted, Array("--target-dir", targetDir))
|
||||
|
||||
GenerateTopAndHarness.main(
|
||||
Array(
|
||||
"-i", s"$targetDir/ExampleModuleNeedsResetInverted.fir",
|
||||
"-ll", "info",
|
||||
"--log-file", transformListName
|
||||
)
|
||||
)
|
||||
|
||||
val output = FileUtils.getText(transformListName)
|
||||
output should include("barstools.tapeout.transforms.AddSuffixToModuleNames")
|
||||
output should include("barstools.tapeout.transforms.ConvertToExtMod")
|
||||
output should include("barstools.tapeout.transforms.RemoveUnusedModules")
|
||||
output should include("barstools.tapeout.transforms.AvoidExtModuleCollisions")
|
||||
}
|
||||
}
|
||||
|
||||
"generate harness should be generated" ignore {
|
||||
val targetDir = "test_run_dir/generate_top_spec"
|
||||
val logOutputName = s"$targetDir/top_spec_output.log"
|
||||
FileUtils.makeDirectory(targetDir)
|
||||
|
||||
val input = FileUtils.getLinesResource("/BlackBoxFloatTester.fir")
|
||||
val printWriter = new PrintWriter(new File(s"$targetDir/BlackBoxFloatTester.fir"))
|
||||
printWriter.write(input.mkString("\n"))
|
||||
printWriter.close()
|
||||
|
||||
println(s"""Resource: ${input.mkString("\n")}""")
|
||||
|
||||
GenerateTopAndHarness.main(
|
||||
Array(
|
||||
"--target-dir", "test_run_dir/generate_top_spec",
|
||||
"-i", s"$targetDir/BlackBoxFloatTester.fir",
|
||||
"-o", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.v",
|
||||
"-tho", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.v",
|
||||
"-i", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.fir",
|
||||
"--syn-top", "UnitTestSuite",
|
||||
"--harness-top", "TestHarness",
|
||||
"-faf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.anno.json",
|
||||
"-tsaof", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.anno.json",
|
||||
"-tdf", "firrtl_black_box_resource_files.top.f",
|
||||
"-tsf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.fir",
|
||||
"-thaof", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.anno.json",
|
||||
"-hdf", "firrtl_black_box_resource_files.harness.f",
|
||||
"-thf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.fir",
|
||||
"--infer-rw",
|
||||
"--repl-seq-mem", "-c:TestHarness:-o:chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.mems.conf",
|
||||
"-thconf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.mems.conf",
|
||||
"-td", "test_run_dir/from-ci",
|
||||
"-ll", "info",
|
||||
"--log-file", logOutputName
|
||||
)
|
||||
)
|
||||
|
||||
val output = FileUtils.getText(logOutputName)
|
||||
println(output)
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
|
||||
|
||||
class ResetNSpec extends AnyFreeSpec with Matchers {
|
||||
"Inverting reset needs to be done throughout module in Chirrtl" in {
|
||||
val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted)
|
||||
val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted, Array("--target-dir", "test_run_dir/reset_n_spec"))
|
||||
chirrtl should include("input reset :")
|
||||
(chirrtl should not).include("input reset_n :")
|
||||
(chirrtl should not).include("node reset = not(reset_n)")
|
||||
@@ -32,7 +32,7 @@ class ResetNSpec extends AnyFreeSpec with Matchers {
|
||||
// generate low-firrtl
|
||||
val firrtl = (new ChiselStage)
|
||||
.execute(
|
||||
Array("-X", "low"),
|
||||
Array("-X", "low", "--target-dir", "test_run_dir/reset_inverting_spec"),
|
||||
Seq(ChiselGeneratorAnnotation(() => new ExampleModuleNeedsResetInverted))
|
||||
)
|
||||
.collect {
|
||||
101
src/test/scala/mdf/macrolib/ConfReaderSpec.scala
Normal file
101
src/test/scala/mdf/macrolib/ConfReaderSpec.scala
Normal file
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
15
src/test/scala/mdf/macrolib/FlipChipMacroSpec.scala
Normal file
15
src/test/scala/mdf/macrolib/FlipChipMacroSpec.scala
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/test/scala/mdf/macrolib/IOMacroSpec.scala
Normal file
67
src/test/scala/mdf/macrolib/IOMacroSpec.scala
Normal file
@@ -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"))
|
||||
)
|
||||
}
|
||||
}
|
||||
15
src/test/scala/mdf/macrolib/IOPropertiesSpec.scala
Normal file
15
src/test/scala/mdf/macrolib/IOPropertiesSpec.scala
Normal file
@@ -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)) =>
|
||||
}
|
||||
}
|
||||
}
|
||||
270
src/test/scala/mdf/macrolib/MacroLibOutput.scala
Normal file
270
src/test/scala/mdf/macrolib/MacroLibOutput.scala
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
406
src/test/scala/mdf/macrolib/MacroLibSpec.scala
Normal file
406
src/test/scala/mdf/macrolib/MacroLibSpec.scala
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import firrtl.FileUtils
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
import java.io.{ByteArrayOutputStream, File, PrintStream, PrintWriter}
|
||||
|
||||
class GenerateTopSpec extends AnyFreeSpec with Matchers {
|
||||
"Generate top and harness" - {
|
||||
"should include the following transforms" in {
|
||||
val buffer = new ByteArrayOutputStream()
|
||||
Console.withOut(new PrintStream(buffer)) {
|
||||
GenerateTopAndHarness.main(Array("-i", "ExampleModuleNeedsResetInverted.fir", "-ll", "info"))
|
||||
}
|
||||
val output = buffer.toString
|
||||
output should include("barstools.tapeout.transforms.AddSuffixToModuleNames")
|
||||
output should include("barstools.tapeout.transforms.ConvertToExtMod")
|
||||
output should include("barstools.tapeout.transforms.RemoveUnusedModules")
|
||||
output should include("barstools.tapeout.transforms.AvoidExtModuleCollisions")
|
||||
}
|
||||
}
|
||||
|
||||
"generate harness should " ignore {
|
||||
val targetDir = "test_run_dir/generate_top_spec"
|
||||
FileUtils.makeDirectory(targetDir)
|
||||
|
||||
val stream = getClass.getResourceAsStream("/BlackBoxFloatTester.fir")
|
||||
val input = scala.io.Source.fromInputStream(stream).getLines()
|
||||
val printWriter = new PrintWriter(new File(s"$targetDir/BlackBoxFloatTester.fir"))
|
||||
printWriter.write(input.mkString("\n"))
|
||||
printWriter.close()
|
||||
|
||||
println(s"""Resource: ${input.mkString("\n")}""")
|
||||
|
||||
|
||||
// val buffer = new ByteArrayOutputStream()
|
||||
// Console.withOut(new PrintStream(buffer)) {
|
||||
GenerateTopAndHarness.main(
|
||||
Array(
|
||||
"--target-dir", "test_run_dir/generate_top_spec",
|
||||
"-i", s"$targetDir/BlackBoxFloatTester.fir",
|
||||
"-o",
|
||||
"chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.v",
|
||||
"-tho", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.v",
|
||||
"-i", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.fir",
|
||||
"--syn-top", "UnitTestSuite",
|
||||
"--harness-top", "TestHarness",
|
||||
"-faf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.anno.json",
|
||||
"-tsaof", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.anno.json",
|
||||
"-tdf", "firrtl_black_box_resource_files.top.f",
|
||||
"-tsf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.fir",
|
||||
"-thaof", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.anno.json",
|
||||
"-hdf", "firrtl_black_box_resource_files.harness.f",
|
||||
"-thf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.fir",
|
||||
"--infer-rw",
|
||||
"--repl-seq-mem", "-c:TestHarness:-o:chipyard.unittest.TestHarness.IceNetUnitTestConfig.top.mems.conf",
|
||||
"-thconf", "chipyard.unittest.TestHarness.IceNetUnitTestConfig.harness.mems.conf",
|
||||
"-td", "test_run_dir/from-ci",
|
||||
"-ll", "info"
|
||||
)
|
||||
)
|
||||
}
|
||||
// val output = buffer.toString
|
||||
// println(output)
|
||||
}
|
||||
Reference in New Issue
Block a user