From 5aa7129c6225d017d971b4906f4d4701c347c707 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:14:28 +0000 Subject: [PATCH 1/7] [midasexamples] Factor out some testutils from TutorialSuite --- .../src/test/scala/TestSuiteCommon.scala | 54 ++++++++++++++++++- .../scala/midasexamples/TutorialSuite.scala | 27 ---------- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/sim/firesim-lib/src/test/scala/TestSuiteCommon.scala b/sim/firesim-lib/src/test/scala/TestSuiteCommon.scala index b7ce38cc..0b8e3692 100644 --- a/sim/firesim-lib/src/test/scala/TestSuiteCommon.scala +++ b/sim/firesim-lib/src/test/scala/TestSuiteCommon.scala @@ -2,10 +2,21 @@ package firesim import java.io.File +import scala.io.Source import scala.sys.process.{stringSeqToProcess, ProcessLogger} /** - * NB: not thread-safe + * An base class for implementing FireSim integration tests that call out to the Make + * buildsystem. These tests typically have three steps whose results are tracked by scalatest: + * 1) Elaborate the target and compile it through golden gate (ElaborateAndCompile) + * 2) Compile a metasimulator for the generated RTL (compileMlSimulator) + * 3) Run the metasimulator. Running a metasimualtion is somewaht + * target-specific and is handled differently by different subclasses. + * + * Some tests inspect the simulation outputs, or run other simulators. See the + * [[TutorialSuite]] for examples of that. + * + * NB: Not thread-safe. */ abstract class TestSuiteCommon extends org.scalatest.flatspec.AnyFlatSpec { @@ -98,9 +109,48 @@ abstract class TestSuiteCommon extends org.scalatest.flatspec.AnyFlatSpec { } } } + + /** + * Extracts all lines in a file that begin with a specific prefix, removing + * extra whitespace between the prefix and the remainder of the line + * + * @param filename Input file + * @param prefix The per-line prefix to filter with + * @param linesToDrop Some number of matched lines to be removed + * @param headerLines An initial number of lines to drop before filtering. + * Assertions, Printf output have a single line header. + * MLsim stdout has some unused output, so set this to 1 by default + * + */ + def extractLines(filename: File, prefix: String, linesToDrop: Int = 0, headerLines: Int = 1): Seq[String] = { + val lines = Source.fromFile(filename).getLines.toList.drop(headerLines) + lines.filter(_.startsWith(prefix)) + .dropRight(linesToDrop) + .map(_.stripPrefix(prefix).replaceAll(" +", " ")) + } + + + /** + * Diffs two sets of lines. Wrap calls to this function in a scalatest + * behavior spec. @param aName and @param bName can be used to provide more + * insightful assertion messages in scalatest reporting. + */ + def diffLines( + aLines: Seq[String], + bLines: Seq[String], + aName: String = "Actual output", + bName: String = "Expected output"): Unit = { + assert(aLines.size == bLines.size && aLines.nonEmpty, + s"\n${aName} length (${aLines.size}) and ${bName} length (${bLines.size}) differ.") + for ((a, b) <- bLines.zip(aLines)) { + assert(a == b) + } + } } -// HACK: Hijacks TestSuiteCommon to run the MIDAS unit tests +/** + * Hijacks TestSuiteCommon (mostly for make related features) to run the synthesizable unit tests. + */ abstract class MidasUnitTestSuite(unitTestConfig: String, shouldFail: Boolean = false) extends TestSuiteCommon { val targetTuple = unitTestConfig // GENERATED_DIR & OUTPUT_DIR are only used to properly invoke `make clean` diff --git a/sim/src/test/scala/midasexamples/TutorialSuite.scala b/sim/src/test/scala/midasexamples/TutorialSuite.scala index 089fb475..54dad36b 100644 --- a/sim/src/test/scala/midasexamples/TutorialSuite.scala +++ b/sim/src/test/scala/midasexamples/TutorialSuite.scala @@ -63,33 +63,6 @@ abstract class TutorialSuite( } } - /** - * Extracts all lines in a file that begin with a specific prefix, removing - * extra whitespace between the prefix and the remainder of the line - * - * @param filename Input file - * @param prefix The per-line prefix to filter with - * @param linesToDrop Some number of matched lines to be removed - * @param headerLines An initial number of lines to drop before filtering. - * Assertions, Printf output have a single line header. - * MLsim stdout has some unused output, so set this to 1 by default - * - */ - def extractLines(filename: File, prefix: String, linesToDrop: Int = 0, headerLines: Int = 1): Seq[String] = { - val lines = Source.fromFile(filename).getLines.toList.drop(headerLines) - lines.filter(_.startsWith(prefix)) - .dropRight(linesToDrop) - .map(_.stripPrefix(prefix).replaceAll(" +", " ")) - } - - def diffLines(expectedLines: Seq[String], actualLines: Seq[String]): Unit = { - assert(actualLines.size == expectedLines.size && actualLines.nonEmpty, - s"\nActual output had length ${actualLines.size}. Expected ${expectedLines.size}") - for ((vPrint, sPrint) <- expectedLines.zip(actualLines)) { - assert(sPrint == vPrint) - } - } - // Checks that a bridge generated log in ${genDir}/${synthLog} matches output // generated directly by the RTL simulator (usually with printfs) def diffSynthesizedLog(synthLog: String, From 3b65793a25158e32ee7ef9597f3b08f219326ba2 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:15:28 +0000 Subject: [PATCH 2/7] [fasedtests] Allow the user to specify logfile / waves file This to match midasexamples --- sim/src/main/makefrag/fasedtests/Makefrag | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sim/src/main/makefrag/fasedtests/Makefrag b/sim/src/main/makefrag/fasedtests/Makefrag index 23f61832..193513d4 100644 --- a/sim/src/main/makefrag/fasedtests/Makefrag +++ b/sim/src/main/makefrag/fasedtests/Makefrag @@ -81,17 +81,24 @@ vcs = $(GENERATED_DIR)/$(DESIGN) vcs_debug = $(GENERATED_DIR)/$(DESIGN)-debug xsim = $(GENERATED_DIR)/$(DESIGN)-$(PLATFORM) +logfile = $(if $(LOGFILE),$(abspath $(LOGFILE)),$(OUTPUT_DIR)/$1.out) +waveform = $(if $(WAVEFORM),$(abspath $(WAVEFORM)),$(OUTPUT_DIR)/$1.$2) + run-verilator: $(verilator) - cd $( err + mkdir -p $(OUTPUT_DIR) + cd $( $(call logfile,verilator) run-verilator-debug: $(verilator_debug) - cd $( err + mkdir -p $(OUTPUT_DIR) + cd $( $(call logfile,verilator) run-vcs: $(vcs) - cd $( err + mkdir -p $(OUTPUT_DIR) + cd $( $(call logfile,vcs) run-vcs-debug: $(vcs_debug) - cd $( err + mkdir -p $(OUTPUT_DIR) + cd $( $(call logfile,vcs) .PHONY: run-xsim run-xsim: $(xsim) From a1adec5d443b96ff005eaeb960fcd945f32da438 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:16:03 +0000 Subject: [PATCH 3/7] [fasedtests] Support more general runtime configuration handling in scalatests --- sim/src/main/scala/fasedtests/Config.scala | 1 + .../scala/fasedtests/FASEDTestSuite.scala | 68 ++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/sim/src/main/scala/fasedtests/Config.scala b/sim/src/main/scala/fasedtests/Config.scala index 97ee6437..1306b47b 100644 --- a/sim/src/main/scala/fasedtests/Config.scala +++ b/sim/src/main/scala/fasedtests/Config.scala @@ -54,6 +54,7 @@ class WithNTransactions(num: Int) extends Config((site, here, up) => { case NumTransactions => num }) +class NT10e3 extends WithNTransactions(1000) class NT10e5 extends WithNTransactions(100000) class NT10e6 extends WithNTransactions(1000000) class NT10e7 extends WithNTransactions(10000000) diff --git a/sim/src/test/scala/fasedtests/FASEDTestSuite.scala b/sim/src/test/scala/fasedtests/FASEDTestSuite.scala index 3ee4314a..1a1ca49d 100644 --- a/sim/src/test/scala/fasedtests/FASEDTestSuite.scala +++ b/sim/src/test/scala/fasedtests/FASEDTestSuite.scala @@ -14,11 +14,42 @@ import freechips.rocketchip.system.DefaultTestSuites._ import firesim.configs.LlcKey +/** Different runtime-configuration modes extend this trait. See below. */ +sealed trait RuntimeConfig { + def behaviorString: String +} + +/** Use the .conf generated by GG by default. */ +case object DefaultRuntimeConfig extends RuntimeConfig { + val behaviorString = "with default runtime conf" +} + +/** Provide no conf file. PlusArgs must be called out in the test class. */ +case object EmptyRuntimeConfig extends RuntimeConfig { + val behaviorString = "with no base runtime conf" +} + +/** Specific an alternate path to a conf file. */ +case class CustomRuntimeConfig(pathRelativeToSim: String) extends RuntimeConfig { + val behaviorString = s"with runtime conf ${pathRelativeToSim}" +} + +/** + * A specialization of TestSuiteCommon for FASED-specific testing. Mostly + * handles differences in the makefrag vs midasexamples.. + * + * @param topModuleClass DESIGN: target top-level module class + * @param targetConfigs TARGET_CONFIG: config string to parameterize the target + * @param platformConfigs PLATFORM_CONFIG: config string to configure GG + * @param baseRuntimeConfig Default runtime conf handling for runtest + * @param additionalPlusArgs Non-standard plusargs to add to runTest invocations by default + */ abstract class FASEDTest( topModuleClass: String, targetConfigs: String, platformConfigs: String, - N: Int = 8 + baseRuntimeConfig: RuntimeConfig = DefaultRuntimeConfig, + additionalPlusArgs: Seq[String] = Seq(), ) extends firesim.TestSuiteCommon { import scala.concurrent.duration._ import ExecutionContext.Implicits.global @@ -33,20 +64,37 @@ abstract class FASEDTest( make((s"run-${backend}%s".format(if (debug) "-debug" else "") +: args):_*) } - def runTest(backend: String, debug: Boolean, args: Seq[String] = Nil, name: String = "pass") = { - compileMlSimulator(backend, debug) + def runTest( + backend: String, + debug: Boolean = false, + logFile: Option[File] = None, + baseRuntimeConfig: RuntimeConfig = baseRuntimeConfig, + additionalPlusArgs: Seq[String] = additionalPlusArgs, + additionalMakeArgs: Seq[String] = Seq(), + behaviorSpec: Option[String] = None) = { + val runtimeConfArg: Option[String] = baseRuntimeConfig match { + case DefaultRuntimeConfig => None + case EmptyRuntimeConfig => Some(s"SIM_RUNTIME_CONF=") + case CustomRuntimeConfig(path) => Some(s"SIM_RUNTIME_CONF=$path") + } + val plusArgs = Seq(s"""EXTRA_SIM_ARGS=${additionalPlusArgs.mkString(" ")}""") + val logArg = logFile.map { logName => s"LOGFILE=${logName}" } + + val makeArgs = + runtimeConfArg ++: + plusArgs ++: + logArg ++: + additionalMakeArgs + + if (isCmdAvailable(backend)) { - it should s"run on $backend" in { - assert(invokeMlSimulator(backend, debug, args) == 0) + it should behaviorSpec.getOrElse(s"run on $backend") in { + assert(invokeMlSimulator(backend, debug, makeArgs) == 0) } } } - def runTests() { - runTest("verilator", false) - //runTest("vcs", true) - } - behavior of s"FASED Instance configured with ${platformConfigs} driven by target: ${topModuleClass}" + compileMlSimulator("verilator", false) runTest("verilator", false) } From 2ea09c9cc951961ca055f3d15dcc50d43bf1878b Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:16:43 +0000 Subject: [PATCH 4/7] [fased] Add assertions for some LBP overflow conditions --- .../main/scala/midas/models/dram/LatencyBandwidthPipe.scala | 3 +++ sim/midas/src/main/scala/midas/models/dram/TimingModel.scala | 2 ++ 2 files changed, 5 insertions(+) diff --git a/sim/midas/src/main/scala/midas/models/dram/LatencyBandwidthPipe.scala b/sim/midas/src/main/scala/midas/models/dram/LatencyBandwidthPipe.scala index fa5fca5b..2fde0e13 100644 --- a/sim/midas/src/main/scala/midas/models/dram/LatencyBandwidthPipe.scala +++ b/sim/midas/src/main/scala/midas/models/dram/LatencyBandwidthPipe.scala @@ -67,6 +67,7 @@ class LatencyPipe(cfg: LatencyPipeConfig)(implicit p: Parameters) extends SplitT wResp.bits := writePipe.io.deq.bits.xaction writePipe.io.deq.ready := wResp.ready && writeDone + assert(writePipe.io.enq.ready || !newWReq, "LBP write latency pipe would overflow.") // ***** Read Latency Pipe ***** val readPipe = Module(new Queue(new ReadPipeEntry, cfg.maxReads, flow = true)) @@ -79,5 +80,7 @@ class LatencyPipe(cfg: LatencyPipeConfig)(implicit p: Parameters) extends SplitT rResp.valid := readPipe.io.deq.valid && readDone rResp.bits := readPipe.io.deq.bits.xaction readPipe.io.deq.ready := rResp.ready && readDone + + assert(readPipe.io.enq.ready || !nastiReq.ar.fire, "LBP read latency pipe would overflow.") } diff --git a/sim/midas/src/main/scala/midas/models/dram/TimingModel.scala b/sim/midas/src/main/scala/midas/models/dram/TimingModel.scala index 7bba2644..db71d736 100644 --- a/sim/midas/src/main/scala/midas/models/dram/TimingModel.scala +++ b/sim/midas/src/main/scala/midas/models/dram/TimingModel.scala @@ -241,4 +241,6 @@ abstract class SplitTransactionModel(cfg: BaseConfig)(implicit p: Parameters) awQueue.io.enq.bits := nastiReq.aw.bits awQueue.io.enq.valid := nastiReq.aw.fire() awQueue.io.deq.ready := newWReq + assert(awQueue.io.enq.ready || !nastiReq.aw.fire, + "AW queue in SplitTransaction timing model would overflow.") } From 2d88f2cbec90a880f46a253b55311157dade6fbe Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:17:59 +0000 Subject: [PATCH 5/7] [fased] Add support for reset-initialized configuration registers --- .../cc/bridges/fased_memory_timing_model.cc | 26 ++++++++++++++----- .../cc/bridges/fased_memory_timing_model.h | 7 +++++ .../src/main/scala/midas/widgets/Widget.scala | 23 ++++++++++++---- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/sim/midas/src/main/cc/bridges/fased_memory_timing_model.cc b/sim/midas/src/main/cc/bridges/fased_memory_timing_model.cc index c508a5ee..88074be9 100644 --- a/sim/midas/src/main/cc/bridges/fased_memory_timing_model.cc +++ b/sim/midas/src/main/cc/bridges/fased_memory_timing_model.cc @@ -62,8 +62,17 @@ FASEDMemoryTimingModel::FASEDMemoryTimingModel( size_t delimit_idx = sub_arg.find("="); size_t suffix_idx = sub_arg.find(suffix); std::string key = sub_arg.substr(0, suffix_idx).c_str(); - int value = std::stoi(sub_arg.substr(delimit_idx+1).c_str()); - model_configuration[key] = value; + + // This is the only nullary plusarg supported by fased + // All other plusargs are key-value pairs that will be written to the bridge module + if (key == std::string("useHardwareDefaultRuntimeSettings")) { + require_all_runtime_settings = false; + } else if (suffix_idx == std::string::npos) { + throw std::runtime_error("[FASED] unknown nullary plusarg: " + key); + } else { + int value = std::stoi(sub_arg.substr(delimit_idx+1).c_str()); + model_configuration[key] = value; + } } } @@ -125,11 +134,16 @@ void FASEDMemoryTimingModel::init() { } if (!exclude) { - char buf[100]; - sprintf(buf, "No value provided for configuration register: %s", pair.first.c_str()); - throw std::runtime_error(buf); + if (require_all_runtime_settings) { + char buf[100]; + sprintf(buf, "[FASED] No value provided for configuration register: %s", pair.first.c_str()); + throw std::runtime_error(buf); + } else { + auto init_val = read(pair.first); + fprintf(stderr, "[FASED] Using hardware default of %u for configuration register %s\n", init_val, pair.first.c_str()); + } } else { - fprintf(stderr, "Ignoring writeable register: %s\n", pair.first.c_str()); + fprintf(stderr, "[FASED] Ignoring writeable register: %s\n", pair.first.c_str()); } } } diff --git a/sim/midas/src/main/cc/bridges/fased_memory_timing_model.h b/sim/midas/src/main/cc/bridges/fased_memory_timing_model.h index 8013951b..53b51691 100644 --- a/sim/midas/src/main/cc/bridges/fased_memory_timing_model.h +++ b/sim/midas/src/main/cc/bridges/fased_memory_timing_model.h @@ -122,6 +122,13 @@ private: bool has_latency_histograms() { return histograms.size() > 0; }; size_t mem_size; + // By default, FASED requires that plus args for all timing model parameters + // are passed in to prevent accidental misconfigurations (ex. when + // DRAM timing parameters are passed to an LBP). When this is set, using the plus arg + // +mm_useHardwareDefaultRuntimeSettings_, + // the driver will instead use the hardware reset values (which map to the values emitted in the + // runtime.conf) and print those values to the log instead. + bool require_all_runtime_settings = true; }; #endif // __FASED_MEMORY_TIMING_MODEL_H diff --git a/sim/midas/src/main/scala/midas/widgets/Widget.scala b/sim/midas/src/main/scala/midas/widgets/Widget.scala index c0ebcb4b..ac78ee7f 100644 --- a/sim/midas/src/main/scala/midas/widgets/Widget.scala +++ b/sim/midas/src/main/scala/midas/widgets/Widget.scala @@ -80,22 +80,35 @@ abstract class WidgetImp(wrapper: Widget) extends LazyModuleImp(wrapper) { // For inputs, generates a registers and binds that to the map // For outputs, direct binds the wire to the map def attachIO(io: Record, prefix: String = ""): Unit = { - def innerAttachIO(node: Data, name: String): Unit = node match { + + /** + * For FASED memory timing models, initalize programmable registers to defaults if provided. + * See [[midas.models.HasProgrammableRegisters]] for more detail. + */ + def getInitValue(field: Bits, parent: Data): Option[UInt] = parent match { + case p: midas.models.HasProgrammableRegisters if p.regMap.isDefinedAt(field) => + Some(p.regMap(field).default.U) + case _ => None + } + + def innerAttachIO(node: Data, parent: Data, name: String): Unit = node match { case (b: Bits) => (DataMirror.directionOf(b): @unchecked) match { case ActualDirection.Output => attach(b, s"${name}", ReadOnly) - case ActualDirection.Input => genWOReg(b, name) + case ActualDirection.Input => + genAndAttachReg(b, name, getInitValue(b, parent)) } case (v: Vec[_]) => { - (v.zipWithIndex).foreach({ case (elm, idx) => innerAttachIO(elm, s"${name}_$idx")}) + (v.zipWithIndex).foreach({ case (elm, idx) => innerAttachIO(elm, node, s"${name}_$idx")}) } case (r: Record) => { - r.elements.foreach({ case (subName, elm) => innerAttachIO(elm, s"${name}_${subName}")}) + r.elements.foreach({ case (subName, elm) => innerAttachIO(elm, node, s"${name}_${subName}")}) } case _ => new RuntimeException("Cannot bind to this sort of node...") } - io.elements.foreach({ case (name, elm) => innerAttachIO(elm, s"${prefix}${name}")}) + io.elements.foreach({ case (name, elm) => innerAttachIO(elm, io, s"${prefix}${name}")}) } + def attachDecoupledSink(channel: DecoupledIO[UInt], name: String): Int = { crRegistry.allocate(DecoupledSinkEntry(channel, name)) } From 7200729adf681a620336376534322c7fae4325cb Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:19:31 +0000 Subject: [PATCH 6/7] [fasedtests] Test reset-intialized configuration registers --- .../scala/fasedtests/FASEDTestSuite.scala | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sim/src/test/scala/fasedtests/FASEDTestSuite.scala b/sim/src/test/scala/fasedtests/FASEDTestSuite.scala index 1a1ca49d..b696e5c7 100644 --- a/sim/src/test/scala/fasedtests/FASEDTestSuite.scala +++ b/sim/src/test/scala/fasedtests/FASEDTestSuite.scala @@ -99,6 +99,31 @@ abstract class FASEDTest( } class AXI4FuzzerLBPTest extends FASEDTest("AXI4Fuzzer", "DefaultConfig", "DefaultF1Config") + +// Sanity checks that target output is the same when using the default runtime +// configuration and the hardwired values. +class CheckHardwiredValuesTest extends FASEDTest("AXI4Fuzzer", "NT10e3_AddrBits16_DefaultConfig", "DefaultF1Config") { + val logA = new File(s"$outDir/using-runtime-conf.out") + runTest( + "verilator", + logFile = Some(logA), + behaviorSpec = Some("run using a runtime.conf")) + + val logB = new File(s"$outDir/using-hardwired-settings.out") + runTest( + "verilator", + logFile = Some(logB), + baseRuntimeConfig = EmptyRuntimeConfig, + additionalPlusArgs = Seq("+mm_useHardwareDefaultRuntimeSettings_0"), + behaviorSpec = Some("run using initialization values")) + + "Initialization values for configuration registers" should "produce the same target behavior as using the default runtime.conf" in { + val aLines = extractLines(logA, "AXI4FuzzMaster_0", headerLines = 0) + val bLines = extractLines(logB, "AXI4FuzzMaster_0", headerLines = 0) + diffLines(aLines, bLines, logA.getName, logB.getName) + } +} + class AXI4FuzzerMultiChannelTest extends FASEDTest("AXI4Fuzzer", "FuzzMask3FFF_QuadFuzzer_QuadChannel_DefaultConfig", "DefaultF1Config") class AXI4FuzzerFCFSTest extends FASEDTest("AXI4Fuzzer", "FCFSConfig", "DefaultF1Config") class AXI4FuzzerFRFCFSTest extends FASEDTest("AXI4Fuzzer", "FRFCFSConfig", "DefaultF1Config") From 5e6d32fe66c5b963cfdd4686c12b0ae4b3f34901 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 1 Dec 2021 20:55:52 +0000 Subject: [PATCH 7/7] [fased] Pipeline an input to an LLC assert for new initialization --- .../src/main/scala/midas/models/dram/LastLevelCache.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sim/midas/src/main/scala/midas/models/dram/LastLevelCache.scala b/sim/midas/src/main/scala/midas/models/dram/LastLevelCache.scala index 27ef3f05..255c36a4 100644 --- a/sim/midas/src/main/scala/midas/models/dram/LastLevelCache.scala +++ b/sim/midas/src/main/scala/midas/models/dram/LastLevelCache.scala @@ -172,12 +172,12 @@ class LLCModel(cfg: BaseConfig)(implicit p: Parameters) extends NastiModule()(p) val mshr_available = mshrs.exists({m: MSHR => m.available() }) val mshr_next_idx = mshrs.indexWhere({ m: MSHR => m.available() }) - // TODO: Put this on a switch val mshrs_allocated = mshrs.count({m: MSHR => m.valid}) - assert((mshrs_allocated < io.settings.activeMSHRs) || !mshr_available, + assert((mshrs_allocated < RegNext(io.settings.activeMSHRs)) || !mshr_available, "Too many runtime MSHRs exposed given runtime programmable limit") - assert((mshrs_allocated === io.settings.activeMSHRs) || mshr_available, - "Too few runtime MSHRs exposed given runtime programmable limit") + + assert((mshrs_allocated === RegNext(io.settings.activeMSHRs)) || mshr_available, + "Too few runtime MSHRs exposed given runtime programmable limit.") val s2_ar_mem = Module(new Queue(new NastiReadAddressChannel, 2)) val s2_aw_mem = Module(new Queue(new NastiWriteAddressChannel, 2))