Re-implement parallel mapping

- Support byte-masked SRAM, yay
- Also nuke a bunch of bugs
This commit is contained in:
Edward Wang
2017-08-09 00:57:38 -07:00
committed by edwardcwang
parent 676b8e72ba
commit 5d3bebd2b9
6 changed files with 291 additions and 148 deletions

View File

@@ -201,6 +201,9 @@ trait HasSimpleTestGenerator {
def extraPorts: Seq[mdf.macrolib.MacroExtraPort] = List()
def extraTag: String = ""
// "Effective" libMaskGran by considering write_enable.
val effectiveLibMaskGran = libMaskGran.getOrElse(libWidth)
// Override this in the sub-generator if you need a more specific name.
// Defaults to using reflection to pull the name of the test using this
// generator.
@@ -242,16 +245,62 @@ trait HasSimpleTestGenerator {
writeToLib(lib, Seq(libSRAM))
writeToMem(mem, Seq(memSRAM))
// For masks, width it's a bit tricky since we have to consider cases like
// memMaskGran = 4 and libMaskGran = 8.
// Consider the actually usable libWidth in cases like the above.
val usableLibWidth = if (memMaskGran.getOrElse(Int.MaxValue) < effectiveLibMaskGran) memMaskGran.get else libWidth
// Number of lib instances needed to hold the mem, in both directions.
// Round up (e.g. 1.5 instances = effectively 2 instances)
val depthInstances = math.ceil(memDepth.toFloat / libDepth).toInt
val widthInstances = math.ceil(memWidth.toFloat / libWidth).toInt
val widthInstances = math.ceil(memWidth.toFloat / usableLibWidth).toInt
// Number of width bits in the last width-direction memory.
// e.g. if memWidth = 16 and libWidth = 8, this would be 8 since the last memory 0_1 has 8 bits of input width.
// e.g. if memWidth = 9 and libWidth = 8, this would be 1 since the last memory 0_1 has 1 bit of input width.
val lastWidthBits = if (memWidth % libWidth == 0) libWidth else (memWidth % libWidth)
val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth)
val selectBits = mem_addr_width - lib_addr_width
/**
* Convenience function to generate a mask statement.
* @param widthInst Width instance (mem_0_x)
* @param depthInst Depth instance (mem_x_0)
*/
def generateMaskStatement(widthInst: Int, depthInst: Int): String = {
// Width of this submemory.
val myMemWidth = if (widthInst == widthInstances - 1) lastWidthBits else usableLibWidth
// Base bit of this submemory.
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
// would be 16.
val myBaseBit = usableLibWidth*widthInst
if (libMaskGran.isDefined) {
if (memMaskGran.isEmpty) {
// If there is no memory mask, we should just turn all the lib mask
// bits high.
s"""mem_${depthInst}_${widthInst}.lib_mask <= UInt<${libMaskBits}>("h${((1 << libMaskBits) - 1).toHexString}")"""
} else {
// Calculate which bit of outer_mask contains the given bit.
// e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then
// calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1
def calculateMaskBit(bit:Int): Int = bit / memMaskGran.getOrElse(memWidth)
val bitsArr = ((libMaskBits - 1 to 0 by -1) map (x => {
if (x*libMaskGran.get > myMemWidth) {
// If we have extra mask bits leftover after the effective width,
// disable those bits.
"""UInt<1>("h0")"""
} else {
val outerMaskBit = calculateMaskBit(x*libMaskGran.get + myBaseBit)
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
}
}))
val maskVal = bitsArr.reduceRight((bit, rest) => s"cat($bit, $rest)")
s"mem_${depthInst}_${widthInst}.lib_mask <= ${maskVal}"
}
} else ""
}
// Generate the header (contains the circuit statement and the target memory
// module.
def generateHeader(): String = {
@@ -293,8 +342,6 @@ circuit $mem_name :
def generateFooter(): 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"""
extmodule $lib_name :
${generateFooterPorts}

View File

@@ -4,14 +4,105 @@ import mdf.macrolib._
// Test the ability of the compiler to deal with various mask combinations.
// Simple powers of two with bit-masked lib.
trait MasksTestSettings {
this: MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator =>
override lazy val memDepth = 2048
override lazy val libDepth = 1024
}
// Try all four different kinds of mask config:
/**
*
* Non-masked mem Masked mem
* ---------------------------------
* Non-masked lib | | |
* ---------------------------------
* Masked lib | | |
* ---------------------------------
*/
class Masks_FourTypes_NonMaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 32
override lazy val memMaskGran = None
override lazy val libWidth = 8
override lazy val libMaskGran = None
compileExecuteAndTest(mem, lib, v, output)
}
class Masks_FourTypes_NonMaskedMem_MaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 32
override lazy val memMaskGran = None
override lazy val libWidth = 8
override lazy val libMaskGran = Some(2)
compileExecuteAndTest(mem, lib, v, output)
}
class Masks_FourTypes_MaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 32
override lazy val memMaskGran = Some(8)
override lazy val libWidth = 8
override lazy val libMaskGran = None
compileExecuteAndTest(mem, lib, v, output)
}
class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 32
override lazy val memMaskGran = Some(4)
override lazy val libWidth = 8
override lazy val libMaskGran = None
compileExecuteAndTest(mem, lib, v, output)
}
class Masks_FourTypes_MaskedMem_MaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 32
override lazy val memMaskGran = Some(8)
override lazy val libWidth = 16
override lazy val libMaskGran = Some(4)
compileExecuteAndTest(mem, lib, v, output)
}
class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 32
override lazy val memMaskGran = Some(8)
override lazy val libWidth = 16
override lazy val libMaskGran = Some(8)
compileExecuteAndTest(mem, lib, v, output)
}
class Masks_FourTypes_MaskedMem_MaskedLib_SmallerMaskGran extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
override lazy val depth = 1024
override lazy val memWidth = 64
override lazy val memMaskGran = Some(4)
override lazy val libWidth = 32
override lazy val libMaskGran = Some(8)
compileExecuteAndTest(mem, lib, v, output)
}
// FPGA-style byte-masked memories.
class Masks_FPGAStyle_32_8 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
override lazy val width = 32
override lazy val memMaskGran = Some(32)
override lazy val libMaskGran = Some(8)
compileExecuteAndTest(mem, lib, v, output)
}
// Simple powers of two with bit-masked lib.
class Masks_PowersOfTwo_8_1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
override lazy val width = 64
override lazy val memMaskGran = Some(8)

View File

@@ -29,26 +29,7 @@ s"""
}
for (i <- 0 to depthInstances - 1) {
// We only support simple masks for now (either libMask == memMask or libMask == 1)
val maskStatement = if (libHasMask) {
if (libMaskGran.get == memMaskGran.get) {
s"""mem_${i}_0.lib_mask <= bits(outer_mask, 0, 0)"""
} else if (libMaskGran.get == 1) {
// Construct a mask string.
// Each bit gets the # of bits specified in maskGran.
// Specify in descending order (MSB first)
// This builds an array like m[1], m[1], m[0], m[0]
val maskBitsArr: Seq[String] = ((memMaskBits - 1 to 0 by -1) flatMap (maskBit => {
((0 to memMaskGran.get - 1) map (_ => s"bits(outer_mask, ${maskBit}, ${maskBit})"))
}))
// Now build it into a recursive string like
// cat(m[1], cat(m[1], cat(m[0], m[0])))
val maskBitsStr: String = maskBitsArr.reverse.tail.foldLeft(maskBitsArr.reverse.head)((prev: String, next: String) => s"cat(${next}, ${prev})")
s"""mem_${i}_0.lib_mask <= ${maskBitsStr}"""
} else "" // TODO: implement when non-bitmasked memories are supported
} else "" // No mask
val maskStatement = generateMaskStatement(0, i)
val enableIdentifier = if (selectBits > 0) s"""eq(outer_addr_sel, UInt<${selectBits}>("h${i.toHexString}"))""" else "UInt<1>(\"h1\")"
output.append(
s"""

View File

@@ -24,40 +24,28 @@ trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator {
// Generate submemory connection blocks.
output append (for (i <- 0 to widthInstances - 1) yield {
// Width of this submemory.
val myMemWidth = if (i == widthInstances - 1) lastWidthBits else libWidth
val myMemWidth = if (i == widthInstances - 1) lastWidthBits else usableLibWidth
// Base bit of this submemory.
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
// would be 16.
val myBaseBit = libWidth*i
val myBaseBit = usableLibWidth*i
val maskStatement = if (libMaskGran.isDefined) {
if (memMaskGran.isEmpty) {
// If there is no memory mask, we should just turn all the lib mask
// bits high.
s"""mem_0_${i}.lib_mask <= UInt<${libMaskBits}>("h${((1 << libMaskBits) - 1).toHexString}")"""
} else if (libMaskGran.get == memMaskGran.get) {
s"mem_0_${i}.lib_mask <= bits(outer_mask, ${i}, ${i})"
} else if (libMaskGran.get == 1) {
// Calculate which bit of outer_mask contains the given bit.
// e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then
// calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1
def calculateMaskBit(bit:Int): Int = (bit / libMaskGran.get) / memMaskGran.getOrElse(memWidth)
val maskStatement = generateMaskStatement(i, 0)
// We need to use writeEnable as a crude "mask" if mem has a mask but
// lib does not.
val writeEnableBit = if (libMaskGran.isEmpty && memMaskGran.isDefined) {
val outerMaskBit = myBaseBit / memMaskGran.get
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
} else """UInt<1>("h1")"""
val bitsArr = ((libMaskBits - 1 to 0 by -1) map (x => {
val outerMaskBit = calculateMaskBit(x*libMaskGran.get + myBaseBit)
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
}))
val maskVal = bitsArr.init.foldRight(bitsArr.last)((bit, rest) => s"cat($bit, $rest)")
s"mem_0_${i}.lib_mask <= ${maskVal}"
} else "" // We support only bit-level masks for now.
} else ""
s"""
mem_0_${i}.lib_clk <= outer_clk
mem_0_${i}.lib_addr <= outer_addr
node outer_dout_0_${i} = bits(mem_0_${i}.lib_dout, ${myMemWidth - 1}, 0)
mem_0_${i}.lib_din <= bits(outer_din, ${myBaseBit + myMemWidth - 1}, ${myBaseBit})
${maskStatement}
mem_0_${i}.lib_write_en <= and(and(outer_write_en, UInt<1>("h1")), UInt<1>("h1"))
mem_0_${i}.lib_write_en <= and(and(outer_write_en, ${writeEnableBit}), UInt<1>("h1"))
"""
}).reduceLeft(_ + _)

View File

@@ -188,90 +188,41 @@ circuit T_2172_ext :
input RW0_wmode : UInt<1>
input RW0_wmask : UInt<4>
inst mem_0_0 of SRAM1RW64x8
inst mem_0_1 of SRAM1RW64x8
inst mem_0_2 of SRAM1RW64x8
inst mem_0_3 of SRAM1RW64x8
inst mem_0_4 of SRAM1RW64x8
inst mem_0_5 of SRAM1RW64x8
inst mem_0_6 of SRAM1RW64x8
inst mem_0_7 of SRAM1RW64x8
inst mem_0_8 of SRAM1RW64x8
inst mem_0_9 of SRAM1RW64x8
inst mem_0_10 of SRAM1RW64x8
inst mem_0_11 of SRAM1RW64x8
inst mem_0_0 of SRAM1RW64x32
inst mem_0_1 of SRAM1RW64x32
inst mem_0_2 of SRAM1RW64x32
inst mem_0_3 of SRAM1RW64x32
mem_0_0.clk <= RW0_clk
mem_0_0.addr <= RW0_addr
node RW0_rdata_0_0 = bits(mem_0_0.dout, 7, 0)
mem_0_0.din <= bits(RW0_wdata, 7, 0)
node RW0_rdata_0_0 = bits(mem_0_0.dout, 19, 0)
mem_0_0.din <= bits(RW0_wdata, 19, 0)
mem_0_0.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1"))
mem_0_1.clk <= RW0_clk
mem_0_1.addr <= RW0_addr
node RW0_rdata_0_1 = bits(mem_0_1.dout, 7, 0)
mem_0_1.din <= bits(RW0_wdata, 15, 8)
mem_0_1.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1"))
node RW0_rdata_0_1 = bits(mem_0_1.dout, 19, 0)
mem_0_1.din <= bits(RW0_wdata, 39, 20)
mem_0_1.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))
mem_0_2.clk <= RW0_clk
mem_0_2.addr <= RW0_addr
node RW0_rdata_0_2 = bits(mem_0_2.dout, 3, 0)
mem_0_2.din <= bits(RW0_wdata, 19, 16)
mem_0_2.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1"))
node RW0_rdata_0_2 = bits(mem_0_2.dout, 19, 0)
mem_0_2.din <= bits(RW0_wdata, 59, 40)
mem_0_2.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))
mem_0_3.clk <= RW0_clk
mem_0_3.addr <= RW0_addr
node RW0_rdata_0_3 = bits(mem_0_3.dout, 7, 0)
mem_0_3.din <= bits(RW0_wdata, 27, 20)
mem_0_3.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))
mem_0_4.clk <= RW0_clk
mem_0_4.addr <= RW0_addr
node RW0_rdata_0_4 = bits(mem_0_4.dout, 7, 0)
mem_0_4.din <= bits(RW0_wdata, 35, 28)
mem_0_4.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))
mem_0_5.clk <= RW0_clk
mem_0_5.addr <= RW0_addr
node RW0_rdata_0_5 = bits(mem_0_5.dout, 3, 0)
mem_0_5.din <= bits(RW0_wdata, 39, 36)
mem_0_5.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1"))
mem_0_6.clk <= RW0_clk
mem_0_6.addr <= RW0_addr
node RW0_rdata_0_6 = bits(mem_0_6.dout, 7, 0)
mem_0_6.din <= bits(RW0_wdata, 47, 40)
mem_0_6.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))
mem_0_7.clk <= RW0_clk
mem_0_7.addr <= RW0_addr
node RW0_rdata_0_7 = bits(mem_0_7.dout, 7, 0)
mem_0_7.din <= bits(RW0_wdata, 55, 48)
mem_0_7.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))
mem_0_8.clk <= RW0_clk
mem_0_8.addr <= RW0_addr
node RW0_rdata_0_8 = bits(mem_0_8.dout, 3, 0)
mem_0_8.din <= bits(RW0_wdata, 59, 56)
mem_0_8.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1"))
mem_0_9.clk <= RW0_clk
mem_0_9.addr <= RW0_addr
node RW0_rdata_0_9 = bits(mem_0_9.dout, 7, 0)
mem_0_9.din <= bits(RW0_wdata, 67, 60)
mem_0_9.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))
mem_0_10.clk <= RW0_clk
mem_0_10.addr <= RW0_addr
node RW0_rdata_0_10 = bits(mem_0_10.dout, 7, 0)
mem_0_10.din <= bits(RW0_wdata, 75, 68)
mem_0_10.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))
mem_0_11.clk <= RW0_clk
mem_0_11.addr <= RW0_addr
node RW0_rdata_0_11 = bits(mem_0_11.dout, 3, 0)
mem_0_11.din <= bits(RW0_wdata, 79, 76)
mem_0_11.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))
node RW0_rdata_0 = cat(RW0_rdata_0_11, cat(RW0_rdata_0_10, cat(RW0_rdata_0_9, cat(RW0_rdata_0_8, cat(RW0_rdata_0_7, cat(RW0_rdata_0_6, cat(RW0_rdata_0_5, cat(RW0_rdata_0_4, cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0)))))))))))
node RW0_rdata_0_3 = bits(mem_0_3.dout, 19, 0)
mem_0_3.din <= bits(RW0_wdata, 79, 60)
mem_0_3.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1"))
node RW0_rdata_0 = cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0)))
RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<1>("h0"))
extmodule SRAM1RW64x8 :
extmodule SRAM1RW64x32 :
input clk : Clock
input addr : UInt<6>
input din : UInt<8>
output dout : UInt<8>
input din : UInt<32>
output dout : UInt<32>
input write_en : UInt<1>
defname = SRAM1RW64x8
defname = SRAM1RW64x32
module T_1090_ext :
input RW0_clk : Clock