diff options
| author | John Wright | 2019-03-07 15:34:33 -0800 |
|---|---|---|
| committer | mergify[bot] | 2019-03-07 23:34:33 +0000 |
| commit | a97a81bc0f717f80bb70733795ac5337653b58c5 (patch) | |
| tree | 23ea1801f2767aee9d26a3c5588891cee4d2aab8 /src/main | |
| parent | 0ac1fa56da06e6b0590ed05ab1ea047188d54602 (diff) | |
Add a data structure for memory conf reading and writing (#1041)
* Copy MemConf.scala from ucb-bar/barstools#35 into memlib.
This provides a data structure wrapper around the existing memory conf format
which contains both reading and writing methods, making it easier to write code
that needs to read the format.
* Add MemConf tests and use a Map[MemPort, Int] for port lists instead of a Seq[MemPort] which is a bit less fragile.
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/scala/firrtl/passes/memlib/MemConf.scala | 69 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala | 14 |
2 files changed, 74 insertions, 9 deletions
diff --git a/src/main/scala/firrtl/passes/memlib/MemConf.scala b/src/main/scala/firrtl/passes/memlib/MemConf.scala new file mode 100644 index 00000000..55600bf6 --- /dev/null +++ b/src/main/scala/firrtl/passes/memlib/MemConf.scala @@ -0,0 +1,69 @@ +// See LICENSE for license details. + +package firrtl.passes +package memlib + +import scala.util.matching._ + +sealed abstract class MemPort(val name: String) { override def toString = name } + +case object ReadPort extends MemPort("read") +case object WritePort extends MemPort("write") +case object MaskedWritePort extends MemPort("mwrite") +case object ReadWritePort extends MemPort("rw") +case object MaskedReadWritePort extends MemPort("mrw") + +object MemPort { + + val all = Set(ReadPort, WritePort, MaskedWritePort, ReadWritePort, MaskedReadWritePort) + + def apply(s: String): Option[MemPort] = MemPort.all.find(_.name == s) + + def fromString(s: String): Map[MemPort, Int] = { + s.split(",").toSeq.map(MemPort.apply).map(_ match { + case Some(x) => x + case _ => throw new Exception(s"Error parsing MemPort string : ${s}") + }).groupBy(identity).mapValues(_.size) + } +} + +case class MemConf( + name: String, + depth: Int, + width: Int, + ports: Map[MemPort, Int], + maskGranularity: Option[Int] +) { + + private def portsStr = ports.map { case (port, num) => Seq.fill(num)(port.name).mkString(",") } mkString (",") + private def maskGranStr = maskGranularity.map((p) => s"mask_gran $p").getOrElse("") + + // Assert that all of the entries in the port map are greater than zero to make it easier to compare two of these case classes + // (otherwise an entry of XYZPort -> 0 would not be equivalent to another with no XYZPort despite being semantically the same) + ports.foreach { case (k, v) => require(v > 0, "Cannot have negative or zero entry in the port map") } + + override def toString = s"name ${name} depth ${depth} width ${width} ports ${portsStr} ${maskGranStr} \n" +} + +object MemConf { + + val regex = raw"\s*name\s+(\w+)\s+depth\s+(\d+)\s+width\s+(\d+)\s+ports\s+([^\s]+)\s+(?:mask_gran\s+(\d+))?\s*".r + + def fromString(s: String): Seq[MemConf] = { + s.split("\n").toSeq.map(_ match { + case MemConf.regex(name, depth, width, ports, maskGran) => MemConf(name, depth.toInt, width.toInt, MemPort.fromString(ports), Option(maskGran).map(_.toInt)) + case _ => throw new Exception(s"Error parsing MemConf string : ${s}") + }) + } + + def apply(name: String, depth: Int, width: Int, readPorts: Int, writePorts: Int, readWritePorts: Int, maskGranularity: Option[Int]): MemConf = { + val ports: Map[MemPort, Int] = (if (maskGranularity.isEmpty) { + (if (writePorts == 0) Map.empty[MemPort, Int] else Map(WritePort -> writePorts)) ++ + (if (readWritePorts == 0) Map.empty[MemPort, Int] else Map(ReadWritePort -> readWritePorts)) + } else { + (if (writePorts == 0) Map.empty[MemPort, Int] else Map(MaskedWritePort -> writePorts)) ++ + (if (readWritePorts == 0) Map.empty[MemPort, Int] else Map(MaskedReadWritePort -> readWritePorts)) + }) ++ (if (readPorts == 0) Map.empty[MemPort, Int] else Map(ReadPort -> readPorts)) + return new MemConf(name, depth, width, ports, maskGranularity) + } +} diff --git a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala index 643f63c6..1f8e89be 100644 --- a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala +++ b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala @@ -50,15 +50,11 @@ class ConfWriter(filename: String) { val outputBuffer = new CharArrayWriter def append(m: DefAnnotatedMemory) = { // legacy - val maskGran = m.maskGran - val readers = List.fill(m.readers.length)("read") - val writers = List.fill(m.writers.length)(if (maskGran.isEmpty) "write" else "mwrite") - val readwriters = List.fill(m.readwriters.length)(if (maskGran.isEmpty) "rw" else "mrw") - val ports = (writers ++ readers ++ readwriters) mkString "," - val maskGranConf = maskGran match { case None => "" case Some(p) => s"mask_gran $p" } - val width = bitWidth(m.dataType) - val conf = s"name ${m.name} depth ${m.depth} width $width ports $ports $maskGranConf \n" - outputBuffer.append(conf) + // assert that we don't overflow going from BigInt to Int conversion + require(bitWidth(m.dataType) <= Int.MaxValue) + m.maskGran.foreach { case x => require(x <= Int.MaxValue) } + val conf = MemConf(m.name, m.depth, bitWidth(m.dataType).toInt, m.readers.length, m.writers.length, m.readwriters.length, m.maskGran.map(_.toInt)) + outputBuffer.append(conf.toString) } def serialize() = { val outputFile = new PrintWriter(filename) |
