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]],
|
libs: Option[Seq[Macro]],
|
||||||
costMetric: CostMetric = CostMetric.default,
|
costMetric: CostMetric = CostMetric.default,
|
||||||
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass {
|
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)] = {
|
def compile(mem: Macro, lib: Macro): Option[(Module, ExtModule)] = {
|
||||||
val pairedPorts = mem.sortedPorts zip lib.sortedPorts
|
val pairedPorts = mem.sortedPorts zip lib.sortedPorts
|
||||||
|
|
||||||
@@ -103,23 +114,33 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
* width=8 memories.
|
* width=8 memories.
|
||||||
*/
|
*/
|
||||||
val bitPairs = ArrayBuffer[(BigInt, BigInt)]()
|
val bitPairs = ArrayBuffer[(BigInt, BigInt)]()
|
||||||
var currentLSB = 0
|
var currentLSB: BigInt = 0
|
||||||
|
|
||||||
// Process every bit in the mem width.
|
// Process every bit in the mem width.
|
||||||
for (memBit <- 0 until mem.src.width) {
|
for (memBit <- 0 until mem.src.width) {
|
||||||
val bitsInCurrentMem = memBit - currentLSB
|
val bitsInCurrentMem = memBit - currentLSB
|
||||||
|
|
||||||
// Helper function to check if it's time to split memories.
|
// We'll need to find a bitPair that works for *all* the ports of the memory.
|
||||||
// @param effectiveLibWidth Split memory when we have this many bits.
|
// e.g. unmasked read port and masked write port.
|
||||||
def splitMemory(effectiveLibWidth: Int): Unit = {
|
// For each port, store a tentative candidate for the split.
|
||||||
if (bitsInCurrentMem == effectiveLibWidth) {
|
// Afterwards, figure out which one to use.
|
||||||
bitPairs += ((currentLSB, memBit - 1))
|
val bitPairCandidates = ArrayBuffer[(BigInt, BigInt)]()
|
||||||
currentLSB = memBit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((memPort, libPort) <- pairedPorts) {
|
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.
|
// Make sure we don't have a maskGran larger than the width of the memory.
|
||||||
assert (memPort.src.effectiveMaskGran <= memPort.src.width)
|
assert (memPort.src.effectiveMaskGran <= memPort.src.width)
|
||||||
assert (libPort.src.effectiveMaskGran <= libPort.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
|
// Add in the last chunk if there are any leftovers
|
||||||
bitPairs += ((currentLSB, mem.src.width.toInt - 1))
|
bitPairs += ((currentLSB, mem.src.width.toInt - 1))
|
||||||
|
// Check bit pairs
|
||||||
|
checkBitPairs(bitPairs)
|
||||||
|
|
||||||
// Depth mapping
|
// Depth mapping
|
||||||
val stmts = ArrayBuffer[Statement]()
|
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 ""
|
} 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
|
// Generate the header (contains the circuit statement and the target memory
|
||||||
// module.
|
// module.
|
||||||
def generateHeader(): String = {
|
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"""
|
s"""
|
||||||
circuit $mem_name :
|
circuit $mem_name :
|
||||||
module $mem_name :
|
module $mem_name :
|
||||||
input ${memPortPrefix}_clk : Clock
|
${generateHeaderPorts}
|
||||||
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}
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the target memory ports.
|
// Generate the target memory ports.
|
||||||
def generateFooterPorts(): String = {
|
def generateFooterPorts(): String = {
|
||||||
require (libSRAM.ports.size == 1, "Footer generator only supports single port lib")
|
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)
|
||||||
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}
|
|
||||||
"""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the footer (contains the target memory extmodule declaration by default).
|
// Generate the footer (contains the target memory extmodule declaration by default).
|
||||||
def generateFooter(): String = {
|
def generateFooter(): String = {
|
||||||
require (libSRAM.ports.size == 1, "Footer generator only supports single port lib")
|
|
||||||
|
|
||||||
s"""
|
s"""
|
||||||
extmodule $lib_name :
|
extmodule $lib_name :
|
||||||
${generateFooterPorts}
|
${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