Fix corner case in compiling a small mem using a large lib (#32)
* Refactor bit pairs calculation into a separate function * Minor clarifications * Clarify MacroCompilerSpec helpers * Add SmallTagArrayTest test * Fix corner case in compiling a small mem using a large lib
This commit is contained in:
@@ -6,6 +6,8 @@ import firrtl.Parser.parse
|
||||
import firrtl.Utils.ceilLog2
|
||||
import java.io.{File, StringWriter}
|
||||
|
||||
import mdf.macrolib.SRAMMacro
|
||||
|
||||
abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalatest.Matchers {
|
||||
import scala.language.implicitConversions
|
||||
implicit def String2SomeString(i: String): Option[String] = Some(i)
|
||||
@@ -228,7 +230,7 @@ trait HasSimpleTestGenerator {
|
||||
// generator.
|
||||
def generatorType: String = this.getClass.getSimpleName
|
||||
|
||||
require (memDepth >= libDepth)
|
||||
//require (memDepth >= libDepth)
|
||||
|
||||
// Convenience variables to check if a mask exists.
|
||||
val memHasMask = memMaskGran != None
|
||||
@@ -258,11 +260,14 @@ trait HasSimpleTestGenerator {
|
||||
def generateLibSRAM() = generateSRAM(lib_name, libPortPrefix, libWidth, libDepth, libMaskGran, extraPorts)
|
||||
def generateMemSRAM() = generateSRAM(mem_name, memPortPrefix, memWidth, memDepth, memMaskGran)
|
||||
|
||||
val libSRAM = generateLibSRAM
|
||||
val memSRAM = generateMemSRAM
|
||||
def libSRAM = generateLibSRAM
|
||||
def memSRAM = generateMemSRAM
|
||||
|
||||
writeToLib(lib, Seq(libSRAM))
|
||||
writeToMem(mem, Seq(memSRAM))
|
||||
def libSRAMs: Seq[SRAMMacro] = Seq(libSRAM)
|
||||
def memSRAMs: Seq[SRAMMacro] = Seq(memSRAM)
|
||||
|
||||
writeToLib(lib, libSRAMs)
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
// For masks, width it's a bit tricky since we have to consider cases like
|
||||
// memMaskGran = 4 and libMaskGran = 8.
|
||||
@@ -321,41 +326,52 @@ trait HasSimpleTestGenerator {
|
||||
}
|
||||
|
||||
/** Helper function to generate a port.
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param addrWidth Address port width
|
||||
* @param width data width
|
||||
* @param write Has a write port?
|
||||
* @param writeEnable Has a write enable port?
|
||||
* @param read Has a read port?
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None. */
|
||||
def generatePort(prefix: String, addrWidth: Int, width: Int, write: Boolean, writeEnable: Boolean, read: Boolean, readEnable: Boolean, mask: Option[Int]): String = {
|
||||
val readStr = if (read) s"output ${prefix}_dout : UInt<$width>" else ""
|
||||
val writeStr = if (write) s"input ${prefix}_din : UInt<$width>" else ""
|
||||
val readEnableStr = if (readEnable) s"input ${prefix}_read_en : UInt<1>" else ""
|
||||
val writeEnableStr = if (writeEnable) s"input ${prefix}_write_en : UInt<1>" else ""
|
||||
*
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param addrWidth Address port width
|
||||
* @param width data width
|
||||
* @param write Has a write port?
|
||||
* @param writeEnable Has a write enable port?
|
||||
* @param read Has a read port?
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
* @param extraPorts Extra ports (name, # bits)
|
||||
*/
|
||||
def generatePort(prefix: String, addrWidth: Int, width: Int, write: Boolean, writeEnable: Boolean, read: Boolean, readEnable: Boolean, mask: Option[Int], extraPorts: Seq[(String, Int)] = Seq()): String = {
|
||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||
|
||||
val readStr = if (read) s"output ${realPrefix}dout : UInt<$width>" else ""
|
||||
val writeStr = if (write) s"input ${realPrefix}din : UInt<$width>" else ""
|
||||
val readEnableStr = if (readEnable) s"input ${realPrefix}read_en : UInt<1>" else ""
|
||||
val writeEnableStr = if (writeEnable) s"input ${realPrefix}write_en : UInt<1>" else ""
|
||||
val maskStr = mask match {
|
||||
case Some(maskBits: Int) => s"input ${prefix}_mask : UInt<${maskBits}>"
|
||||
case Some(maskBits: Int) => s"input ${realPrefix}mask : UInt<$maskBits>"
|
||||
case _ => ""
|
||||
}
|
||||
s"""
|
||||
input ${prefix}_clk : Clock
|
||||
input ${prefix}_addr : UInt<$addrWidth>
|
||||
${writeStr}
|
||||
${readStr}
|
||||
${readEnableStr}
|
||||
${writeEnableStr}
|
||||
${maskStr}
|
||||
"""
|
||||
val extraPortsStr = extraPorts.map { case (name, bits) => s" input $name : UInt<$bits>" }.mkString("\n")
|
||||
s"""
|
||||
input ${realPrefix}clk : Clock
|
||||
input ${realPrefix}addr : UInt<$addrWidth>
|
||||
$writeStr
|
||||
$readStr
|
||||
$readEnableStr
|
||||
$writeEnableStr
|
||||
$maskStr
|
||||
$extraPortsStr
|
||||
"""
|
||||
}
|
||||
|
||||
/** Helper function to generate a RW footer port.
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None. */
|
||||
def generateReadWriteFooterPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = {
|
||||
/**
|
||||
* Helper function to generate a RW footer port.
|
||||
*
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
* @param extraPorts Extra ports (name, # bits)
|
||||
*/
|
||||
def generateReadWriteFooterPort(prefix: String, readEnable: Boolean, mask: Option[Int], extraPorts: Seq[(String, Int)] = Seq()): String = {
|
||||
generatePort(prefix, lib_addr_width, libWidth,
|
||||
write=true, writeEnable=true, read=true, readEnable=readEnable, mask)
|
||||
write = true, writeEnable = true, read = true, readEnable = readEnable, mask = mask, extraPorts = extraPorts)
|
||||
}
|
||||
|
||||
/** Helper function to generate a RW header port.
|
||||
@@ -385,8 +401,9 @@ ${generateHeaderPorts}
|
||||
|
||||
// Generate the target memory ports.
|
||||
def generateFooterPorts(): String = {
|
||||
require (libSRAM.ports.size == 1, "Footer generator only supports single RW port mem")
|
||||
generateReadWriteFooterPort(libPortPrefix, libSRAM.ports(0).readEnable.isDefined, if (libHasMask) Some(libMaskBits) else None)
|
||||
require(libSRAM.ports.size == 1, "Footer generator only supports single RW port mem")
|
||||
generateReadWriteFooterPort(libPortPrefix, libSRAM.ports(0).readEnable.isDefined,
|
||||
if (libHasMask) Some(libMaskBits) else None, extraPorts.map(p => (p.name, p.width)))
|
||||
}
|
||||
|
||||
// Generate the footer (contains the target memory extmodule declaration by default).
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// See LICENSE for license details.
|
||||
package barstools.macros
|
||||
|
||||
import mdf.macrolib._
|
||||
@@ -1232,6 +1233,39 @@ circuit smem_0_ext :
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleTestGenerator {
|
||||
// Test that mapping a smaller memory using a larger lib can still work.
|
||||
override def memWidth: Int = 26
|
||||
override def memDepth: Int = 2
|
||||
override def memMaskGran: Option[Int] = Some(26)
|
||||
override def memPortPrefix: String = ""
|
||||
|
||||
override def libWidth: Int = 32
|
||||
override def libDepth: Int = 64
|
||||
override def libMaskGran: Option[Int] = Some(1)
|
||||
override def libPortPrefix: String = ""
|
||||
|
||||
override def extraPorts: Seq[MacroExtraPort] = Seq(
|
||||
MacroExtraPort(name = "must_be_one", portType = Constant, width = 1, value = 1)
|
||||
)
|
||||
|
||||
override def generateBody(): String =
|
||||
s"""
|
||||
| inst mem_0_0 of $lib_name
|
||||
| mem_0_0.must_be_one <= UInt<1>("h1")
|
||||
| mem_0_0.clk <= clk
|
||||
| mem_0_0.addr <= addr
|
||||
| node dout_0_0 = bits(mem_0_0.dout, 25, 0)
|
||||
| mem_0_0.din <= bits(din, 25, 0)
|
||||
| mem_0_0.mask <= cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(UInt<1>("h0"), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), cat(bits(mask, 0, 0), bits(mask, 0, 0))))))))))))))))))))))))))))))))
|
||||
| mem_0_0.write_en <= and(and(write_en, UInt<1>("h1")), UInt<1>("h1"))
|
||||
| node dout_0 = dout_0_0
|
||||
| dout <= mux(UInt<1>("h1"), dout_0, UInt<1>("h0"))
|
||||
""".stripMargin
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
val mem = s"mem-RocketChipTest.json"
|
||||
val lib = s"lib-RocketChipTest.json"
|
||||
|
||||
Reference in New Issue
Block a user