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?
407 lines
11 KiB
Scala
407 lines
11 KiB
Scala
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
|
|
}
|
|
}
|