Moved a zillion files all over the place so that everything is now
in tapeout/src in the correct directory corresponding to internal packages. Everything compiles and tests run TODO: - Figure out assembly step for MacroCompiler - Does root project matter?
This commit is contained in:
406
tapeout/src/test/scala/mdf/macrolib/MacroLibSpec.scala
Normal file
406
tapeout/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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user