Correct multi-ported memory compilation (#27)
* Correct multi-ported memory compilation It was incorrectly splitting multiple times before. Fixed the issue and added regression tests for this issue. * Add 1 read 1 write test
This commit is contained in:
@@ -91,6 +91,17 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
libs: Option[Seq[Macro]],
|
||||
costMetric: CostMetric = CostMetric.default,
|
||||
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass {
|
||||
// Helper function to check the legality of bitPairs.
|
||||
// e.g. ((0,21), (22,43)) is legal
|
||||
// ((0,21), (22,21)) is illegal and will throw an assert
|
||||
private def checkBitPairs(bitPairs: Seq[(BigInt, BigInt)]): Unit = {
|
||||
bitPairs.foldLeft(BigInt(-1))((lastBit, nextPair) => {
|
||||
assert(lastBit + 1 == nextPair._1, s"Pair's first bit ${nextPair._1} does not follow last bit ${lastBit}");
|
||||
assert(nextPair._2 >= nextPair._1, s"Pair ${nextPair} in bitPairs ${bitPairs} is illegal");
|
||||
nextPair._2
|
||||
})
|
||||
}
|
||||
|
||||
def compile(mem: Macro, lib: Macro): Option[(Module, ExtModule)] = {
|
||||
val pairedPorts = mem.sortedPorts zip lib.sortedPorts
|
||||
|
||||
@@ -103,23 +114,33 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
* width=8 memories.
|
||||
*/
|
||||
val bitPairs = ArrayBuffer[(BigInt, BigInt)]()
|
||||
var currentLSB = 0
|
||||
var currentLSB: BigInt = 0
|
||||
|
||||
// Process every bit in the mem width.
|
||||
for (memBit <- 0 until mem.src.width) {
|
||||
val bitsInCurrentMem = memBit - currentLSB
|
||||
|
||||
// Helper function to check if it's time to split memories.
|
||||
// @param effectiveLibWidth Split memory when we have this many bits.
|
||||
def splitMemory(effectiveLibWidth: Int): Unit = {
|
||||
if (bitsInCurrentMem == effectiveLibWidth) {
|
||||
bitPairs += ((currentLSB, memBit - 1))
|
||||
currentLSB = memBit
|
||||
}
|
||||
}
|
||||
|
||||
// We'll need to find a bitPair that works for *all* the ports of the memory.
|
||||
// e.g. unmasked read port and masked write port.
|
||||
// For each port, store a tentative candidate for the split.
|
||||
// Afterwards, figure out which one to use.
|
||||
val bitPairCandidates = ArrayBuffer[(BigInt, BigInt)]()
|
||||
for ((memPort, libPort) <- pairedPorts) {
|
||||
|
||||
// Sanity check to make sure we only split once per bit, once per port.
|
||||
var alreadySplit: Boolean = false
|
||||
|
||||
// Helper function to check if it's time to split memories.
|
||||
// @param effectiveLibWidth Split memory when we have this many bits.
|
||||
def splitMemory(effectiveLibWidth: Int): Unit = {
|
||||
assert (!alreadySplit)
|
||||
|
||||
if (bitsInCurrentMem == effectiveLibWidth) {
|
||||
bitPairCandidates += ((currentLSB, memBit - 1))
|
||||
alreadySplit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we don't have a maskGran larger than the width of the memory.
|
||||
assert (memPort.src.effectiveMaskGran <= memPort.src.width)
|
||||
assert (libPort.src.effectiveMaskGran <= libPort.src.width)
|
||||
@@ -204,9 +225,23 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Choose an actual bit pair to add.
|
||||
// We'll have to choose the smallest one (e.g. unmasked read port might be more tolerant of a bigger split than the masked write port).
|
||||
if (bitPairCandidates.length == 0) {
|
||||
// No pair needed to split, just continue
|
||||
} else {
|
||||
val bestPair = bitPairCandidates.reduceLeft((leftPair, rightPair) => {
|
||||
if (leftPair._2 - leftPair._1 + 1 > rightPair._2 - rightPair._1 + 1) leftPair else rightPair
|
||||
})
|
||||
bitPairs += bestPair
|
||||
currentLSB = bestPair._2 + BigInt(1) // advance the LSB pointer
|
||||
}
|
||||
}
|
||||
// Add in the last chunk if there are any leftovers
|
||||
bitPairs += ((currentLSB, mem.src.width.toInt - 1))
|
||||
// Check bit pairs
|
||||
checkBitPairs(bitPairs)
|
||||
|
||||
// Depth mapping
|
||||
val stmts = ArrayBuffer[Statement]()
|
||||
|
||||
1165
macros/src/test/resources/lib-BOOMTest.json
Normal file
1165
macros/src/test/resources/lib-BOOMTest.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -301,47 +301,77 @@ trait HasSimpleTestGenerator {
|
||||
} else ""
|
||||
}
|
||||
|
||||
/** 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 ""
|
||||
val maskStr = mask match {
|
||||
case Some(maskBits: Int) => s"input ${prefix}_mask : UInt<${maskBits}>"
|
||||
case _ => ""
|
||||
}
|
||||
s"""
|
||||
input ${prefix}_clk : Clock
|
||||
input ${prefix}_addr : UInt<$addrWidth>
|
||||
${writeStr}
|
||||
${readStr}
|
||||
${readEnableStr}
|
||||
${writeEnableStr}
|
||||
${maskStr}
|
||||
"""
|
||||
}
|
||||
|
||||
/** 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 = {
|
||||
generatePort(libPortPrefix, lib_addr_width, libWidth,
|
||||
write=true, writeEnable=true, read=true, readEnable=readEnable, mask)
|
||||
}
|
||||
|
||||
/** Helper function to generate a RW header 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 generateReadWriteHeaderPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = {
|
||||
generatePort(memPortPrefix, mem_addr_width, memWidth,
|
||||
write=true, writeEnable=true, read=true, readEnable=readEnable, mask)
|
||||
}
|
||||
|
||||
// Generate the header memory ports.
|
||||
def generateHeaderPorts(): String = {
|
||||
require (memSRAM.ports.size == 1, "Header generator only supports single RW port mem")
|
||||
generateReadWriteHeaderPort(memPortPrefix, memSRAM.ports(0).readEnable.isDefined, if (memHasMask) Some(memMaskBits) else None)
|
||||
}
|
||||
|
||||
// Generate the header (contains the circuit statement and the target memory
|
||||
// module.
|
||||
def generateHeader(): String = {
|
||||
require (memSRAM.ports.size == 1, "Header generator only supports single port mem")
|
||||
|
||||
val readEnable = if (memSRAM.ports(0).readEnable.isDefined) s"input ${memPortPrefix}_read_en : UInt<1>" else ""
|
||||
val headerMask = if (memHasMask) s"input ${memPortPrefix}_mask : UInt<${memMaskBits}>" else ""
|
||||
s"""
|
||||
circuit $mem_name :
|
||||
module $mem_name :
|
||||
input ${memPortPrefix}_clk : Clock
|
||||
input ${memPortPrefix}_addr : UInt<$mem_addr_width>
|
||||
input ${memPortPrefix}_din : UInt<$memWidth>
|
||||
output ${memPortPrefix}_dout : UInt<$memWidth>
|
||||
${readEnable}
|
||||
input ${memPortPrefix}_write_en : UInt<1>
|
||||
${headerMask}
|
||||
${generateHeaderPorts}
|
||||
"""
|
||||
}
|
||||
|
||||
// Generate the target memory ports.
|
||||
def generateFooterPorts(): String = {
|
||||
require (libSRAM.ports.size == 1, "Footer generator only supports single port lib")
|
||||
|
||||
val readEnable = if (libSRAM.ports(0).readEnable.isDefined) s"input ${libPortPrefix}_read_en : UInt<1>" else ""
|
||||
val footerMask = if (libHasMask) s"input ${libPortPrefix}_mask : UInt<${libMaskBits}>" else ""
|
||||
s"""
|
||||
input ${libPortPrefix}_clk : Clock
|
||||
input ${libPortPrefix}_addr : UInt<$lib_addr_width>
|
||||
input ${libPortPrefix}_din : UInt<$libWidth>
|
||||
output ${libPortPrefix}_dout : UInt<$libWidth>
|
||||
${readEnable}
|
||||
input ${libPortPrefix}_write_en : UInt<1>
|
||||
${footerMask}
|
||||
"""
|
||||
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)
|
||||
}
|
||||
|
||||
// Generate the footer (contains the target memory extmodule declaration by default).
|
||||
def generateFooter(): String = {
|
||||
require (libSRAM.ports.size == 1, "Footer generator only supports single port lib")
|
||||
|
||||
s"""
|
||||
extmodule $lib_name :
|
||||
${generateFooterPorts}
|
||||
|
||||
392
macros/src/test/scala/MultiPort.scala
Normal file
392
macros/src/test/scala/MultiPort.scala
Normal file
@@ -0,0 +1,392 @@
|
||||
package barstools.macros
|
||||
|
||||
// Test that the memory compiler works fine for compiling multi-port memories.
|
||||
// TODO: extend test generator to also automatically generate multi-ported memories.
|
||||
|
||||
class SplitWidth_2rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val depth = 1024
|
||||
override lazy val memWidth = 64
|
||||
override lazy val memMaskGran = Some(16)
|
||||
override lazy val libWidth = 16
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", memWidth, memDepth, maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", memWidth, memDepth, maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def generateHeaderPorts() = {
|
||||
generateReadWriteHeaderPort("portA", true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort("portB", true, Some(memMaskBits))
|
||||
}
|
||||
|
||||
override def generateFooterPorts() = {
|
||||
generateReadWriteFooterPort("portA", true, None) + "\n" + generateReadWriteFooterPort("portB", true, None)
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
inst mem_0_3 of awesome_lib_mem
|
||||
mem_0_0.portA_clk <= portA_clk
|
||||
mem_0_0.portA_addr <= portA_addr
|
||||
node portA_dout_0_0 = bits(mem_0_0.portA_dout, 15, 0)
|
||||
mem_0_0.portA_din <= bits(portA_din, 15, 0)
|
||||
mem_0_0.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_0.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 0, 0)), UInt<1>("h1"))
|
||||
mem_0_1.portA_clk <= portA_clk
|
||||
mem_0_1.portA_addr <= portA_addr
|
||||
node portA_dout_0_1 = bits(mem_0_1.portA_dout, 15, 0)
|
||||
mem_0_1.portA_din <= bits(portA_din, 31, 16)
|
||||
mem_0_1.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_1.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 1, 1)), UInt<1>("h1"))
|
||||
mem_0_2.portA_clk <= portA_clk
|
||||
mem_0_2.portA_addr <= portA_addr
|
||||
node portA_dout_0_2 = bits(mem_0_2.portA_dout, 15, 0)
|
||||
mem_0_2.portA_din <= bits(portA_din, 47, 32)
|
||||
mem_0_2.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_2.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 2, 2)), UInt<1>("h1"))
|
||||
mem_0_3.portA_clk <= portA_clk
|
||||
mem_0_3.portA_addr <= portA_addr
|
||||
node portA_dout_0_3 = bits(mem_0_3.portA_dout, 15, 0)
|
||||
mem_0_3.portA_din <= bits(portA_din, 63, 48)
|
||||
mem_0_3.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_3.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 3, 3)), UInt<1>("h1"))
|
||||
node portA_dout_0 = cat(portA_dout_0_3, cat(portA_dout_0_2, cat(portA_dout_0_1, portA_dout_0_0)))
|
||||
mem_0_0.portB_clk <= portB_clk
|
||||
mem_0_0.portB_addr <= portB_addr
|
||||
node portB_dout_0_0 = bits(mem_0_0.portB_dout, 15, 0)
|
||||
mem_0_0.portB_din <= bits(portB_din, 15, 0)
|
||||
mem_0_0.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_0.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 0, 0)), UInt<1>("h1"))
|
||||
mem_0_1.portB_clk <= portB_clk
|
||||
mem_0_1.portB_addr <= portB_addr
|
||||
node portB_dout_0_1 = bits(mem_0_1.portB_dout, 15, 0)
|
||||
mem_0_1.portB_din <= bits(portB_din, 31, 16)
|
||||
mem_0_1.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_1.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 1, 1)), UInt<1>("h1"))
|
||||
mem_0_2.portB_clk <= portB_clk
|
||||
mem_0_2.portB_addr <= portB_addr
|
||||
node portB_dout_0_2 = bits(mem_0_2.portB_dout, 15, 0)
|
||||
mem_0_2.portB_din <= bits(portB_din, 47, 32)
|
||||
mem_0_2.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_2.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 2, 2)), UInt<1>("h1"))
|
||||
mem_0_3.portB_clk <= portB_clk
|
||||
mem_0_3.portB_addr <= portB_addr
|
||||
node portB_dout_0_3 = bits(mem_0_3.portB_dout, 15, 0)
|
||||
mem_0_3.portB_din <= bits(portB_din, 63, 48)
|
||||
mem_0_3.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_3.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 3, 3)), UInt<1>("h1"))
|
||||
node portB_dout_0 = cat(portB_dout_0_3, cat(portB_dout_0_2, cat(portB_dout_0_1, portB_dout_0_0)))
|
||||
portA_dout <= mux(UInt<1>("h1"), portA_dout_0, UInt<1>("h0"))
|
||||
portB_dout <= mux(UInt<1>("h1"), portB_dout_0, UInt<1>("h0"))
|
||||
"""
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth_1r_1w extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val depth = 1024
|
||||
override lazy val memWidth = 64
|
||||
override lazy val memMaskGran = Some(16)
|
||||
override lazy val libWidth = 16
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", memWidth, memDepth, maskGran=memMaskGran,
|
||||
write=false, writeEnable=false,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", memWidth, memDepth, maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=false, readEnable=false
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", libWidth, libDepth,
|
||||
write=false, writeEnable=false,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=false, readEnable=false
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def generateHeaderPorts() = {
|
||||
generatePort("portA", mem_addr_width, memWidth,
|
||||
write=false, writeEnable=false, read=true, readEnable=true, Some(memMaskBits)) + "\n" +
|
||||
generatePort("portB", mem_addr_width, memWidth,
|
||||
write=true, writeEnable=true, read=false, readEnable=false, Some(memMaskBits))
|
||||
}
|
||||
|
||||
override def generateFooterPorts() = {
|
||||
generatePort("portA", lib_addr_width, libWidth,
|
||||
write=false, writeEnable=false, read=true, readEnable=true, None) + "\n" +
|
||||
generatePort("portB", lib_addr_width, libWidth,
|
||||
write=true, writeEnable=true, read=false, readEnable=false, None)
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
inst mem_0_3 of awesome_lib_mem
|
||||
mem_0_0.portB_clk <= portB_clk
|
||||
mem_0_0.portB_addr <= portB_addr
|
||||
mem_0_0.portB_din <= bits(portB_din, 15, 0)
|
||||
mem_0_0.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 0, 0)), UInt<1>("h1"))
|
||||
mem_0_1.portB_clk <= portB_clk
|
||||
mem_0_1.portB_addr <= portB_addr
|
||||
mem_0_1.portB_din <= bits(portB_din, 31, 16)
|
||||
mem_0_1.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 1, 1)), UInt<1>("h1"))
|
||||
mem_0_2.portB_clk <= portB_clk
|
||||
mem_0_2.portB_addr <= portB_addr
|
||||
mem_0_2.portB_din <= bits(portB_din, 47, 32)
|
||||
mem_0_2.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 2, 2)), UInt<1>("h1"))
|
||||
mem_0_3.portB_clk <= portB_clk
|
||||
mem_0_3.portB_addr <= portB_addr
|
||||
mem_0_3.portB_din <= bits(portB_din, 63, 48)
|
||||
mem_0_3.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 3, 3)), UInt<1>("h1"))
|
||||
mem_0_0.portA_clk <= portA_clk
|
||||
mem_0_0.portA_addr <= portA_addr
|
||||
node portA_dout_0_0 = bits(mem_0_0.portA_dout, 15, 0)
|
||||
mem_0_0.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_1.portA_clk <= portA_clk
|
||||
mem_0_1.portA_addr <= portA_addr
|
||||
node portA_dout_0_1 = bits(mem_0_1.portA_dout, 15, 0)
|
||||
mem_0_1.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_2.portA_clk <= portA_clk
|
||||
mem_0_2.portA_addr <= portA_addr
|
||||
node portA_dout_0_2 = bits(mem_0_2.portA_dout, 15, 0)
|
||||
mem_0_2.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_3.portA_clk <= portA_clk
|
||||
mem_0_3.portA_addr <= portA_addr
|
||||
node portA_dout_0_3 = bits(mem_0_3.portA_dout, 15, 0)
|
||||
mem_0_3.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
node portA_dout_0 = cat(portA_dout_0_3, cat(portA_dout_0_2, cat(portA_dout_0_1, portA_dout_0_0)))
|
||||
portA_dout <= mux(UInt<1>("h1"), portA_dout_0, UInt<1>("h0"))
|
||||
"""
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth_2rw_differentMasks extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val depth = 1024
|
||||
override lazy val memWidth = 64
|
||||
override lazy val memMaskGran = Some(16)
|
||||
override lazy val libWidth = 16
|
||||
|
||||
lazy val memMaskGranB = 8 // these generators are run at constructor time
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
println(memMaskGranB)
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", memWidth, memDepth, maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", memWidth, memDepth, maskGran=Some(memMaskGranB),
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
override def generateHeaderPorts() = {
|
||||
generateReadWriteHeaderPort("portA", true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort("portB", true, Some(memWidth / memMaskGranB))
|
||||
}
|
||||
|
||||
override def generateFooterPorts() = {
|
||||
generateReadWriteFooterPort("portA", true, None) + "\n" + generateReadWriteFooterPort("portB", true, None)
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
inst mem_0_3 of awesome_lib_mem
|
||||
inst mem_0_4 of awesome_lib_mem
|
||||
inst mem_0_5 of awesome_lib_mem
|
||||
inst mem_0_6 of awesome_lib_mem
|
||||
inst mem_0_7 of awesome_lib_mem
|
||||
mem_0_0.portA_clk <= portA_clk
|
||||
mem_0_0.portA_addr <= portA_addr
|
||||
node portA_dout_0_0 = bits(mem_0_0.portA_dout, 7, 0)
|
||||
mem_0_0.portA_din <= bits(portA_din, 7, 0)
|
||||
mem_0_0.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_0.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 0, 0)), UInt<1>("h1"))
|
||||
mem_0_1.portA_clk <= portA_clk
|
||||
mem_0_1.portA_addr <= portA_addr
|
||||
node portA_dout_0_1 = bits(mem_0_1.portA_dout, 7, 0)
|
||||
mem_0_1.portA_din <= bits(portA_din, 15, 8)
|
||||
mem_0_1.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_1.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 0, 0)), UInt<1>("h1"))
|
||||
mem_0_2.portA_clk <= portA_clk
|
||||
mem_0_2.portA_addr <= portA_addr
|
||||
node portA_dout_0_2 = bits(mem_0_2.portA_dout, 7, 0)
|
||||
mem_0_2.portA_din <= bits(portA_din, 23, 16)
|
||||
mem_0_2.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_2.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 1, 1)), UInt<1>("h1"))
|
||||
mem_0_3.portA_clk <= portA_clk
|
||||
mem_0_3.portA_addr <= portA_addr
|
||||
node portA_dout_0_3 = bits(mem_0_3.portA_dout, 7, 0)
|
||||
mem_0_3.portA_din <= bits(portA_din, 31, 24)
|
||||
mem_0_3.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_3.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 1, 1)), UInt<1>("h1"))
|
||||
mem_0_4.portA_clk <= portA_clk
|
||||
mem_0_4.portA_addr <= portA_addr
|
||||
node portA_dout_0_4 = bits(mem_0_4.portA_dout, 7, 0)
|
||||
mem_0_4.portA_din <= bits(portA_din, 39, 32)
|
||||
mem_0_4.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_4.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 2, 2)), UInt<1>("h1"))
|
||||
mem_0_5.portA_clk <= portA_clk
|
||||
mem_0_5.portA_addr <= portA_addr
|
||||
node portA_dout_0_5 = bits(mem_0_5.portA_dout, 7, 0)
|
||||
mem_0_5.portA_din <= bits(portA_din, 47, 40)
|
||||
mem_0_5.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_5.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 2, 2)), UInt<1>("h1"))
|
||||
mem_0_6.portA_clk <= portA_clk
|
||||
mem_0_6.portA_addr <= portA_addr
|
||||
node portA_dout_0_6 = bits(mem_0_6.portA_dout, 7, 0)
|
||||
mem_0_6.portA_din <= bits(portA_din, 55, 48)
|
||||
mem_0_6.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_6.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 3, 3)), UInt<1>("h1"))
|
||||
mem_0_7.portA_clk <= portA_clk
|
||||
mem_0_7.portA_addr <= portA_addr
|
||||
node portA_dout_0_7 = bits(mem_0_7.portA_dout, 7, 0)
|
||||
mem_0_7.portA_din <= bits(portA_din, 63, 56)
|
||||
mem_0_7.portA_read_en <= and(portA_read_en, UInt<1>("h1"))
|
||||
mem_0_7.portA_write_en <= and(and(portA_write_en, bits(portA_mask, 3, 3)), UInt<1>("h1"))
|
||||
node portA_dout_0 = cat(portA_dout_0_7, cat(portA_dout_0_6, cat(portA_dout_0_5, cat(portA_dout_0_4, cat(portA_dout_0_3, cat(portA_dout_0_2, cat(portA_dout_0_1, portA_dout_0_0)))))))
|
||||
mem_0_0.portB_clk <= portB_clk
|
||||
mem_0_0.portB_addr <= portB_addr
|
||||
node portB_dout_0_0 = bits(mem_0_0.portB_dout, 7, 0)
|
||||
mem_0_0.portB_din <= bits(portB_din, 7, 0)
|
||||
mem_0_0.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_0.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 0, 0)), UInt<1>("h1"))
|
||||
mem_0_1.portB_clk <= portB_clk
|
||||
mem_0_1.portB_addr <= portB_addr
|
||||
node portB_dout_0_1 = bits(mem_0_1.portB_dout, 7, 0)
|
||||
mem_0_1.portB_din <= bits(portB_din, 15, 8)
|
||||
mem_0_1.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_1.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 1, 1)), UInt<1>("h1"))
|
||||
mem_0_2.portB_clk <= portB_clk
|
||||
mem_0_2.portB_addr <= portB_addr
|
||||
node portB_dout_0_2 = bits(mem_0_2.portB_dout, 7, 0)
|
||||
mem_0_2.portB_din <= bits(portB_din, 23, 16)
|
||||
mem_0_2.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_2.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 2, 2)), UInt<1>("h1"))
|
||||
mem_0_3.portB_clk <= portB_clk
|
||||
mem_0_3.portB_addr <= portB_addr
|
||||
node portB_dout_0_3 = bits(mem_0_3.portB_dout, 7, 0)
|
||||
mem_0_3.portB_din <= bits(portB_din, 31, 24)
|
||||
mem_0_3.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_3.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 3, 3)), UInt<1>("h1"))
|
||||
mem_0_4.portB_clk <= portB_clk
|
||||
mem_0_4.portB_addr <= portB_addr
|
||||
node portB_dout_0_4 = bits(mem_0_4.portB_dout, 7, 0)
|
||||
mem_0_4.portB_din <= bits(portB_din, 39, 32)
|
||||
mem_0_4.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_4.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 4, 4)), UInt<1>("h1"))
|
||||
mem_0_5.portB_clk <= portB_clk
|
||||
mem_0_5.portB_addr <= portB_addr
|
||||
node portB_dout_0_5 = bits(mem_0_5.portB_dout, 7, 0)
|
||||
mem_0_5.portB_din <= bits(portB_din, 47, 40)
|
||||
mem_0_5.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_5.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 5, 5)), UInt<1>("h1"))
|
||||
mem_0_6.portB_clk <= portB_clk
|
||||
mem_0_6.portB_addr <= portB_addr
|
||||
node portB_dout_0_6 = bits(mem_0_6.portB_dout, 7, 0)
|
||||
mem_0_6.portB_din <= bits(portB_din, 55, 48)
|
||||
mem_0_6.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_6.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 6, 6)), UInt<1>("h1"))
|
||||
mem_0_7.portB_clk <= portB_clk
|
||||
mem_0_7.portB_addr <= portB_addr
|
||||
node portB_dout_0_7 = bits(mem_0_7.portB_dout, 7, 0)
|
||||
mem_0_7.portB_din <= bits(portB_din, 63, 56)
|
||||
mem_0_7.portB_read_en <= and(portB_read_en, UInt<1>("h1"))
|
||||
mem_0_7.portB_write_en <= and(and(portB_write_en, bits(portB_mask, 7, 7)), UInt<1>("h1"))
|
||||
node portB_dout_0 = cat(portB_dout_0_7, cat(portB_dout_0_6, cat(portB_dout_0_5, cat(portB_dout_0_4, cat(portB_dout_0_3, cat(portB_dout_0_2, cat(portB_dout_0_1, portB_dout_0_0)))))))
|
||||
portA_dout <= mux(UInt<1>("h1"), portA_dout_0, UInt<1>("h0"))
|
||||
portB_dout <= mux(UInt<1>("h1"), portB_dout_0, UInt<1>("h0"))
|
||||
"""
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user