diff --git a/tapeout/src/main/scala/transforms/macros/MacroCompiler.scala b/tapeout/src/main/scala/transforms/macros/MacroCompiler.scala index 0330ab13..87386c12 100644 --- a/tapeout/src/main/scala/transforms/macros/MacroCompiler.scala +++ b/tapeout/src/main/scala/transforms/macros/MacroCompiler.scala @@ -25,22 +25,10 @@ object MacroCompilerAnnotation { } } -class MacroCompilerPass(memFile: Option[File], - libFile: Option[File]) extends firrtl.passes.Pass { - require(memFile.isDefined) - private val mems: Option[Seq[Macro]] = readJSON(memFile) map (_ map (x => new Macro(x))) - private val libs: Option[Seq[Macro]] = readJSON(libFile) map (_ map (x => new Macro(x))) - +class MacroCompilerPass(mems: Option[Seq[Macro]], + libs: Option[Seq[Macro]]) extends firrtl.passes.Pass { def compile(mem: Macro, lib: Macro): Option[(Module, ExtModule)] = { - val pairedPorts = ( - (mem.ports filter (p => p.inputName.isDefined && !p.outputName.isDefined)) ++ // write - (mem.ports filter (p => !p.inputName.isDefined && p.outputName.isDefined)) ++ // read - (mem.ports filter (p => p.inputName.isDefined && p.outputName.isDefined)) // read writers - ) zip ( - (lib.ports filter (p => p.inputName.isDefined && !p.outputName.isDefined)) ++ // write - (lib.ports filter (p => !p.inputName.isDefined && p.outputName.isDefined)) ++ // read - (lib.ports filter (p => p.inputName.isDefined && p.outputName.isDefined)) // read writers - ) + val pairedPorts = mem.sortedPorts zip lib.sortedPorts // Parallel mapping val pairs = ArrayBuffer[(BigInt, BigInt)]() @@ -74,7 +62,6 @@ class MacroCompilerPass(memFile: Option[File], pairs += ((last, mem.width.toInt - 1)) // Serial mapping - val instType = BundleType(lib.ports flatMap (_.tpe.fields)) val stmts = ArrayBuffer[Statement]() val selects = HashMap[String, Expression]() val outputs = HashMap[String, ArrayBuffer[(Expression, Expression)]]() @@ -93,7 +80,7 @@ class MacroCompilerPass(memFile: Option[File], for ((off, i) <- (0 until mem.depth.toInt by lib.depth.toInt).zipWithIndex) { for (j <- pairs.indices) { val name = s"mem_${i}_${j}" - stmts += WDefInstance(NoInfo, name, lib.name, instType) + stmts += WDefInstance(NoInfo, name, lib.name, lib.tpe) // connect extra ports stmts ++= lib.extraPorts map { case (portName, portValue) => Connect(NoInfo, WSubField(WRef(name), portName), portValue) @@ -109,12 +96,7 @@ class MacroCompilerPass(memFile: Option[File], def andAddrMatch(e: Expression) = and(e, addrMatch) val cats = ArrayBuffer[Expression]() for (((low, high), j) <- pairs.zipWithIndex) { - val inst = WRef(s"mem_${i}_${j}", instType) - def invert(exp: Expression, polarity: Option[PortPolarity]) = - polarity match { - case Some(ActiveLow) | Some(NegativeEdge) => not(exp) - case _ => exp - } + val inst = WRef(s"mem_${i}_${j}", lib.tpe) def connectPorts(mem: Expression, lib: String, @@ -344,10 +326,13 @@ class MacroCompilerTransform extends Transform { def inputForm = HighForm def outputForm = HighForm def execute(state: CircuitState) = getMyAnnotations(state) match { - case Seq(MacroCompilerAnnotation(state.circuit.main, mem, lib, synflops)) => + case Seq(MacroCompilerAnnotation(state.circuit.main, memFile, libFile, synflops)) => + require(memFile.isDefined) + val mems: Option[Seq[Macro]] = readJSON(memFile) map (_ map (x => new Macro(x))) + val libs: Option[Seq[Macro]] = readJSON(libFile) map (_ map (x => new Macro(x))) val transforms = Seq( - new MacroCompilerPass(mem, lib), - // TODO: Syn flops + new MacroCompilerPass(mems, libs), + new SynFlopsPass(synflops, libs getOrElse mems.get), firrtl.passes.SplitExpressions ) ((transforms foldLeft state)((s, xform) => xform runTransform s)) @@ -359,7 +344,7 @@ class MacroCompiler extends Compiler { def transforms = Seq(new MacroCompilerTransform) ++ getLoweringTransforms(firrtl.HighForm, firrtl.LowForm) // ++ - // Seq(new LowFirrtlOptimization) // Todo: This is dangerous... + // Seq(new LowFirrtlOptimization) // Todo: This is dangerous } object MacroCompiler extends App { diff --git a/tapeout/src/main/scala/transforms/macros/SynFlops.scala b/tapeout/src/main/scala/transforms/macros/SynFlops.scala new file mode 100644 index 00000000..499258c8 --- /dev/null +++ b/tapeout/src/main/scala/transforms/macros/SynFlops.scala @@ -0,0 +1,110 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms.macros + +import firrtl._ +import firrtl.ir._ +import firrtl.Utils._ +import firrtl.passes.MemPortUtils.{memPortField, memType} +import Utils._ + +class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pass { + lazy val libMods = (libs map { lib => lib.name -> { + val dataType = (lib.ports foldLeft (None: Option[BigInt]))((res, port) => + (res, port.maskName) match { + case (_, None) => + res + case (None, Some(_)) => + Some(port.effectiveMaskGran) + case (Some(x), Some(_)) => + assert(x == port.effectiveMaskGran) + res + } + ) match { + case None => UIntType(IntWidth(lib.width)) + case Some(gran) => VectorType(UIntType(IntWidth(gran)), (lib.width / gran).toInt) + } + + val mem = DefMemory( + NoInfo, + "ram", + dataType, + lib.depth.toInt, + 1, // writeLatency + 0, // readLatency + (lib.readers ++ lib.readwriters).indices map (i => s"R_$i"), + (lib.writers ++ lib.readwriters).indices map (i => s"W_$i"), + Nil + ) + + val readConnects = (lib.readers ++ lib.readwriters).zipWithIndex flatMap { case (r, i) => + val clock = invert(WRef(r.clockName), r.clockPolarity) + val address = invert(WRef(r.addressName), r.addressPolarity) + val enable = (r.chipEnableName, r.readEnableName) match { + case (Some(en), Some(re)) => + and(invert(WRef(en), r.chipEnablePolarity), + invert(WRef(re), r.readEnablePolarity)) + case (Some(en), None) => invert(WRef(en), r.chipEnablePolarity) + case (None, Some(re)) => invert(WRef(re), r.readEnablePolarity) + case (None, None) => one + } + val data = memPortField(mem, s"R_$i", "data") + val read = (dataType: @unchecked) match { + case VectorType(tpe, size) => cat(((0 until size) map (k => + WSubIndex(data, k, tpe, UNKNOWNGENDER))).reverse) + case _: UIntType => data + } + val addrReg = WRef(s"R_${i}_addr_reg", r.AddrType, RegKind) + Seq( + DefRegister(NoInfo, addrReg.name, r.AddrType, clock, zero, addrReg), + Connect(NoInfo, memPortField(mem, s"R_$i", "clk"), clock), + Connect(NoInfo, memPortField(mem, s"R_$i", "addr"), addrReg), + Connect(NoInfo, memPortField(mem, s"R_$i", "en"), enable), + Connect(NoInfo, WRef(r.outputName.get), read), + Connect(NoInfo, addrReg, Mux(enable, address, addrReg, UnknownType)) + ) + } + + val writeConnects = (lib.writers ++ lib.readwriters).zipWithIndex flatMap { case (w, i) => + val clock = invert(WRef(w.clockName), w.clockPolarity) + val address = invert(WRef(w.addressName), w.addressPolarity) + val enable = (w.chipEnableName, w.writeEnableName) match { + case (Some(en), Some(we)) => + and(invert(WRef(en), w.chipEnablePolarity), + invert(WRef(we), w.writeEnablePolarity)) + case (Some(en), None) => invert(WRef(en), w.chipEnablePolarity) + case (None, Some(we)) => invert(WRef(we), w.writeEnablePolarity) + case (None, None) => zero // is it possible? + } + val mask = memPortField(mem, s"W_$i", "mask") + val data = memPortField(mem, s"W_$i", "data") + val write = invert(WRef(w.inputName.get), w.inputPolarity) + Seq( + Connect(NoInfo, memPortField(mem, s"W_$i", "clk"), clock), + Connect(NoInfo, memPortField(mem, s"W_$i", "addr"), address), + Connect(NoInfo, memPortField(mem, s"W_$i", "en"), enable) + ) ++ (dataType match { + case VectorType(tpe, size) => + val width = bitWidth(tpe).toInt + ((0 until size) map (k => + Connect(NoInfo, WSubIndex(data, k, tpe, UNKNOWNGENDER), + bits(write, (k + 1) * width - 1, k * width)))) ++ + ((0 until size) map (k => + Connect(NoInfo, WSubIndex(mask, k, BoolType, UNKNOWNGENDER), + bits(WRef(w.maskName.get), k)))) + case _: UIntType => + Seq(Connect(NoInfo, data, write), Connect(NoInfo, mask, one)) + }) + } + lib.module(Block(mem +: (readConnects ++ writeConnects))) + }}).toMap + + def run(c: Circuit): Circuit = { + if (!synflops) c + else { + val circuit = c.copy(modules = (c.modules map (m => libMods getOrElse (m.name, m)))) + // print(circuit.serialize) + circuit + } + } +} diff --git a/tapeout/src/main/scala/transforms/macros/Utils.scala b/tapeout/src/main/scala/transforms/macros/Utils.scala index 5a2e34e6..4520a114 100644 --- a/tapeout/src/main/scala/transforms/macros/Utils.scala +++ b/tapeout/src/main/scala/transforms/macros/Utils.scala @@ -48,9 +48,9 @@ case class MacroPort( width: BigInt, depth: BigInt) { val effectiveMaskGran = maskGran.getOrElse(width) - private val AddrType = UIntType(IntWidth(ceilLog2(depth) max 1)) - private val DataType = UIntType(IntWidth(width)) - private val MaskType = UIntType(IntWidth(width / effectiveMaskGran)) + val AddrType = UIntType(IntWidth(ceilLog2(depth) max 1)) + val DataType = UIntType(IntWidth(width)) + val MaskType = UIntType(IntWidth(width / effectiveMaskGran)) val tpe = BundleType(Seq( Field(clockName, Flip, ClockType), Field(addressName, Flip, AddrType)) ++ @@ -93,6 +93,10 @@ class Macro(lib: Map[String, Any]) { depth ) } + val writers = ports filter (p => p.inputName.isDefined && !p.outputName.isDefined) + val readers = ports filter (p => !p.inputName.isDefined && p.outputName.isDefined) + val readwriters = ports filter (p => p.inputName.isDefined && p.outputName.isDefined) + val sortedPorts = writers ++ readers ++ readwriters val extraPorts = lib get "extra ports" match { case None => Nil case Some(p) => p.asInstanceOf[List[_]] map { x => @@ -104,6 +108,7 @@ class Macro(lib: Map[String, Any]) { (name -> UIntLiteral(value, IntWidth(width))) } } + val tpe = BundleType(ports flatMap (_.tpe.fields)) private val modPorts = (ports flatMap (_.ports)) ++ (extraPorts map { case (name, value) => Port(NoInfo, name, Input, value.tpe) }) val blackbox = ExtModule(NoInfo, name, modPorts, name, Nil) @@ -140,4 +145,10 @@ object Utils { else DoPrim(PrimOps.Cat, Seq(es.head, cat(es.tail)), Nil, UnknownType) def not(e: Expression) = DoPrim(PrimOps.Not, Seq(e), Nil, e.tpe) + + def invert(exp: Expression, polarity: Option[PortPolarity]) = + polarity match { + case Some(ActiveLow) | Some(NegativeEdge) => not(exp) + case _ => exp + } } diff --git a/tapeout/src/test/scala/transforms/macros/MacroCompilerSpec.scala b/tapeout/src/test/scala/transforms/macros/MacroCompilerSpec.scala index 6b34204d..765490f8 100644 --- a/tapeout/src/test/scala/transforms/macros/MacroCompilerSpec.scala +++ b/tapeout/src/test/scala/transforms/macros/MacroCompilerSpec.scala @@ -5,6 +5,7 @@ import firrtl.ir.{Circuit, NoInfo} import firrtl.passes.RemoveEmpty import firrtl.Parser.parse import java.io.{File, StringWriter} +import Utils.readJSON abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalatest.Matchers { val macroDir = new File("tapeout/src/test/resources/macros") @@ -19,11 +20,16 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate MacroCompiler.run(args(mem, lib, v, synflops)) } - def execute(mem: Option[File], lib: Option[File], synflops: Boolean, output: String) { - require(mem.isDefined) - val macros = Utils.readJSON(mem).get map (x => (new Macro(x)).blackbox) + def execute(memFile: Option[File], libFile: Option[File], synflops: Boolean, output: String) { + require(memFile.isDefined) + val mems = readJSON(memFile) map (_ map (x => new Macro(x))) + val libs = readJSON(libFile) map (_ map (x => new Macro(x))) + val macros = mems.get map (_.blackbox) val circuit = Circuit(NoInfo, macros, macros.last.name) - val passes = Seq(new MacroCompilerPass(mem, lib), RemoveEmpty) + val passes = Seq( + new MacroCompilerPass(mems, libs), + new SynFlopsPass(synflops, libs getOrElse mems.get), + RemoveEmpty) val result = (passes foldLeft circuit)((c, pass) => pass run c) val gold = RemoveEmpty run parse(output) (result.serialize) should be (gold.serialize) diff --git a/tapeout/src/test/scala/transforms/macros/SynFlops.scala b/tapeout/src/test/scala/transforms/macros/SynFlops.scala new file mode 100644 index 00000000..981e5e8d --- /dev/null +++ b/tapeout/src/test/scala/transforms/macros/SynFlops.scala @@ -0,0 +1,333 @@ +package barstools.tapeout.transforms.macros + +import java.io.File + +class Synflops2048x16_mrw extends MacroCompilerSpec { + val mem = new File(macroDir, "mem-2048x16-mrw.json") + val v = new File(testDir, "syn_flops_2048x16_mrw.v") + val output = +""" +circuit name_of_sram_module : + module name_of_sram_module : + input clock : Clock + input RW0A : UInt<11> + input RW0I : UInt<16> + output RW0O : UInt<16> + input RW0E : UInt<1> + input RW0W : UInt<1> + input RW0M : UInt<2> + + mem ram : + data-type => UInt<8>[2] + depth => 2048 + read-latency => 0 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + reg R_0_addr_reg : UInt<11>, clock with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= clock + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= RW0E + RW0O <= cat(ram.R_0.data[1], ram.R_0.data[0]) + R_0_addr_reg <= mux(RW0E, RW0A, R_0_addr_reg) + ram.W_0.clk <= clock + ram.W_0.addr <= RW0A + ram.W_0.en <= and(RW0E, RW0W) + ram.W_0.data[0] <= bits(RW0I, 7, 0) + ram.W_0.data[1] <= bits(RW0I, 15, 8) + ram.W_0.mask[0] <= bits(RW0M, 0, 0) + ram.W_0.mask[1] <= bits(RW0M, 1, 1) +""" + compile(mem, None, v, true) + execute(Some(mem), None, true, output) +} + +class Synflops2048x8_r_mw extends MacroCompilerSpec { + val mem = new File(macroDir, "mem-2048x8-r-mw.json") + val v = new File(testDir, "syn_flops_2048x8_r_mw.v") + val output = +""" +circuit name_of_sram_module : + module name_of_sram_module : + input clock : Clock + input W0A : UInt<11> + input W0I : UInt<8> + input W0E : UInt<1> + input W0M : UInt<1> + input clock : Clock + input R0A : UInt<11> + output R0O : UInt<8> + + mem ram : + data-type => UInt<8>[1] + depth => 2048 + read-latency => 0 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + reg R_0_addr_reg : UInt<11>, clock with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= clock + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= UInt<1>("h1") + R0O <= ram.R_0.data[0] + R_0_addr_reg <= mux(UInt<1>("h1"), R0A, R_0_addr_reg) + ram.W_0.clk <= clock + ram.W_0.addr <= W0A + ram.W_0.en <= W0E + ram.W_0.data[0] <= bits(W0I, 7, 0) + ram.W_0.mask[0] <= bits(W0M, 0, 0) +""" + compile(mem, None, v, true) + execute(Some(mem), None, true, output) +} + +class Synflops2048x10_rw extends MacroCompilerSpec { + val mem = new File(macroDir, "lib-2048x10-rw.json") + val v = new File(testDir, "syn_flops_2048x10_rw.v") + val output = +""" +circuit vendor_sram : + module vendor_sram : + input clock : Clock + input RW0A : UInt<11> + input RW0I : UInt<10> + output RW0O : UInt<10> + input RW0E : UInt<1> + input RW0W : UInt<1> + + mem ram : + data-type => UInt<10> + depth => 2048 + read-latency => 0 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + reg R_0_addr_reg : UInt<11>, clock with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= clock + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= RW0E + RW0O <= ram.R_0.data + R_0_addr_reg <= mux(RW0E, RW0A, R_0_addr_reg) + ram.W_0.clk <= clock + ram.W_0.addr <= RW0A + ram.W_0.en <= and(RW0E, RW0W) + ram.W_0.data <= RW0I + ram.W_0.mask <= UInt<1>("h1") +""" + compile(mem, None, v, true) + execute(Some(mem), None, true, output) +} + +class Synflops2048x8_mrw_re extends MacroCompilerSpec { + val mem = new File(macroDir, "lib-2048x8-mrw-re.json") + val v = new File(testDir, "syn_flops_2048x8_mrw_re.v") + val output = +""" +circuit vendor_sram : + module vendor_sram : + input clock : Clock + input RW0A : UInt<11> + input RW0I : UInt<8> + output RW0O : UInt<8> + input RW0E : UInt<1> + input RW0R : UInt<1> + input RW0W : UInt<1> + input RW0M : UInt<1> + + mem ram : + data-type => UInt<8>[1] + depth => 2048 + read-latency => 0 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + reg R_0_addr_reg : UInt<11>, clock with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= clock + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= and(RW0E, not(RW0R)) + RW0O <= ram.R_0.data[0] + R_0_addr_reg <= mux(and(RW0E, not(RW0R)), RW0A, R_0_addr_reg) + ram.W_0.clk <= clock + ram.W_0.addr <= RW0A + ram.W_0.en <= and(RW0E, RW0W) + ram.W_0.data[0] <= bits(RW0I, 7, 0) + ram.W_0.mask[0] <= bits(RW0M, 0, 0) +""" + compile(mem, None, v, true) + execute(Some(mem), None, true, output) +} + +class Synflops2048x16_n28 extends MacroCompilerSpec { + val mem = new File(macroDir, "lib-2048x16-n28.json") + val v = new File(testDir, "syn_flops_2048x16_n28.v") + val output = +""" +circuit vendor_sram_4 : + module vendor_sram_16 : + input clock : Clock + input RW0A : UInt<11> + input RW0I : UInt<16> + output RW0O : UInt<16> + input RW0E : UInt<1> + input RW0W : UInt<1> + input RW0M : UInt<16> + + mem ram : + data-type => UInt<1>[16] + depth => 2048 + read-latency => 0 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + reg R_0_addr_reg : UInt<11>, clock with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= clock + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= RW0E + RW0O <= cat(ram.R_0.data[15], cat(ram.R_0.data[14], cat(ram.R_0.data[13], cat(ram.R_0.data[12], cat(ram.R_0.data[11], cat(ram.R_0.data[10], cat(ram.R_0.data[9], cat(ram.R_0.data[8], cat(ram.R_0.data[7], cat(ram.R_0.data[6], cat(ram.R_0.data[5], cat(ram.R_0.data[4], cat(ram.R_0.data[3], cat(ram.R_0.data[2], cat(ram.R_0.data[1], ram.R_0.data[0]))))))))))))))) + R_0_addr_reg <= mux(RW0E, RW0A, R_0_addr_reg) + ram.W_0.clk <= clock + ram.W_0.addr <= RW0A + ram.W_0.en <= and(RW0E, RW0W) + ram.W_0.data[0] <= bits(RW0I, 0, 0) + ram.W_0.data[1] <= bits(RW0I, 1, 1) + ram.W_0.data[2] <= bits(RW0I, 2, 2) + ram.W_0.data[3] <= bits(RW0I, 3, 3) + ram.W_0.data[4] <= bits(RW0I, 4, 4) + ram.W_0.data[5] <= bits(RW0I, 5, 5) + ram.W_0.data[6] <= bits(RW0I, 6, 6) + ram.W_0.data[7] <= bits(RW0I, 7, 7) + ram.W_0.data[8] <= bits(RW0I, 8, 8) + ram.W_0.data[9] <= bits(RW0I, 9, 9) + ram.W_0.data[10] <= bits(RW0I, 10, 10) + ram.W_0.data[11] <= bits(RW0I, 11, 11) + ram.W_0.data[12] <= bits(RW0I, 12, 12) + ram.W_0.data[13] <= bits(RW0I, 13, 13) + ram.W_0.data[14] <= bits(RW0I, 14, 14) + ram.W_0.data[15] <= bits(RW0I, 15, 15) + ram.W_0.mask[0] <= bits(RW0M, 0, 0) + ram.W_0.mask[1] <= bits(RW0M, 1, 1) + ram.W_0.mask[2] <= bits(RW0M, 2, 2) + ram.W_0.mask[3] <= bits(RW0M, 3, 3) + ram.W_0.mask[4] <= bits(RW0M, 4, 4) + ram.W_0.mask[5] <= bits(RW0M, 5, 5) + ram.W_0.mask[6] <= bits(RW0M, 6, 6) + ram.W_0.mask[7] <= bits(RW0M, 7, 7) + ram.W_0.mask[8] <= bits(RW0M, 8, 8) + ram.W_0.mask[9] <= bits(RW0M, 9, 9) + ram.W_0.mask[10] <= bits(RW0M, 10, 10) + ram.W_0.mask[11] <= bits(RW0M, 11, 11) + ram.W_0.mask[12] <= bits(RW0M, 12, 12) + ram.W_0.mask[13] <= bits(RW0M, 13, 13) + ram.W_0.mask[14] <= bits(RW0M, 14, 14) + ram.W_0.mask[15] <= bits(RW0M, 15, 15) + + module vendor_sram_4 : + input clock : Clock + input RW0A : UInt<11> + input RW0I : UInt<4> + output RW0O : UInt<4> + input RW0E : UInt<1> + input RW0W : UInt<1> + input RW0M : UInt<4> + + mem ram : + data-type => UInt<1>[4] + depth => 2048 + read-latency => 0 + write-latency => 1 + reader => R_0 + writer => W_0 + read-under-write => undefined + reg R_0_addr_reg : UInt<11>, clock with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= clock + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= RW0E + RW0O <= cat(ram.R_0.data[3], cat(ram.R_0.data[2], cat(ram.R_0.data[1], ram.R_0.data[0]))) + R_0_addr_reg <= mux(RW0E, RW0A, R_0_addr_reg) + ram.W_0.clk <= clock + ram.W_0.addr <= RW0A + ram.W_0.en <= and(RW0E, RW0W) + ram.W_0.data[0] <= bits(RW0I, 0, 0) + ram.W_0.data[1] <= bits(RW0I, 1, 1) + ram.W_0.data[2] <= bits(RW0I, 2, 2) + ram.W_0.data[3] <= bits(RW0I, 3, 3) + ram.W_0.mask[0] <= bits(RW0M, 0, 0) + ram.W_0.mask[1] <= bits(RW0M, 1, 1) + ram.W_0.mask[2] <= bits(RW0M, 2, 2) + ram.W_0.mask[3] <= bits(RW0M, 3, 3) +""" + compile(mem, None, v, true) + execute(Some(mem), None, true, output) +} + +class Synflops32x32_2rw extends MacroCompilerSpec { + val mem = new File(macroDir, "lib-32x32-2rw.json") + val v = new File(testDir, "syn_flops_32x32_2rw.v") + val output = +""" +circuit SRAM2RW32x32 : + module SRAM2RW32x32 : + input CE1 : Clock + input A1 : UInt<5> + input I1 : UInt<32> + output O1 : UInt<32> + input CSB1 : UInt<1> + input OEB1 : UInt<1> + input WEB1 : UInt<1> + input CE2 : Clock + input A2 : UInt<5> + input I2 : UInt<32> + output O2 : UInt<32> + input CSB2 : UInt<1> + input OEB2 : UInt<1> + input WEB2 : UInt<1> + + mem ram : + data-type => UInt<32> + depth => 32 + read-latency => 0 + write-latency => 1 + reader => R_0 + reader => R_1 + writer => W_0 + writer => W_1 + read-under-write => undefined + reg R_0_addr_reg : UInt<5>, CE1 with : + reset => (UInt<1>("h0"), R_0_addr_reg) + ram.R_0.clk <= CE1 + ram.R_0.addr <= R_0_addr_reg + ram.R_0.en <= and(not(CSB1), not(OEB1)) + O1 <= ram.R_0.data + R_0_addr_reg <= mux(and(not(CSB1), not(OEB1)), A1, R_0_addr_reg) + reg R_1_addr_reg : UInt<5>, CE2 with : + reset => (UInt<1>("h0"), R_1_addr_reg) + ram.R_1.clk <= CE2 + ram.R_1.addr <= R_1_addr_reg + ram.R_1.en <= and(not(CSB2), not(OEB2)) + O2 <= ram.R_1.data + R_1_addr_reg <= mux(and(not(CSB2), not(OEB2)), A2, R_1_addr_reg) + ram.W_0.clk <= CE1 + ram.W_0.addr <= A1 + ram.W_0.en <= and(not(CSB1), not(WEB1)) + ram.W_0.data <= I1 + ram.W_0.mask <= UInt<1>("h1") + ram.W_1.clk <= CE2 + ram.W_1.addr <= A2 + ram.W_1.en <= and(not(CSB2), not(WEB2)) + ram.W_1.data <= I2 + ram.W_1.mask <= UInt<1>("h1") +""" + compile(mem, None, v, true) + execute(Some(mem), None, true, output) +}