// SPDX-License-Identifier: Apache-2.0 package firrtlTests import firrtlTests.execution._ object MemLatencySpec { case class Write(addr: Int, data: Int, mask: Option[Boolean] = None) case class Read(addr: Int, expectedValue: Int) case class MemAccess(w: Option[Write], r: Option[Read]) def writeOnly(addr: Int, data: Int) = MemAccess(Some(Write(addr, data)), None) def readOnly(addr: Int, expectedValue: Int) = MemAccess(None, Some(Read(addr, expectedValue))) } abstract class MemLatencySpec(rLatency: Int, wLatency: Int, ruw: String) extends SimpleExecutionTest with VerilogExecution { import MemLatencySpec._ require(rLatency >= 0, s"Illegal read-latency ${rLatency} supplied to MemLatencySpec") require(wLatency > 0, s"Illegal write-latency ${wLatency} supplied to MemLatencySpec") val body = s"""mem m : | data-type => UInt<32> | depth => 256 | reader => r | writer => w | read-latency => ${rLatency} | write-latency => ${wLatency} | read-under-write => ${ruw} |m.r.clk <= clock |m.w.clk <= clock |""".stripMargin val memAccesses: Seq[MemAccess] def mask2Poke(m: Option[Boolean]) = m match { case Some(false) => Poke("m.w.mask", 0) case _ => Poke("m.w.mask", 1) } def wPokes = memAccesses.map { case MemAccess(Some(Write(a, d, m)), _) => Seq(Poke("m.w.en", 1), Poke("m.w.addr", a), Poke("m.w.data", d), mask2Poke(m)) case _ => Seq(Poke("m.w.en", 0), Invalidate("m.w.addr"), Invalidate("m.w.data")) } def rPokes = memAccesses.map { case MemAccess(_, Some(Read(a, _))) => Seq(Poke("m.r.en", 1), Poke("m.r.addr", a)) case _ => Seq(Poke("m.r.en", 0), Invalidate("m.r.addr")) } // Need to idle for cycles at the end val idle = Seq(Poke("m.w.en", 0), Poke("m.r.en", 0)) def pokes = (wPokes.zip(rPokes)).map { case (wp, rp) => wp ++ rp } ++ Seq.fill(rLatency)(idle) // Need to delay read value expects by def expects = Seq.fill(rLatency)(Seq(Step(1))) ++ memAccesses.map { case MemAccess(_, Some(Read(_, expected))) => Seq(Expect("m.r.data", expected), Step(1)) case _ => Seq(Step(1)) } def commands: Seq[SimpleTestCommand] = (pokes.zip(expects)).flatMap { case (p, e) => p ++ e } } trait ToggleMaskAndEnable { import MemLatencySpec._ /** * A canonical sequence of memory accesses for sanity checking memories of different latencies. * The shortest true "RAW" hazard is reading address 14 two accesses after writing it. Since this * access assumed the new value of 87, this means that the access pattern is only valid for * certain combinations of read- and write-latencies that vary between read- and write-first * memories. * * @note Read-first mems should return expected values for (write-latency <= 2) * @note Write-first mems should return expected values for (write-latency <= read-latency + 2) */ val memAccesses: Seq[MemAccess] = Seq( MemAccess(Some(Write(6, 32)), None), MemAccess(Some(Write(14, 87)), None), MemAccess(None, None), MemAccess(Some(Write(19, 63)), Some(Read(14, 87))), MemAccess(Some(Write(22, 49)), None), MemAccess(Some(Write(11, 99)), Some(Read(6, 32))), MemAccess(Some(Write(42, 42)), None), MemAccess(Some(Write(77, 81)), None), MemAccess(Some(Write(6, 7)), Some(Read(19, 63))), MemAccess(Some(Write(39, 5)), Some(Read(42, 42))), MemAccess(Some(Write(39, 6, Some(false))), Some(Read(77, 81))), // set mask to zero, should not write MemAccess(None, Some(Read(6, 7))), // also read a twice-written address MemAccess(None, Some(Read(39, 5))) // ensure masked writes didn't happen ) } /* * This framework is for execution tests, so these tests all focus on * *legal* configurations. Illegal memory parameters that should * result in errors should be tested in MemSpec. */ // These two are the same in practice, but the two tests could help expose bugs in VerilogMemDelays class CombMemSpecNewRUW extends MemLatencySpec(rLatency = 0, wLatency = 1, ruw = "new") with ToggleMaskAndEnable class CombMemSpecOldRUW extends MemLatencySpec(rLatency = 0, wLatency = 1, ruw = "old") with ToggleMaskAndEnable // Odd combination: combinational read with 2-cycle write latency class CombMemWL2SpecNewRUW extends MemLatencySpec(rLatency = 0, wLatency = 2, ruw = "new") with ToggleMaskAndEnable class CombMemWL2SpecOldRUW extends MemLatencySpec(rLatency = 0, wLatency = 2, ruw = "old") with ToggleMaskAndEnable // Standard sync read mem class WriteFirstMemToggleSpec extends MemLatencySpec(rLatency = 1, wLatency = 1, ruw = "new") with ToggleMaskAndEnable class ReadFirstMemToggleSpec extends MemLatencySpec(rLatency = 1, wLatency = 1, ruw = "old") with ToggleMaskAndEnable // Read latency 2 class WriteFirstMemToggleSpecRL2 extends MemLatencySpec(rLatency = 2, wLatency = 1, ruw = "new") with ToggleMaskAndEnable class ReadFirstMemToggleSpecRL2 extends MemLatencySpec(rLatency = 2, wLatency = 1, ruw = "old") with ToggleMaskAndEnable // Write latency 2 class WriteFirstMemToggleSpecWL2 extends MemLatencySpec(rLatency = 1, wLatency = 2, ruw = "new") with ToggleMaskAndEnable class ReadFirstMemToggleSpecWL2 extends MemLatencySpec(rLatency = 1, wLatency = 2, ruw = "old") with ToggleMaskAndEnable // Read latency 2, write latency 2 class WriteFirstMemToggleSpecRL2WL2 extends MemLatencySpec(rLatency = 2, wLatency = 2, ruw = "new") with ToggleMaskAndEnable class ReadFirstMemToggleSpecRL2WL2 extends MemLatencySpec(rLatency = 2, wLatency = 2, ruw = "old") with ToggleMaskAndEnable // Read latency 3, write latency 2 class WriteFirstMemToggleSpecRL3WL2 extends MemLatencySpec(rLatency = 3, wLatency = 2, ruw = "new") with ToggleMaskAndEnable class ReadFirstMemToggleSpecRL3WL2 extends MemLatencySpec(rLatency = 3, wLatency = 2, ruw = "old") with ToggleMaskAndEnable // Read latency 2, write latency 4 -> ToggleSpec pattern only valid for write-first at this combo class WriteFirstMemToggleSpecRL2WL4 extends MemLatencySpec(rLatency = 2, wLatency = 4, ruw = "new") with ToggleMaskAndEnable