Files
chipyard/src/main/scala/mdf/macrolib/ConfReader.scala
2021-08-10 15:40:58 -07:00

96 lines
3.7 KiB
Scala

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(_))
}
}