// SPDX-License-Identifier: Apache-2.0 package firrtlTests import firrtl._ import firrtl.ir._ import firrtl.passes._ import firrtl.transforms._ import firrtl.Mappers._ import firrtl.PrimOps.AsClock import firrtl.testutils._ import firrtl.testutils.FirrtlCheckers._ class ChirrtlMemSpec extends LowFirrtlTransformSpec { object MemEnableCheckPass extends Pass { type Netlist = collection.mutable.HashMap[String, Expression] def buildNetlist(netlist: Netlist)(s: Statement): Statement = { s match { case s: Connect => Utils.kind(s.loc) match { case MemKind => netlist(s.loc.serialize) = s.expr case _ => } case _ => } s.map(buildNetlist(netlist)) } // walks on memories and checks whether or not read enables are high def checkStmt(netlist: Netlist)(s: Statement): Boolean = s match { case s: DefMemory if s.name == "mem" && s.readers.size == 1 => val en = MemPortUtils.memPortField(s, s.readers.head, "en") // memory read enable ?= 1 WrappedExpression.weq(netlist(en.serialize), Utils.one) case s: Block => s.stmts.exists(checkStmt(netlist)) case _ => false } def run(c: Circuit) = { val errors = new Errors val check = c.modules.exists { case m: Module => val netlist = new Netlist checkStmt(netlist)(buildNetlist(netlist)(m.body)) case m: ExtModule => false } if (!check) { errors.append(new PassException("Enable signal for the read port is incorrect!")) errors.trigger } c } } "Sequential Memory" should "have correct enable signals" in { val input = """ circuit foo : module foo : input clock : Clock input reset : UInt<1> output io : {flip wen : UInt<1>, flip in : UInt<1>, flip counter : UInt<2>, out : UInt<1>} io is invalid smem mem : UInt<1>[4] node T_0 = add(io.counter, UInt<1>("h01")) node temp = tail(T_0, 1) read mport bar = mem[temp], clock when io.wen : write mport T_1 = mem[io.counter], clock T_1 <= io.in io.out <= bar """.stripMargin val res = compile(parse(input)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } "Combinational Memory" should "have correct enable signals" in { val input = """ circuit foo : module foo : input clock : Clock input reset : UInt<1> output io : {flip ren: UInt<1>, flip wen : UInt<1>, flip in : UInt<1>, flip counter : UInt<2>, out : UInt<1>} io is invalid cmem mem : UInt<1>[4] reg counter : UInt<1>, clock with : (reset => (reset, UInt<1>("h0"))) read mport bar = mem[counter], clock when io.ren: counter <= add(counter, UInt<1>("h1")) when io.wen : write mport T_1 = mem[io.counter], clock T_1 <= io.in io.out <= bar """.stripMargin val res = compile(parse(input)) // Check correctness of firrtl parse(res.getEmittedCircuit.value) } "An mport that refers to an undefined memory" should "have a helpful error message" in { val input = """circuit testTestModule : | module testTestModule : | input clock : Clock | input reset : UInt<1> | output io : {flip in : UInt<10>, out : UInt<10>} | | node _T_10 = bits(io.in, 1, 0) | read mport _T_11 = m[_T_10], clock | io.out <= _T_11""".stripMargin intercept[PassException] { compile(parse(input)) }.getMessage should startWith("Undefined memory m referenced by mport _T_11") } ignore should "Memories should not have validif on port clocks when declared in a when" in { val input = """;buildInfoPackage: chisel3, version: 3.0-SNAPSHOT, scalaVersion: 2.11.11, sbtVersion: 0.13.16, builtAtString: 2017-10-06 20:55:20.367, builtAtMillis: 1507323320367 |circuit Stack : | module Stack : | input clock : Clock | input reset : UInt<1> | output io : {flip push : UInt<1>, flip pop : UInt<1>, flip en : UInt<1>, flip dataIn : UInt<32>, dataOut : UInt<32>} | | clock is invalid | reset is invalid | io is invalid | cmem stack_mem : UInt<32>[4] @[Stack.scala 15:22] | reg sp : UInt<3>, clock with : (reset => (reset, UInt<3>("h00"))) @[Stack.scala 16:26] | reg out : UInt<32>, clock with : (reset => (reset, UInt<32>("h00"))) @[Stack.scala 17:26] | when io.en : @[Stack.scala 19:16] | node _T_14 = lt(sp, UInt<3>("h04")) @[Stack.scala 20:25] | node _T_15 = and(io.push, _T_14) @[Stack.scala 20:18] | when _T_15 : @[Stack.scala 20:42] | node _T_16 = bits(sp, 1, 0) | infer mport _T_17 = stack_mem[_T_16], clock | _T_17 <= io.dataIn @[Stack.scala 21:21] | node _T_19 = add(sp, UInt<1>("h01")) @[Stack.scala 22:16] | node _T_20 = tail(_T_19, 1) @[Stack.scala 22:16] | sp <= _T_20 @[Stack.scala 22:10] | skip @[Stack.scala 20:42] | else : @[Stack.scala 23:39] | node _T_22 = gt(sp, UInt<1>("h00")) @[Stack.scala 23:31] | node _T_23 = and(io.pop, _T_22) @[Stack.scala 23:24] | when _T_23 : @[Stack.scala 23:39] | node _T_25 = sub(sp, UInt<1>("h01")) @[Stack.scala 24:16] | node _T_26 = asUInt(_T_25) @[Stack.scala 24:16] | node _T_27 = tail(_T_26, 1) @[Stack.scala 24:16] | sp <= _T_27 @[Stack.scala 24:10] | skip @[Stack.scala 23:39] | node _T_29 = gt(sp, UInt<1>("h00")) @[Stack.scala 26:14] | when _T_29 : @[Stack.scala 26:21] | node _T_31 = sub(sp, UInt<1>("h01")) @[Stack.scala 27:27] | node _T_32 = asUInt(_T_31) @[Stack.scala 27:27] | node _T_33 = tail(_T_32, 1) @[Stack.scala 27:27] | node _T_34 = bits(_T_33, 1, 0) | infer mport _T_35 = stack_mem[_T_34], clock | out <= _T_35 @[Stack.scala 27:11] | skip @[Stack.scala 26:21] | skip @[Stack.scala 19:16] | io.dataOut <= out @[Stack.scala 31:14] """.stripMargin val res = compile(parse(input)) assert(res.search { case Connect( _, WSubField(WSubField(WRef("stack_mem", _, _, _), "_T_35", _, _), "clk", _, _), WRef("clock", _, _, _) ) => true case Connect( _, WSubField(WSubField(WRef("stack_mem", _, _, _), "_T_17", _, _), "clk", _, _), WRef("clock", _, _, _) ) => true }) } ignore should "Mem non-local clock port assignment should be ok assign in only one side of when" in { val input = """circuit foo : | module foo : | input clock : Clock | input en : UInt<1> | input addr: UInt<2> | output out: UInt<32> | out is invalid | cmem mem : UInt<32>[4] | when en: | read mport bar = mem[addr], clock | out <= bar |""".stripMargin val res = compile(parse(input)) assert(res.search { case Connect(_, WSubField(WSubField(WRef("mem", _, _, _), "bar", _, _), "clk", _, _), WRef("clock", _, _, _)) => true }) } ignore should "Mem local clock port assignment should be ok" in { val input = """circuit foo : | module foo : | input clock : Clock | input en : UInt<1> | input addr: UInt<2> | output out: UInt<32> | out is invalid | cmem mem : UInt<32>[4] | when en: | node local = clock | read mport bar = mem[addr], local | out <= bar |""".stripMargin val res = compile(parse(input)) assert(res.search { case Connect(_, WSubField(WSubField(WRef("mem", _, _, _), "bar", _, _), "clk", _, _), WRef("clock", _, _, _)) => true }) } ignore should "Mem local nested clock port assignment should be ok" in { val input = """circuit foo : | module foo : | input clock : Clock | input en : UInt<1> | input addr: UInt<2> | output out: UInt<32> | out is invalid | cmem mem : UInt<32>[4] | when en: | node local = clock | read mport bar = mem[addr], asClock(local) | out <= bar |""".stripMargin val res = new LowFirrtlCompiler().compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res.search { case Connect( _, WSubField(WSubField(WRef("mem", _, _, _), "bar", _, _), "clk", _, _), DoPrim(AsClock, Seq(WRef("clock", _, _, _)), Nil, _) ) => true }) } ignore should "Mem non-local nested clock port assignment should be ok" in { val input = """circuit foo : | module foo : | input clock : Clock | input en : UInt<1> | input addr: UInt<2> | output out: UInt<32> | out is invalid | cmem mem : UInt<32>[4] | when en: | read mport bar = mem[addr], asClock(clock) | out <= bar |""".stripMargin val res = (new HighFirrtlCompiler).compile(CircuitState(parse(input), ChirrtlForm), Seq()).circuit assert(res.search { case Connect( _, SubField(SubField(Reference("mem", _, _, _), "bar", _, _), "clk", _, _), DoPrim(AsClock, Seq(Reference("clock", _, _, _)), _, _) ) => true }) } }