From 5aa7129c6225d017d971b4906f4d4701c347c707 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 30 Nov 2021 23:14:28 +0000 Subject: [PATCH 01/17] [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 02/17] [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 03/17] [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 04/17] [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 05/17] [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 06/17] [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 07/17] [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)) From 431de3104a326d71bdb03e190853e9e4fb576e89 Mon Sep 17 00:00:00 2001 From: Cloud User Date: Tue, 14 Dec 2021 23:52:25 +0000 Subject: [PATCH 08/17] Appending WithAutoILA mixin to BaseF1Config. Adding note about WithAutoILA and WithILATopWiringTransform to AutoILA docs. --- .../Debugging-Hardware-Using-ILA.rst | 5 +++++ sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala | 1 + 2 files changed, 6 insertions(+) diff --git a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst index a066c43a..f980a509 100644 --- a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst +++ b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst @@ -18,6 +18,11 @@ signals directly in the Chisel source. These will be consumed by a downstream FIRRTL pass which wires out the annotated signals, and binds them to an appropriately sized ILA instance. +Enabling AutoILA +---------------- + +To enable AutoILA, mixins `WithILATopWiringTransform` and `WithAutoILA` must be appended to the `PLATFORM_CONFIG`. These are appended by default to the `BaseF1Config`. + Annotating Signals ------------------------ diff --git a/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala b/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala index 8ccd0543..2b717fdc 100644 --- a/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala +++ b/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala @@ -86,5 +86,6 @@ class BaseF1Config extends Config( new WithAsyncResetReplacement ++ new WithEC2F1Artefacts ++ new WithILATopWiringTransform ++ + new WithAutoILA ++ new midas.F1Config ) From 5fc0d946fcb09275289bf7529631df3f09b04cd0 Mon Sep 17 00:00:00 2001 From: James Dunn Date: Thu, 16 Dec 2021 01:26:49 +0000 Subject: [PATCH 09/17] Addressing PR comment and removing WithILATopWiringTransform note in the docs. --- .../Debugging-Hardware-Using-ILA.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst index f980a509..752a0c45 100644 --- a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst +++ b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst @@ -21,7 +21,7 @@ appropriately sized ILA instance. Enabling AutoILA ---------------- -To enable AutoILA, mixins `WithILATopWiringTransform` and `WithAutoILA` must be appended to the `PLATFORM_CONFIG`. These are appended by default to the `BaseF1Config`. +To enable AutoILA, mixin `WithAutoILA` must be appended to the `PLATFORM_CONFIG`. This is appended by default to the `BaseF1Config`. Annotating Signals ------------------------ From ec442e0955c603171a3f3865d99818e2ea751a70 Mon Sep 17 00:00:00 2001 From: Hasan Genc Date: Tue, 18 Jan 2022 00:03:24 -0800 Subject: [PATCH 10/17] Reduce Gemmini build's frequency (cherry picked from commit 3da301be1e741329042bcbcb6a0bad4c28b8305f) --- deploy/sample-backup-configs/sample_config_build_recipes.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/sample-backup-configs/sample_config_build_recipes.ini b/deploy/sample-backup-configs/sample_config_build_recipes.ini index 695e0622..450bb77c 100644 --- a/deploy/sample-backup-configs/sample_config_build_recipes.ini +++ b/deploy/sample-backup-configs/sample_config_build_recipes.ini @@ -59,7 +59,7 @@ deploytriplet=None [firesim-rocket-singlecore-gemmini-no-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimConfigTweaks_chipyard.GemminiRocketConfig -PLATFORM_CONFIG=WithAutoILA_F110MHz_BaseF1Config +PLATFORM_CONFIG=WithAutoILA_F30MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None From 5bd648c2d15c05ecba1929b6b59ee1a1c0fcd314 Mon Sep 17 00:00:00 2001 From: Abraham Gonzalez Date: Tue, 18 Jan 2022 20:49:27 -0800 Subject: [PATCH 11/17] Remove hardcoded aws fpga version (#909) * Determine AWS FPGA hash locally * Update aws-fpga naming in run_farm.py --- deploy/runtools/run_farm.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/deploy/runtools/run_farm.py b/deploy/runtools/run_farm.py index d3537708..fc5fe10b 100644 --- a/deploy/runtools/run_farm.py +++ b/deploy/runtools/run_farm.py @@ -384,14 +384,15 @@ class InstanceDeployManager: def get_and_install_aws_fpga_sdk(self): """ Installs the aws-sdk. This gets us access to tools to flash the fpga. """ - # TODO: we checkout a specific version of aws-fpga here, in case upstream - # master is bumped. But now we have to remember to change AWS_FPGA_FIRESIM_UPSTREAM_VERSION - # when we bump our stuff. Need a better way to do this. - AWS_FPGA_FIRESIM_UPSTREAM_VERSION = "6c707ab4a26c2766b916dad9d40727266fa0e4ef" - self.instance_logger("""Installing AWS FPGA SDK on remote nodes. Upstream hash: {}""".format(AWS_FPGA_FIRESIM_UPSTREAM_VERSION)) + with cd("../"): + # use local version of aws_fpga on runfarm nodes + aws_fpga_upstream_version = local('git -C platforms/f1/aws-fpga describe --tags --always --dirty', capture=True) + if "-dirty" in aws_fpga_upstream_version: + rootLogger.critical("Unable to use local changes to aws-fpga. Continuing without them.") + self.instance_logger("""Installing AWS FPGA SDK on remote nodes. Upstream hash: {}""".format(aws_fpga_upstream_version)) with warn_only(), StreamLogger('stdout'), StreamLogger('stderr'): run('git clone https://github.com/aws/aws-fpga') - run('cd aws-fpga && git checkout ' + AWS_FPGA_FIRESIM_UPSTREAM_VERSION) + run('cd aws-fpga && git checkout ' + aws_fpga_upstream_version) with cd('/home/centos/aws-fpga'), StreamLogger('stdout'), StreamLogger('stderr'): run('source sdk_setup.sh') @@ -547,7 +548,7 @@ class InstanceDeployManager: self.instance_logger("Starting Vivado virtual JTAG.") with StreamLogger('stdout'), StreamLogger('stderr'): run("""screen -S virtual_jtag -d -m bash -c "script -f -c 'sudo fpga-start-virtual-jtag -P 10201 -S 0'"; sleep 1""") - + def kill_ila_server(self): """ Kill the vivado hw_server and virtual jtag """ with warn_only(), StreamLogger('stdout'), StreamLogger('stderr'): From 0ed1e42df82fd00da694d320cb7e5311de64591a Mon Sep 17 00:00:00 2001 From: Abraham Gonzalez Date: Thu, 20 Jan 2022 11:03:37 -0800 Subject: [PATCH 12/17] Port to Python3 + Switch to GH-A CI (#878) * First pass at porting to python3 * Fix import errors | Setup user argcomplete * Update awstools CLI with user data file | Bump CI to use it * Wait until launch is complete * Add userdata as string | Use sudo for machine-launch-script * Remove execute permissions on machine-launch-script * Better match on machine-launch-script complete * Revert python-devel removal * Use python3 for pytests * Update more python3 items * Remove extra shebang * Port docs to python3 and add to CI * Add ISCA experiments to CI build check * Use yum not apt-get * Add make to doc section * Bump multilate-loadgen for sysroot fix * For BW test build don't use shebang * Fix docs Makefile options * Fix more doc warnings * Add first set of regression tests * Fix raw_input * Regression bump | Run workload fix * Add functools to topology * Fix linux poweroff test (nic still has issues) * Update regression scripts * Ignore machine-launch-script.sh in regression area * Fix map python3 issues * Get rid of shebangs * Fix more regressions * Print machine-launch.log on fail | More clarification on user_data * Transfer to CI some shorter regressions * Add a manual approval to fpga based tests * Fix indentation in config.yml * Fix test symlink * Use spot for CI manager instance | Try to use python3 for aws CI container | Version all pip packages * Make run-ini-api-tests an executable * Fix CI terminaterunfarm arg * Add firesim.pem file to manager * Bump python in CI instance * Bump pip in CI container * Remove pip sudo in CI container * Fix launch script pip version equals * Ini converted into strings * Properly pass test_dir to opts in CI * First pass at GH-A * Round 2 CI fixes * Try changes * Remove CircleCI | Switch to fancy GH-A * Rename self-host setup script * Update chmod * Use - instead of _ for env. vars * Rename some defs | Remove extra imports * Small comment updates * Forgot to import in ini-api tests | Small comment on Fabric timeouts * Add sys to linux poweroff * Update linux timeout, fix small imports * Update comment * Fix-up workflow-monitor.py * Avoid excessive logging in run-linux | Terminate spot instances after max-runtime * Add more workflow-monitor states | Add pty=False to running workloads * Update CI documentation | Add CI badge [ci skip] * Don't use spot instances * Update CI readme * Determine runner version from remote repo and check for runner setup * Address PR comments * Update CI_README location of where to find IPs | Forgot ret_code * Only run CI on prs/pushes to dev/main/master * Fix terminate_workflow_instances in init-manager.py * Cleanup FireSim repo cloning | Only run CI on PRs (since its runs on merge commit) --- .circleci/.gitignore | 1 - .circleci/build-default-workloads.py | 17 -- .circleci/change-workflow-instance-states.py | 21 -- .circleci/config.yml | 265 ------------------ .circleci/requirements.txt | 4 - .circleci/run-manager-pytests.py | 15 - .circleci/run-scala-test.py | 21 -- .circleci/workflow-monitor.py | 72 ----- .github/CI_README.md | 46 +++ .github/actions/build-scala-doc/action.yml | 10 + .github/actions/buildsetup/action.yml | 8 + .../actions/initial-scala-compile/action.yml | 8 + .github/actions/initialize-manager/action.yml | 13 + .../actions/install-ci-python-reqs/action.yml | 10 + .github/actions/job-end/action.yml | 8 + .github/actions/job-start/action.yml | 19 ++ .../actions/machinelaunchscript/action.yml | 8 + .../push-scaladoc-to-ghpages/action.yml | 29 ++ .github/actions/repo-setup-aws/action.yml | 17 ++ .github/actions/repo-setup/action.yml | 9 + .github/actions/run-scala-test/action.yml | 22 ++ .github/actions/scala-build/action.yml | 10 + {.circleci => .github/scripts}/Dockerfile | 4 +- .../scripts}/build-and-push-docker-image.sh | 0 .github/scripts/build-default-workloads.py | 25 ++ .../change-workflow-instance-states.py | 22 ++ .../scripts}/ci_variables.py | 10 +- {.circleci => .github/scripts}/common.py | 61 ++-- .../scripts}/cull-old-ci-instances.py | 11 +- .../scripts}/firesim-managerinit.expect | 0 .github/scripts/gh-a-runner.expect | 16 ++ .../scripts}/initialize-manager.py | 41 +-- .../scripts}/launch-manager-instance.py | 14 +- .github/scripts/requirements.txt | 5 + .github/scripts/run-ini-api-tests.py | 88 ++++++ .github/scripts/run-linux-poweroff.py | 44 +++ .github/scripts/run-manager-pytests.py | 15 + .../scripts}/run-sbt-command.py | 9 +- .github/scripts/run-scala-test.py | 26 ++ .github/scripts/setup-manager-self-hosted.py | 82 ++++++ .github/scripts/workflow-monitor.py | 71 +++++ .github/workflows/firesim-cull-instances.yml | 26 ++ .../workflows/firesim-publish-scala-doc.yml | 33 +++ .github/workflows/firesim-run-tests.yml | 204 ++++++++++++++ README.md | 4 +- deploy/awstools/awstools.py | 63 +++-- deploy/buildtools/buildconfig.py | 10 +- deploy/firesim | 8 +- .../runtools/firesim_topology_with_passes.py | 25 +- deploy/runtools/run_farm.py | 2 +- deploy/runtools/runtime_config.py | 6 +- deploy/tests/awstools/test_awstools.py | 2 +- deploy/util/streamlogger.py | 4 +- deploy/workloads/Makefile | 27 +- .../ccbench-cache-sweep/plotccbench.py | 12 +- deploy/workloads/gen-benchmark-rootfs.py | 10 +- .../mutilate-loadgen-riscv-release | 2 +- .../ping-latency/ping-latency-graph.py | 10 +- deploy/workloads/run-bw-test.sh | 2 +- deploy/workloads/run-ping-latency.sh | 2 +- deploy/workloads/run-simperf-test-latency.sh | 2 +- .../run-simperf-test-scale-supernode.sh | 2 +- deploy/workloads/run-simperf-test-scale.sh | 2 +- .../simperf-test-results.py | 8 +- .../simperf-test-scale-results.py | 8 +- docs/.gitignore | 1 + .../Debugging-Hanging-Simulators.rst | 2 +- .../Dromajo.rst | 6 +- .../Debugging-in-Software/RTL-Simulation.rst | 13 +- .../Generating-Different-Targets.rst | 18 +- .../Manager/Manager-Environment-Variables.rst | 2 +- docs/Advanced-Usage/Manager/Manager-Tasks.rst | 10 +- docs/Advanced-Usage/Miscellaneous-Tips.rst | 6 + .../Deprecated-Defining-Custom-Workloads.rst | 8 +- docs/Golden-Gate/Bridge-Walkthrough.rst | 4 +- ...red-Infrastructure-in-Your-AWS-Account.rst | 11 +- docs/Makefile | 4 +- docs/README.md | 6 +- docs/conf.py | 2 +- regression/aws-ec2-tests/.gitignore | 3 + regression/aws-ec2-tests/README.md | 8 + .../aws-ec2-tests/build-default-afis.sh | 11 + regression/aws-ec2-tests/defaults.sh | 32 +++ .../aws-ec2-tests/firesim-managerinit.expect | 5 + .../launch-setup-manager-instance.sh | 44 +++ regression/aws-ec2-tests/run-isca-2018.sh | 15 + scripts/aws-setup.py | 2 + scripts/machine-launch-script.sh | 48 ++-- .../midas/models/dram/parse_micron_headers.py | 3 +- sim/scripts/create-afi.py | 2 +- sim/scripts/create-bucket.py | 2 +- .../midasexamples/generate_memory_init.py | 3 +- target-design/switch/Makefile | 10 - 93 files changed, 1274 insertions(+), 658 deletions(-) delete mode 100644 .circleci/.gitignore delete mode 100755 .circleci/build-default-workloads.py delete mode 100755 .circleci/change-workflow-instance-states.py delete mode 100644 .circleci/config.yml delete mode 100644 .circleci/requirements.txt delete mode 100755 .circleci/run-manager-pytests.py delete mode 100755 .circleci/run-scala-test.py delete mode 100755 .circleci/workflow-monitor.py create mode 100644 .github/CI_README.md create mode 100644 .github/actions/build-scala-doc/action.yml create mode 100644 .github/actions/buildsetup/action.yml create mode 100644 .github/actions/initial-scala-compile/action.yml create mode 100644 .github/actions/initialize-manager/action.yml create mode 100644 .github/actions/install-ci-python-reqs/action.yml create mode 100644 .github/actions/job-end/action.yml create mode 100644 .github/actions/job-start/action.yml create mode 100644 .github/actions/machinelaunchscript/action.yml create mode 100644 .github/actions/push-scaladoc-to-ghpages/action.yml create mode 100644 .github/actions/repo-setup-aws/action.yml create mode 100644 .github/actions/repo-setup/action.yml create mode 100644 .github/actions/run-scala-test/action.yml create mode 100644 .github/actions/scala-build/action.yml rename {.circleci => .github/scripts}/Dockerfile (82%) rename {.circleci => .github/scripts}/build-and-push-docker-image.sh (100%) create mode 100755 .github/scripts/build-default-workloads.py create mode 100755 .github/scripts/change-workflow-instance-states.py rename {.circleci => .github/scripts}/ci_variables.py (55%) rename {.circleci => .github/scripts}/common.py (63%) rename {.circleci => .github/scripts}/cull-old-ci-instances.py (76%) rename {.circleci => .github/scripts}/firesim-managerinit.expect (100%) create mode 100755 .github/scripts/gh-a-runner.expect rename {.circleci => .github/scripts}/initialize-manager.py (56%) rename {.circleci => .github/scripts}/launch-manager-instance.py (79%) create mode 100644 .github/scripts/requirements.txt create mode 100755 .github/scripts/run-ini-api-tests.py create mode 100755 .github/scripts/run-linux-poweroff.py create mode 100755 .github/scripts/run-manager-pytests.py rename {.circleci => .github/scripts}/run-sbt-command.py (66%) create mode 100755 .github/scripts/run-scala-test.py create mode 100755 .github/scripts/setup-manager-self-hosted.py create mode 100755 .github/scripts/workflow-monitor.py create mode 100644 .github/workflows/firesim-cull-instances.yml create mode 100644 .github/workflows/firesim-publish-scala-doc.yml create mode 100644 .github/workflows/firesim-run-tests.yml mode change 100644 => 100755 deploy/awstools/awstools.py create mode 100644 regression/aws-ec2-tests/.gitignore create mode 100644 regression/aws-ec2-tests/README.md create mode 100755 regression/aws-ec2-tests/build-default-afis.sh create mode 100644 regression/aws-ec2-tests/defaults.sh create mode 100755 regression/aws-ec2-tests/firesim-managerinit.expect create mode 100755 regression/aws-ec2-tests/launch-setup-manager-instance.sh create mode 100755 regression/aws-ec2-tests/run-isca-2018.sh mode change 100644 => 100755 scripts/aws-setup.py diff --git a/.circleci/.gitignore b/.circleci/.gitignore deleted file mode 100644 index 0d20b648..00000000 --- a/.circleci/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pyc diff --git a/.circleci/build-default-workloads.py b/.circleci/build-default-workloads.py deleted file mode 100755 index 87a5df2a..00000000 --- a/.circleci/build-default-workloads.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python - -from fabric.api import * - -from common import manager_fsim_dir, manager_hostname -from ci_variables import ci_workflow_id - -def build_default_workloads(): - """ Builds workloads that will be run on F1 instances as part of CI """ - - with prefix('cd {} && source ./env.sh'.format(manager_fsim_dir)), \ - prefix('cd deploy/workloads'): - run("marshal -v build br-base.json") - run("make linux-poweroff") - -if __name__ == "__main__": - execute(build_default_workloads, hosts=[manager_hostname(ci_workflow_id)]) diff --git a/.circleci/change-workflow-instance-states.py b/.circleci/change-workflow-instance-states.py deleted file mode 100755 index 54ed43a5..00000000 --- a/.circleci/change-workflow-instance-states.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -# Changes the state of instances associated with the CI run's unique tag. -# Where a run is a workflow in CircleCI. Can be used to start, stop, or -# terminate. This may run from either the manager or from the CI instance. - -import sys -import argparse - -import common - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('tag_value', - help = "The tag used to identify workflow instances.") - parser.add_argument('state_change', - choices = ['terminate', 'stop', 'start'], - help = "The state transition to initiate on workflow instances.") - - args = parser.parse_args() - common.change_workflow_instance_states(args.tag_value, args.state_change) diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index fee4f766..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,265 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference -version: 2.1 -orbs: - aws-cli: circleci/aws-cli@1.0.0 -executors: - main-env: - docker: - - image: firesim/firesim-ci:v1.1 - user: "centos" - environment: - JVM_MEMORY: 3500M # Default JVM maximum heap limit - LANG: en_US.UTF-8 # required by sbt when it sees boost directories - MARSHAL_JLEVEL: 8 # This is unset by default, leading to starvation conditions on the manager - aws: - docker: - - image: cimg/python:2.7-node - -commands: - machinelaunchscript: - description: "Run firesim's machine launch script" - steps: - - run: - command: | - cd scripts && /usr/bin/bash ./machine-launch-script.sh - - install-ci-python-reqs: - description: "Installs python deps for manager-managing CI container" - steps: - - run: - command: | - pip install -r .circleci/requirements.txt - - buildsetup: - description: "Run firesim's build-setup.sh" - steps: - - run: - command: | - ./build-setup.sh fast - - scala-build: - description: "Compile all relevant Scala sources for CI" - steps: - - run: - command: | - source env.sh - make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND=test:compile - - build-scala-doc: - description: "Compiles Scala Doc" - steps: - - run: - command: | - source env.sh - make -C sim scaladoc - - push-scaladoc-to-ghpages: - description: "Pushes scaladoc to ghphage branch" - steps: - # The default SSH key does not have write permissions - - add_ssh_keys: - fingerprint: - - 0e:d9:c3:3b:62:03:7a:da:17:1f:a9:5a:4f:34:50:4c - - run: - command: | - git config --global user.email "biancolin@berkeley.edu" - git config --global user.name "circleci" - - when: - condition: << pipeline.git.tag >> - steps: - - run: - command: | - source env.sh - export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for << pipeline.git.tag >> release" - make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND='set apiDirectory := \"<< pipeline.git.tag >>\"; ghpagesPushSite' - - unless: - condition: << pipeline.git.tag >> - steps: - - run: - command: | - source env.sh - export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for dev:<< pipeline.git.revision >>" - make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND="ghpagesPushSite" - - run-scala-test: - description: "Runs the scala test with name ." - parameters: - target-project: - type: string - default: "midasexamples" - test-name: - type: string - test-package: - type: string - default: "firesim.midasexamples" - timeout: - type: string - default: "120m" - steps: - - run: - command: | - .circleci/run-scala-test.py << parameters.target-project >> << parameters.test-package >>.<< parameters.test-name >> - no_output_timeout: << parameters.timeout >> - - - initialize-manager: - description: "Setups the manager instance and firesim repo" - parameters: - max-runtime-hours: - type: integer - timeout: - type: string - default: "30m" - steps: - - run: - command: | - .circleci/initialize-manager.py << parameters.max-runtime-hours >> - no_output_timeout: << parameters.timeout >> - - # This avoids a race that occurs when multiple cold scala compilations are launched at the same time. - initial-scala-compile: - description: "Initial Scala Compilation" - parameters: - timeout: - type: string - default: "30m" - steps: - - run: - command: | - .circleci/run-sbt-command.py firesim test:compile - no_output_timeout: << parameters.timeout >> - - repo-setup: - description: "Runs all baseline setup tasks up to scala compilation in the CI container." - steps: - - checkout - - machinelaunchscript - - buildsetup - - scala-build - - repo-setup-aws: - description: "Runs all baseline setup to interact with a AWS-hosted manager instance" - steps: - - checkout - - aws-cli/setup - - install-ci-python-reqs - -jobs: - setup-default-manager: - executor: aws - environment: - TERM: xterm-256color - steps: - - repo-setup-aws - - run: - name: Initialize FireSim manager - command: | - .circleci/launch-manager-instance.py - - initialize-manager: - max-runtime-hours: 10 - - initial-scala-compile - - build-default-workloads: - description: "Invokes marshal to build the default rootfs." - executor: aws - steps: - - repo-setup-aws - - run: - command: | - .circleci/build-default-workloads.py - no_output_timeout: 30m - - run-manager-pytests: - executor: aws - steps: - - repo-setup-aws - - run: - command: .circleci/run-manager-pytests.py - - run-test-groupA: - executor: aws - steps: - - repo-setup-aws - - run-scala-test: - test-name: "CIGroupA" - - run-test-groupB: - executor: aws - steps: - - repo-setup-aws - - run-scala-test: - test-name: "CIGroupB" - - run-chipyard-tests: - executor: aws - steps: - - repo-setup-aws - - run-scala-test: - target-project: "firesim" - test-package: "firesim.firesim" - test-name: "CITests" - - publish-scala-doc: - executor: main-env - steps: - - repo-setup - - build-scala-doc - - push-scaladoc-to-ghpages - - cull-old-ci-instances: - executor: aws - steps: - - repo-setup-aws - - run: - name: Cull Old CI AWS Instances - command: | - .circleci/cull-old-ci-instances.py - -workflows: - version: 2 - - firesimCIall: - jobs: - - setup-default-manager - - - build-default-workloads: - requires: - - setup-default-manager - - - run-test-groupA: - requires: - - setup-default-manager - - - run-test-groupB: - requires: - - run-test-groupA - - - run-chipyard-tests: - requires: - - run-test-groupB - - - run-manager-pytests: - requires: - - setup-default-manager - - # This uses a CI container, not a manager instance - - publish-scala-doc: - filters: - branches: - only: - - dev - tags: - only: - - /[0-9]*\.[0-9]*\.[0-9]*/ - - cull-old-ci-instances: - triggers: - - schedule: - cron: "0 0,12 * * *" - filters: - branches: - only: - - dev - jobs: - - cull-old-ci-instances - diff --git a/.circleci/requirements.txt b/.circleci/requirements.txt deleted file mode 100644 index bfa7e74c..00000000 --- a/.circleci/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -fabric==1.14.0 -boto3==1.6.2 -pytz -pyyaml diff --git a/.circleci/run-manager-pytests.py b/.circleci/run-manager-pytests.py deleted file mode 100755 index 4d13d958..00000000 --- a/.circleci/run-manager-pytests.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -from fabric.api import * - -from common import manager_fsim_dir, manager_hostname -from ci_variables import ci_workflow_id - -def run_manager_pytests(): - """ Runs all manager pytests """ - - with cd(manager_fsim_dir), prefix('source env.sh'): - run("cd deploy && python -m pytest") - -if __name__ == "__main__": - execute(run_manager_pytests, hosts=[manager_hostname(ci_workflow_id)]) diff --git a/.circleci/run-scala-test.py b/.circleci/run-scala-test.py deleted file mode 100755 index c7a4d7fd..00000000 --- a/.circleci/run-scala-test.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -import sys - -from fabric.api import * - -from common import manager_fsim_dir, manager_hostname -from ci_variables import ci_workflow_id - -def run_scala_test(target_project, test_name): - """ Runs a scala test under the desired target project - - target_project -- The make variable to select the desired target project makefrag - - test_name -- the full classname of the test - """ - with cd(manager_fsim_dir), prefix('source env.sh'): - run("make -C sim testOnly TARGET_PROJECT={} SCALA_TEST={}".format(target_project, test_name)) - -if __name__ == "__main__": - execute(run_scala_test, sys.argv[1], sys.argv[2], hosts = [manager_hostname(ci_workflow_id)]) diff --git a/.circleci/workflow-monitor.py b/.circleci/workflow-monitor.py deleted file mode 100755 index f2e3eda1..00000000 --- a/.circleci/workflow-monitor.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python - -# Runs in the background on a manager instance to determine when it can be torn -# down by polling the workflow's state using CircleCI's v2.0 restful api. -# -# Terminate instances if: -# - the workflow is successful (all jobs complete successfully) -# - the workflow is cancelled -# Stop instances if: -# - the workflow has failed (all jobs have completed, but at least one has failed) -# -# Other states to consider: not_run, on_hold, unauthorized - -import httplib -import time -import sys -import base64 -import json - -from common import terminate_workflow_instances, stop_workflow_instances - -# Time between HTTPS requests to circleci -POLLING_INTERVAL_SECONDS = 60 -# Number of failed requests before stopping the instances -QUERY_FAILURE_THRESHOLD = 10 - -# We should never get to 'not_run' or 'unauthorized' but terminate for good measure -TERMINATE_STATES = ['success', 'canceled', 'not_run', 'unauthorized'] -STOP_STATES = ['failed', 'error'] -NOP_STATES = ['running', 'failing'] - -def main(workflow_id, circle_ci_token): - - state = None - consecutive_failures = 0 - auth_token = base64.b64encode(b"{}:", circle_ci_token) - headers = {'authorization': "Basic {}".format(auth_token)} - - while True: - time.sleep(POLLING_INTERVAL_SECONDS) - - conn = httplib.HTTPSConnection("circleci.com") - conn.request("GET", "/api/v2/workflow/{}".format(workflow_id), headers=headers) - - res = conn.getresponse() - - if res.status == httplib.OK: - consecutive_failures = 0 - res_dict = json.load(res) - state = res_dict["status"] - - print "Workflow {} status: {}".format(workflow_id, state) - if state in TERMINATE_STATES: - terminate_workflow_instances(workflow_id) - exit(0) - elif state in STOP_STATES: - stop_workflow_instances(workflow_id) - exit(0) - elif state not in NOP_STATES: - print "Unexpected Workflow State: {}".format(state) - raise ValueError - - else: - print "HTTP GET error: {} {}. Retrying.".format(res.status, res.reason) - consecutive_failures = consecutive_failures + 1 - if consecutive_failures == QUERY_FAILURE_THRESHOLD: - stop_workflow_instances(workflow_id) - exit(1) - - -if __name__ == "__main__": - main(sys.argv[1], sys.argv[2]) diff --git a/.github/CI_README.md b/.github/CI_README.md new file mode 100644 index 00000000..f6d3694f --- /dev/null +++ b/.github/CI_README.md @@ -0,0 +1,46 @@ +FireSim Continuous Integration +============================== + +Helpful Links: +* Workflow GUI - https://github.com/firesim/firesim/actions +* Chipyard Explanation of Github Actions (GH-A) - https://github.com/ucb-bar/chipyard/blob/dev/.github/CI_README.md + +Github Actions (GH-A) Description +--------------------------------- + +Much of the following CI infrastructure is based on the Chipyard CI. +For a basic explanation of how the GH-A CI works, see https://github.com/ucb-bar/chipyard/blob/dev/.github/CI_README.md. +However, there are a couple of notable differences/comments as follows: + +* In order to provide a fresh environment to test changes, the CI dynamically spawns a AWS instance and sets up GH-A +to use this instance as a GH-A self-hosted runner (see https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners). + * All scripts that run on the manager instance use Fabric with `localhost` and each GH-A step must include `runs-on: ${{ github.run_id }}` KV pair to indicate that you want to run directly on a runner that is on the manager instance. + * Currently only 4 runners are spawned per workflow (reused across multiple workflow runs - i.e. clicking rerun). Every commit gets its own manager instance and 4 runners that run on the manager instance. + * When the CI terminates/stops an instance, the CI automatically deregisters the runners from GH-A (any runner API calls require a personal token with the `repo` scope). +* The CI structure is as follows: + 1. Launch the manager instance + setup the N self-hosted runners. + 2. Run the original initialization (add the firesim.pem, run the build setup, etc). + 3. Continue with the rest of the tests using the GH-A runners. + + +Running FPGA-related Tasks +-------------------------- + +CI now includes the capability to run FPGA-simulations on specific PRs. +However, by default, this requires approval from the `firesim-fpga-approval` team (called a "deployment"). +You can gain approval to run FPGA-simulations in two ways. + +1. Each member in the `firesim-fpga-approval` team will receive an email asking for approval on a specific PR. From that email, they can approve the request and run the FPGA-simulation tests. +2. From the workflow run GUI (go to https://github.com/firesim/firesim/actions and click a specific workflow run) a `firesim-fpga-approval` team member can approve the deployment (note this button only shows up once the job that needs approval is reached). + +Debugging Failures +------------------ + +When a failure occurs on the manager instance the CI will stop or terminate the instance (terminate if your instance is using the spot market). +Currently, the only way to access any running instance that is created from the CI is to do the following: + +1. Request the CI PEM file needed to SSH into the CI instances (ask the FireSim developers) +2. Obtain the public IP address from the "Launch AWS instance used for the FireSim manager (instance info found here)" (which runs the `launch-manager-instance.py` script) step in the `setup-self-hosted-manager` job in the CI. +3. SSH into the instance and do any testing required. + +If the instance is stopped, then you must request a AWS IAM user account from the FireSim developers to access the EC2 console and restart the instance. diff --git a/.github/actions/build-scala-doc/action.yml b/.github/actions/build-scala-doc/action.yml new file mode 100644 index 00000000..2169ed86 --- /dev/null +++ b/.github/actions/build-scala-doc/action.yml @@ -0,0 +1,10 @@ +name: build-scala-doc +description: "Compiles Scala Doc" + +runs: + using: "composite" + steps: + - run: | + source env.sh + make -C sim scaladoc + shell: bash diff --git a/.github/actions/buildsetup/action.yml b/.github/actions/buildsetup/action.yml new file mode 100644 index 00000000..34a47a39 --- /dev/null +++ b/.github/actions/buildsetup/action.yml @@ -0,0 +1,8 @@ +name: buildsetup +description: "Run firesim's build-setup.sh" + +runs: + using: "composite" + steps: + - run: ./build-setup.sh fast + shell: bash diff --git a/.github/actions/initial-scala-compile/action.yml b/.github/actions/initial-scala-compile/action.yml new file mode 100644 index 00000000..7e1d568e --- /dev/null +++ b/.github/actions/initial-scala-compile/action.yml @@ -0,0 +1,8 @@ +name: initial-scala-compile +description: "Initial Scala Compilation" + +runs: + using: "composite" + steps: + - run: .github/scripts/run-sbt-command.py firesim test:compile + shell: bash diff --git a/.github/actions/initialize-manager/action.yml b/.github/actions/initialize-manager/action.yml new file mode 100644 index 00000000..e3a1519d --- /dev/null +++ b/.github/actions/initialize-manager/action.yml @@ -0,0 +1,13 @@ +name: initialize-manager +description: "Sets up the manager instance and firesim repo" + +inputs: + max-runtime-hours: + description: "Max runtime hours" + required: true + +runs: + using: "composite" + steps: + - run: .github/scripts/initialize-manager.py ${{ inputs.max-runtime-hours }} + shell: bash diff --git a/.github/actions/install-ci-python-reqs/action.yml b/.github/actions/install-ci-python-reqs/action.yml new file mode 100644 index 00000000..d391b5a5 --- /dev/null +++ b/.github/actions/install-ci-python-reqs/action.yml @@ -0,0 +1,10 @@ +name: install-ci-python-reqs +description: "Installs python deps for manager-managing CI container" + +runs: + using: "composite" + steps: + - run: | + pip3 install --upgrade pip==21.3.1 + python3 -m pip install -r .github/scripts/requirements.txt + shell: bash diff --git a/.github/actions/job-end/action.yml b/.github/actions/job-end/action.yml new file mode 100644 index 00000000..51b4f4d7 --- /dev/null +++ b/.github/actions/job-end/action.yml @@ -0,0 +1,8 @@ +name: job-end +description: "Save a job status" + +runs: + using: "composite" + steps: + - run: echo "success" > run_result + shell: bash diff --git a/.github/actions/job-start/action.yml b/.github/actions/job-start/action.yml new file mode 100644 index 00000000..dc4d0642 --- /dev/null +++ b/.github/actions/job-start/action.yml @@ -0,0 +1,19 @@ +name: job-start +description: "Setup a job status" +outputs: + run_result: + value: ${{ steps.run_result.outputs.run_result }} + +runs: + using: "composite" + steps: + - name: Restore the previous run result + uses: actions/cache@v2 + with: + path: run_result + key: ${{ github.run_id }}-${{ github.job }} + restore-keys: ${{ github.run_id }}-${{ github.job }} + - name: Set run_result to default or use cached value + id: run_result + run: echo "::set-output name=run_result::$(cat run_result 2>/dev/null || echo 'default')" + shell: bash diff --git a/.github/actions/machinelaunchscript/action.yml b/.github/actions/machinelaunchscript/action.yml new file mode 100644 index 00000000..df737b65 --- /dev/null +++ b/.github/actions/machinelaunchscript/action.yml @@ -0,0 +1,8 @@ +name: machinelaunchscript +description: "Run FireSim's machine-launch-script.sh" + +runs: + using: "composite" + steps: + - run: cd scripts/ && /usr/bin/bash machine-launch-script.sh + shell: bash diff --git a/.github/actions/push-scaladoc-to-ghpages/action.yml b/.github/actions/push-scaladoc-to-ghpages/action.yml new file mode 100644 index 00000000..2f1a772d --- /dev/null +++ b/.github/actions/push-scaladoc-to-ghpages/action.yml @@ -0,0 +1,29 @@ +name: push-scaladoc-to-ghpages +description: "Pushes scaladoc to ghphage branch" + +runs: + using: "composite" + steps: + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SERVER_KEY }} + known_hosts: ${{ secrets.SERVER_NAME }} + + - run: | + git config --global user.email "biancolin@berkeley.edu" + git config --global user.name "github-actions" + shell: bash + + - run: | + if [[ "${{ GITHUB_REF_TYPE }}" != 'tag' ]]; then + source env.sh + export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for ${{ GITHUB_REF_NAME }} release" + make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND='set apiDirectory := \"${{ GITHUB_REF_NAME }}\"; ghpagesPushSite' + else + source env.sh + export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for dev:${{ GITHUB_SHA }}" + make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND="ghpagesPushSite" + fi + shell: bash + diff --git a/.github/actions/repo-setup-aws/action.yml b/.github/actions/repo-setup-aws/action.yml new file mode 100644 index 00000000..d818d234 --- /dev/null +++ b/.github/actions/repo-setup-aws/action.yml @@ -0,0 +1,17 @@ +name: repo-setup-aws +description: "Runs all baseline setup to interact with a AWS-hosted manager instance" + +runs: + using: "composite" + steps: + - uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ env.AWS-ACCESS-KEY-ID }} + aws-secret-access-key: ${{ env.AWS-SECRET-ACCESS-KEY }} + aws-region: ${{ env.AWS-DEFAULT-REGION }} + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ env.FIRESIM_PEM }} + known_hosts: unnecessary + - uses: ./.github/actions/install-ci-python-reqs diff --git a/.github/actions/repo-setup/action.yml b/.github/actions/repo-setup/action.yml new file mode 100644 index 00000000..95b2c29a --- /dev/null +++ b/.github/actions/repo-setup/action.yml @@ -0,0 +1,9 @@ +name: repo-setup +description: "Runs all baseline setup tasks up to scala compilation in the CI container." + +runs: + using: "composite" + steps: + - uses: ./.github/actions/machinelaunchscript + - uses: ./.github/actions/buildsetup + - uses: ./.github/actions/scala-build diff --git a/.github/actions/run-scala-test/action.yml b/.github/actions/run-scala-test/action.yml new file mode 100644 index 00000000..100942dd --- /dev/null +++ b/.github/actions/run-scala-test/action.yml @@ -0,0 +1,22 @@ +name: run-scala-test +description: "Runs the scala test with name ." + +inputs: + target-project: + description: "Target project" + required: false + default: "midasexamples" + test-name: + description: "Test name" + required: true + test-package: + description: "Test package" + required: false + default: "firesim.midasexamples" + +runs: + using: "composite" + steps: + - run: | + .github/scripts/run-scala-test.py ${{ inputs.target-project }} ${{ inputs.test-package }}.${{ inputs.test-name }} + shell: bash diff --git a/.github/actions/scala-build/action.yml b/.github/actions/scala-build/action.yml new file mode 100644 index 00000000..0d99f04e --- /dev/null +++ b/.github/actions/scala-build/action.yml @@ -0,0 +1,10 @@ +name: scala-build +description: "Compile all relevant Scala sources for CI" + +runs: + using: "composite" + steps: + - run: | + source env.sh + make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND=test:compile + shell: bash diff --git a/.circleci/Dockerfile b/.github/scripts/Dockerfile similarity index 82% rename from .circleci/Dockerfile rename to .github/scripts/Dockerfile index 9515693a..35a30019 100644 --- a/.circleci/Dockerfile +++ b/.github/scripts/Dockerfile @@ -6,8 +6,8 @@ RUN yum -y install sudo epel-release RUN yum -y install python-pip # Match the version on the dev ami RUN pip2 install --upgrade pip==18.0 -# Provide a baseline of version for circle CI to use. -# If Circle CI uses its native version to initialize the repo, future submodule +# Provide a baseline of version for GH-A CI to use. +# If GH-A CI uses its native version to initialize the repo, future submodule # initializations with the machine-launch installed version of git produce very # non-intuitive results # (the chipyard submodule is initialized to an apparently random commit) diff --git a/.circleci/build-and-push-docker-image.sh b/.github/scripts/build-and-push-docker-image.sh similarity index 100% rename from .circleci/build-and-push-docker-image.sh rename to .github/scripts/build-and-push-docker-image.sh diff --git a/.github/scripts/build-default-workloads.py b/.github/scripts/build-default-workloads.py new file mode 100755 index 00000000..c3c8189a --- /dev/null +++ b/.github/scripts/build-default-workloads.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +from fabric.api import * + +from common import manager_fsim_dir, set_fabric_firesim_pem + +def build_default_workloads(): + """ Builds workloads that will be run on F1 instances as part of CI """ + + with prefix('cd {} && source ./env.sh'.format(manager_fsim_dir)), \ + prefix('cd deploy/workloads'): + + # avoid logging excessive amounts to prevent GH-A masking secrets (which slows down log output) + with settings(warn_only=True): + rc = run("marshal -v build br-base.json &> br-base.full.log").return_code + if rc != 0: + run("cat br-base.full.log") + raise Exception("Building br-base.json failed to run") + + run("make linux-poweroff") + run("make allpaper") + +if __name__ == "__main__": + set_fabric_firesim_pem() + execute(build_default_workloads, hosts=["localhost"]) diff --git a/.github/scripts/change-workflow-instance-states.py b/.github/scripts/change-workflow-instance-states.py new file mode 100755 index 00000000..792e58e7 --- /dev/null +++ b/.github/scripts/change-workflow-instance-states.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +# Changes the state of instances associated with the CI workflow run's unique tag. +# Can be used to start, stop, or terminate. This may run from either the manager +# or from the CI instance. + +import argparse + +from common import change_workflow_instance_states + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('tag_value', + help = "The tag used to identify workflow instances.") + parser.add_argument('state_change', + choices = ['terminate', 'stop', 'start'], + help = "The state transition to initiate on workflow instances.") + parser.add_argument('github_api_token', + help = "API token to modify self-hosted runner state.") + + args = parser.parse_args() + change_workflow_instance_states(args.github_api_token, args.tag_value, args.state_change) diff --git a/.circleci/ci_variables.py b/.github/scripts/ci_variables.py similarity index 55% rename from .circleci/ci_variables.py rename to .github/scripts/ci_variables.py index f8775da8..b6d5f65e 100644 --- a/.circleci/ci_variables.py +++ b/.github/scripts/ci_variables.py @@ -5,9 +5,9 @@ import os # CI instance environment variables # This is used as a unique tag for all instances launched in a workflow -ci_workflow_id = os.environ['CIRCLE_WORKFLOW_ID'] -ci_commit_sha1 = os.environ['CIRCLE_SHA1'] +ci_workflow_run_id = os.environ['GITHUB_RUN_ID'] +ci_commit_sha1 = os.environ['GITHUB_SHA'] # expanduser to replace the ~ present in the default, for portability -ci_workdir = os.path.expanduser(os.environ['CIRCLE_WORKING_DIRECTORY']) -ci_api_token = os.environ['CIRCLE_CI_API_TOKEN'] - +ci_workdir = os.path.expanduser(os.environ['GITHUB_WORKSPACE']) +ci_api_token = os.environ['GITHUB_TOKEN'] +ci_personal_api_token = os.environ['PERSONAL_ACCESS_TOKEN'] diff --git a/.circleci/common.py b/.github/scripts/common.py similarity index 63% rename from .circleci/common.py rename to .github/scripts/common.py index 4de96657..22e65808 100644 --- a/.circleci/common.py +++ b/.github/scripts/common.py @@ -1,21 +1,31 @@ import sys import boto3 +import os from fabric.api import * +import requests # Reuse manager utilities -from ci_variables import ci_workdir -sys.path.append(ci_workdir + "/deploy/awstools") +script_dir = os.path.dirname(os.path.realpath(__file__)) + "/../.." +sys.path.append(script_dir + "/deploy/awstools") from awstools import get_instances_with_filter # Remote paths manager_home_dir = "/home/centos" -manager_fsim_dir = "/home/centos/firesim" +manager_fsim_pem = manager_home_dir + "/firesim.pem" +manager_fsim_dir = manager_home_dir + "/firesim" manager_marshal_dir = manager_fsim_dir + "/target-design/chipyard/software/firemarshal" -manager_ci_dir = manager_fsim_dir + "/.circleci" +manager_ci_dir = manager_fsim_dir + "/.github/scripts" # Common fabric settings env.output_prefix = False env.abort_on_prompts = True +env.timeout = 100 +env.connection_attempts = 10 +env.disable_known_hosts = True +env.keepalive = 60 # keep long SSH connections running + +def set_fabric_firesim_pem(): + env.key_filename = manager_fsim_pem # This tag is common to all instances launched as part of a given workflow unique_tag_key = 'ci-workflow-id' @@ -35,7 +45,7 @@ def get_manager_tag_dict(sha, tag_value): unique_tag_key: tag_value} def get_manager_instance(tag_value): - """ Looks up the manager instance dict using the CI run's unique tag""" + """ Looks up the manager instance dict using the CI workflow run's unique tag""" instances = get_instances_with_filter([get_ci_filter(tag_value), manager_filter]) if instances: assert len(instances) == 1 @@ -44,7 +54,7 @@ def get_manager_instance(tag_value): return None def get_manager_instance_id(tag_value): - """ Looks up the manager instance ID using the CI run's unique tag""" + """ Looks up the manager instance ID using the CI workflow run's unique tag""" manager = get_manager_instance(tag_value) if manager is None: @@ -54,7 +64,7 @@ def get_manager_instance_id(tag_value): return manager['InstanceId'] def get_manager_ip(tag_value): - """ Looks up the manager IP using the CI run's unique tag""" + """ Looks up the manager IP using the CI workflow run's unique tag""" manager = get_manager_instance(tag_value) if manager is None: @@ -67,7 +77,7 @@ def manager_hostname(tag_value): return "centos@{}".format(get_manager_ip(tag_value)) def get_all_workflow_instances(tag_value): - """ Grabs a list of all instance dicts sharing the CI run's unique tag """ + """ Grabs a list of all instance dicts sharing the CI workflow run's unique tag """ return get_instances_with_filter([get_ci_filter(tag_value)]) def instance_metadata_str(instance): @@ -83,9 +93,26 @@ def instance_metadata_str(instance): return static_md + dynamic_md +def deregister_runner_if_exists(gh_token, runner_name): + headers = {'Authorization': "token {}".format(gh_token.strip())} -def change_workflow_instance_states(tag_value, state_change, dryrun=False): - """ Change the state of all instances sharing the same CI run's tag. """ + # Check if exists before deregistering + r = requests.get("https://api.github.com/repos/firesim/firesim/actions/runners", headers=headers) + if r.status_code != 200: + # if couldn't delete then just exit + return + + res_dict = r.json() + runner_list = res_dict["runners"] + for runner in runner_list: + if runner_name in runner["name"]: + r = requests.delete("https://api.github.com/repos/firesim/firesim/actions/runners/{}".format(runner["id"]), headers=headers) + if r.status_code != 204: + # if couldn't delete then just exit + return + +def change_workflow_instance_states(gh_token, tag_value, state_change, dryrun=False): + """ Change the state of all instances sharing the same CI workflow run's tag. """ all_instances = get_all_workflow_instances(tag_value) manager_instance = get_manager_instance(tag_value) @@ -100,6 +127,7 @@ def change_workflow_instance_states(tag_value, state_change, dryrun=False): client = boto3.client('ec2') if state_change == 'stop': print("Stopping instances: {}".format(", ".join(instance_ids))) + deregister_runner_if_exists(gh_token, tag_value) client.stop_instances(InstanceIds=instance_ids, DryRun=dryrun) elif state_change == 'start': print("Starting instances: {}".format(", ".join(instance_ids))) @@ -116,15 +144,16 @@ def change_workflow_instance_states(tag_value, state_change, dryrun=False): elif state_change == 'terminate': print("Terminating instances: {}".format(", ".join(instance_ids))) + deregister_runner_if_exists(gh_token, tag_value) client.terminate_instances(InstanceIds=instance_ids, DryRun=dryrun) else: raise ValueError("Unrecognized transition type: {}".format(state_change)) -def terminate_workflow_instances(tag_value, dryrun=False): - change_workflow_instance_states(tag_value, "terminate", dryrun) +def terminate_workflow_instances(gh_token, tag_value, dryrun=False): + change_workflow_instance_states(gh_token, tag_value, "terminate", dryrun) -def stop_workflow_instances(tag_value, dryrun=False): - change_workflow_instance_states(tag_value, "stop", dryrun) +def stop_workflow_instances(gh_token, tag_value, dryrun=False): + change_workflow_instance_states(gh_token, tag_value, "stop", dryrun) -def start_workflow_instances(tag_value, dryrun=False): - change_workflow_instance_states(tag_value, "start", dryrun) +def start_workflow_instances(gh_token, tag_value, dryrun=False): + change_workflow_instance_states(gh_token, tag_value, "start", dryrun) diff --git a/.circleci/cull-old-ci-instances.py b/.github/scripts/cull-old-ci-instances.py similarity index 76% rename from .circleci/cull-old-ci-instances.py rename to .github/scripts/cull-old-ci-instances.py index 509231df..f308bd2e 100755 --- a/.circleci/cull-old-ci-instances.py +++ b/.github/scripts/cull-old-ci-instances.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Runs periodically in it's own workflow in the CI/CD environment to teardown # instances that have exceeded a lifetime limit @@ -8,10 +8,10 @@ import pytz import boto3 import sys -from common import unique_tag_key +from common import unique_tag_key, deregister_runner_if_exists # Reuse manager utilities -from ci_variables import ci_workdir +from ci_variables import ci_workdir, ci_personal_api_token, ci_workflow_run_id sys.path.append(ci_workdir + "/deploy/awstools") from awstools import get_instances_with_filter @@ -27,12 +27,13 @@ def main(): all_ci_instances = get_instances_with_filter([all_ci_instances_filter], allowed_states=['*']) client = boto3.client('ec2') - print "Terminated Instances:" + print("Terminated Instances:") for inst in all_ci_instances: lifetime_secs = (current_time - inst["LaunchTime"]).total_seconds() if lifetime_secs > (INSTANCE_LIFETIME_LIMIT_HOURS * 3600): + deregister_runner_if_exists(ci_personal_api_token, ci_workflow_run_id): client.terminate_instances(InstanceIds=[inst["InstanceId"]]) - print " " + inst["InstanceId"] + print(" " + inst["InstanceId"]) if __name__ == "__main__": main() diff --git a/.circleci/firesim-managerinit.expect b/.github/scripts/firesim-managerinit.expect similarity index 100% rename from .circleci/firesim-managerinit.expect rename to .github/scripts/firesim-managerinit.expect diff --git a/.github/scripts/gh-a-runner.expect b/.github/scripts/gh-a-runner.expect new file mode 100755 index 00000000..c0a87ed0 --- /dev/null +++ b/.github/scripts/gh-a-runner.expect @@ -0,0 +1,16 @@ +#! /usr/bin/env expect + +set timeout -1 + +set token [lindex $argv 0] +set runner_name [lindex $argv 1] +set unique_label [lindex $argv 2] + +spawn ./config.sh --url https://github.com/firesim/firesim --token $token + +send -- "\r" +send -- "$runner_name\r" +send -- "$unique_label\r" +send -- "\r" + +expect eof diff --git a/.circleci/initialize-manager.py b/.github/scripts/initialize-manager.py similarity index 56% rename from .circleci/initialize-manager.py rename to .github/scripts/initialize-manager.py index a181e984..c1d55e4e 100755 --- a/.circleci/initialize-manager.py +++ b/.github/scripts/initialize-manager.py @@ -1,6 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import traceback +import time from fabric.api import * @@ -12,20 +13,24 @@ def initialize_manager(max_runtime): """ Performs the prerequisite tasks for all CI jobs that will run on the manager instance max_runtime (hours): The maximum uptime this manager and its associated - instances should have before it is stopped. This serves as a redundant check + instances should have before it is stopped. This serves as a redundant check in case the workflow-monitor is brought down for some reason. """ # Catch any exception that occurs so that we can gracefully teardown try: - put(ci_workdir + "/scripts/machine-launch-script.sh", manager_home_dir) + # wait until machine launch is complete with cd(manager_home_dir): - run("chmod +x machine-launch-script.sh") - run("sudo ./machine-launch-script.sh") - run("git clone https://github.com/firesim/firesim.git") + # add firesim.pem + with open(manager_fsim_pem, "w") as pem_file: + pem_file.write(os.environ["FIRESIM_PEM"]) + local("chmod 600 {}".format(manager_fsim_pem)) + set_fabric_firesim_pem() + + # copy ci version of the repo into the new globally accessible location + run("git clone {} {}".format(ci_workdir, manager_fsim_dir)) with cd(manager_fsim_dir): - run("git checkout " + ci_commit_sha1) run("./build-setup.sh --fast") # Initialize marshal submodules early because it appears some form of @@ -36,27 +41,27 @@ def initialize_manager(max_runtime): run("./init-submodules.sh") with cd(manager_fsim_dir), prefix("source ./sourceme-f1-manager.sh"): - run(".circleci/firesim-managerinit.expect {} {} {}".format( - os.environ["AWS_ACCESS_KEY_ID"], - os.environ["AWS_SECRET_ACCESS_KEY"], - os.environ["AWS_DEFAULT_REGION"])) + run(".github/scripts/firesim-managerinit.expect {} {} {}".format( + os.environ["AWS-ACCESS-KEY-ID"], + os.environ["AWS-SECRET-ACCESS-KEY"], + os.environ["AWS-DEFAULT-REGION"])) with cd(manager_ci_dir): # Put a baseline time-to-live bound on the manager. - # Instances will be stopped and cleaned up in a nightly job. + # Instances will be terminated (since they are spot requests) and cleaned up in a nightly job. # Setting pty=False is required to stop the screen from being - # culled when the SSH session associated with teh run command ends. - run("screen -S ttl -dm bash -c \'sleep {}; ./change-workflow-instance-states.py {} stop\'" - .format(int(max_runtime) * 3600, ci_workflow_id), pty=False) + # culled when the SSH session associated with the run command ends. + run("screen -S ttl -dm bash -c \'sleep {}; ./change-workflow-instance-states.py {} terminate {}\'" + .format(int(max_runtime) * 3600, ci_workflow_run_id, ci_personal_api_token), pty=False) run("screen -S workflow-monitor -L -dm ./workflow-monitor.py {} {}" - .format(ci_workflow_id, ci_api_token), pty=False) + .format(ci_workflow_run_id, ci_personal_api_token), pty=False) except BaseException as e: traceback.print_exc(file=sys.stdout) - terminate_workflow_instances(ci_workflow_id) + terminate_workflow_instances(ci_personal_api_token, ci_workflow_run_id) sys.exit(1) if __name__ == "__main__": max_runtime = sys.argv[1] - execute(initialize_manager, max_runtime, hosts=[manager_hostname(ci_workflow_id)]) + execute(initialize_manager, max_runtime, hosts=["localhost"]) diff --git a/.circleci/launch-manager-instance.py b/.github/scripts/launch-manager-instance.py similarity index 79% rename from .circleci/launch-manager-instance.py rename to .github/scripts/launch-manager-instance.py index 727e46c1..ab4337ef 100755 --- a/.circleci/launch-manager-instance.py +++ b/.github/scripts/launch-manager-instance.py @@ -1,11 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Used to launch a fresh manager instance from the CI container. import sys -from fabric.api import * - # This must run in the CI container from ci_variables import * from common import * @@ -16,7 +14,7 @@ import awstools def main(): """ Spins up a new manager instance for our CI run """ - manager_instance = get_manager_instance(ci_workflow_id) + manager_instance = get_manager_instance(ci_workflow_run_id) if manager_instance is not None: print("There is an existing manager instance for this CI workflow:") print(instance_metadata_str(manager_instance)) @@ -27,11 +25,13 @@ def main(): 'launch', '--inst_type', 'z1d.2xlarge', '--block_devices', str([{'DeviceName':'/dev/sda1','Ebs':{'VolumeSize':300,'VolumeType':'gp2'}}]), - '--tags', str(get_manager_tag_dict(ci_commit_sha1, ci_workflow_id))]) - manager_instance = get_manager_instance(ci_workflow_id) + '--tags', str(get_manager_tag_dict(ci_commit_sha1, ci_workflow_run_id)), + '--user_data_file', ci_workdir + "/scripts/machine-launch-script.sh" + ]) + manager_instance = get_manager_instance(ci_workflow_run_id) print("Instance ready.") - print(instance_metadata_str(get_manager_instance(ci_workflow_id))) + print(instance_metadata_str(get_manager_instance(ci_workflow_run_id))) sys.stdout.flush() if __name__ == "__main__": diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt new file mode 100644 index 00000000..a554bc4f --- /dev/null +++ b/.github/scripts/requirements.txt @@ -0,0 +1,5 @@ +fab-classic==1.19.1 +boto3==1.20.21 +pytz +pyyaml +requests diff --git a/.github/scripts/run-ini-api-tests.py b/.github/scripts/run-ini-api-tests.py new file mode 100755 index 00000000..e000b859 --- /dev/null +++ b/.github/scripts/run-ini-api-tests.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +from fabric.api import * + +from common import manager_fsim_dir, manager_ci_dir, manager_fsim_pem, set_fabric_firesim_pem + +import sys + +def run_build_recipes_ini_api_tests(): + """ Test config_{build, build_recipes}.ini APIs """ + + def commands_to_run(commands, opts): + """ Run a list of commands with the specified opts """ + for command in commands: + with prefix('cd {} && source sourceme-f1-manager.sh'.format(manager_fsim_dir)): + rc = 0 + with settings(warn_only=True): + rc = run("{} {}".format(command, opts)).return_code + if rc == 0: + print("{} passed unexpectedly.".format(command)) + + # exit since passing is not wanted + sys.exit(1) + + def run_test(name): + # test files should exist on the manager already + test_dir = "{}/ini-tests/failing-buildafi-files/{}".format(manager_ci_dir, name) + + commands_to_run( + ["firesim buildafi"], + "-b {}/sample_config_build.ini -r {}/sample_config_build_recipes.ini".format(test_dir, test_dir)) + + run_test("invalid-build-section") + run_test("invalid-recipe-inst-type") + + # test invalid config_build.ini + commands_to_run(["firesim buildafi"], "-b ~/GHOST_FILE") + + # test invalid config_build_recipes.ini + commands_to_run(["firesim buildafi"], "-r ~/GHOST_FILE") + +def run_runtime_hwdb_ini_api_tests(): + """ Test config_{runtime, hwdb}.ini APIs """ + + def commands_to_run(commands, opts): + """ Run a list of commands with the specified opts """ + for command in commands: + with prefix('cd {} && source sourceme-f1-manager.sh'.format(manager_fsim_dir)): + rc = 0 + with settings(warn_only=True): + rc = run("{} {}".format(command, opts)).return_code + if rc == 0: + print("{} passed unexpectedly.".format(command)) + + # test passed so make sure to terminate runfarm + run("firesim terminaterunfarm -q {}".format(opts)) + + # exit since passing is not wanted + sys.exit(1) + + def run_test(name): + # test files should exist on the manager already + test_dir = "{}/ini-tests/failing-runtime-files/{}".format(manager_ci_dir, name) + + commands_to_run( + ["firesim launchrunfarm", "firesim infrasetup", "firesim runworkload", "firesim terminaterunfarm -q"], + "-c {}/sample_config_runtime.ini -a {}/sample_config_hwdb.ini".format(test_dir, test_dir)) + + run_test("hwdb-invalid-afi") + run_test("runtime-invalid-hwconfig") + run_test("runtime-invalid-topology") + run_test("runtime-invalid-workloadname") + + # test invalid config_runtime.ini + commands_to_run(["firesim launchrunfarm", "firesim infrasetup", "firesim runworkload", "firesim terminaterunfarm -q"], "-c ~/GHOST_FILE") + + # test invalid config_hwdb.ini + commands_to_run(["firesim launchrunfarm", "firesim infrasetup", "firesim runworkload", "firesim terminaterunfarm -q"], "-a ~/GHOST_FILE") + +def run_ini_api_tests(): + """ Test manager .ini file APIs """ + + run_build_recipes_ini_api_tests() + run_runtime_hwdb_ini_api_tests() + +if __name__ == "__main__": + set_fabric_firesim_pem() + execute(run_ini_api_tests, hosts=["localhost"]) diff --git a/.github/scripts/run-linux-poweroff.py b/.github/scripts/run-linux-poweroff.py new file mode 100755 index 00000000..764fee86 --- /dev/null +++ b/.github/scripts/run-linux-poweroff.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import sys + +from fabric.api import * + +from common import manager_fsim_dir, set_fabric_firesim_pem + +def run_linux_poweroff(): + """ Runs Linux poweroff workloads """ + + with prefix('cd {} && source sourceme-f1-manager.sh'.format(manager_fsim_dir)): + run("cd sw/firesim-software && ./marshal -v build br-base.json && ./marshal -v install br-base.json") + run("cd deploy/workloads/ && make linux-poweroff") + + def run_w_timeout(workload, timeout): + """ Run workload with a specific timeout + + :arg: workload (str) - workload ini (abs path) + :arg: timeout (str) - timeout amount for the workload to run + """ + rc = 0 + with settings(warn_only=True): + # avoid logging excessive amounts to prevent GH-A masking secrets (which slows down log output) + # pty=False needed to avoid issues with screen -ls stalling in fabric + rc = run("timeout {} ./deploy/workloads/run-workload.sh {} --withlaunch &> {}.log".format(timeout, workload, workload), pty=False).return_code + if rc != 0: + # need to confirm that instance is off + print("Workload {} failed. Printing last lines of log. See {}.log for full info".format(workload, workload)) + print("Log start:") + run("tail -n 100 {}.log".format(workload)) + print("Log end.") + print("Terminating workload") + run("firesim terminaterunfarm -q -c {}".format(workload)) + sys.exit(rc) + else: + print("Workload {} successful.".format(workload)) + + run_w_timeout("{}/deploy/workloads/linux-poweroff-all-no-nic.ini".format(manager_fsim_dir), "30m") + run_w_timeout("{}/deploy/workloads/linux-poweroff-nic.ini".format(manager_fsim_dir), "30m") + +if __name__ == "__main__": + set_fabric_firesim_pem() + execute(run_linux_poweroff, hosts=["localhost"]) diff --git a/.github/scripts/run-manager-pytests.py b/.github/scripts/run-manager-pytests.py new file mode 100755 index 00000000..f8af50e9 --- /dev/null +++ b/.github/scripts/run-manager-pytests.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +from fabric.api import * + +from common import manager_fsim_dir, set_fabric_firesim_pem + +def run_manager_pytests(): + """ Runs all manager pytests """ + + with cd(manager_fsim_dir), prefix('source env.sh'): + run("cd deploy && python3 -m pytest") + +if __name__ == "__main__": + set_fabric_firesim_pem() + execute(run_manager_pytests, hosts=["localhost"]) diff --git a/.circleci/run-sbt-command.py b/.github/scripts/run-sbt-command.py similarity index 66% rename from .circleci/run-sbt-command.py rename to .github/scripts/run-sbt-command.py index 76cbc396..a4e6a482 100755 --- a/.circleci/run-sbt-command.py +++ b/.github/scripts/run-sbt-command.py @@ -1,11 +1,11 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys from fabric.api import * -from common import manager_fsim_dir, manager_hostname -from ci_variables import ci_workflow_id +from common import manager_fsim_dir, manager_hostname, set_fabric_firesim_pem +from ci_variables import ci_workflow_run_id def run_sbt_command(target_project, command): """ Runs a command in SBT shell for the default project specified by the target_project makefrag @@ -19,4 +19,5 @@ def run_sbt_command(target_project, command): run("make -C sim sbt SBT_COMMAND={} TARGET_PROJECT={}".format(command, target_project)) if __name__ == "__main__": - execute(run_sbt_command, sys.argv[1], sys.argv[2], hosts=[manager_hostname(ci_workflow_id)]) + set_fabric_firesim_pem() + execute(run_sbt_command, sys.argv[1], sys.argv[2], hosts=["localhost"]) diff --git a/.github/scripts/run-scala-test.py b/.github/scripts/run-scala-test.py new file mode 100755 index 00000000..b34c9e67 --- /dev/null +++ b/.github/scripts/run-scala-test.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import sys + +from fabric.api import * + +from common import manager_fsim_dir, set_fabric_firesim_pem + +def run_scala_test(target_project, test_name): + """ Runs a scala test under the desired target project + + target_project -- The make variable to select the desired target project makefrag + + test_name -- the full classname of the test + """ + with cd(manager_fsim_dir), prefix('source env.sh'): + # avoid logging excessive amounts to prevent GH-A masking secrets (which slows down log output) + with settings(warn_only=True): + rc = run("make -C sim testOnly TARGET_PROJECT={} SCALA_TEST={} &> scala-test.full.log".format(target_project, test_name)).return_code + if rc != 0: + run("cat scala-test.full.log") + raise Exception("Running scala test failed") + +if __name__ == "__main__": + set_fabric_firesim_pem() + execute(run_scala_test, sys.argv[1], sys.argv[2], hosts = ["localhost"]) diff --git a/.github/scripts/setup-manager-self-hosted.py b/.github/scripts/setup-manager-self-hosted.py new file mode 100755 index 00000000..43fa6613 --- /dev/null +++ b/.github/scripts/setup-manager-self-hosted.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +import traceback +import time +import requests +import sys + +from fabric.api import * + +from common import * +# This is expected to be launch from the ci container +from ci_variables import * + +def initialize_manager_hosted(): + """ Performs the prerequisite tasks for all CI jobs that will run on the manager instance + + max_runtime (hours): The maximum uptime this manager and its associated + instances should have before it is stopped. This serves as a redundant check + in case the workflow-monitor is brought down for some reason. + """ + + # Catch any exception that occurs so that we can gracefully teardown + try: + # wait until machine launch is complete + with cd(manager_home_dir): + with settings(warn_only=True): + rc = run("timeout 10m grep -q '.*machine launch script complete.*' <(tail -f machine-launchstatus)").return_code + if rc != 0: + run("cat machine-launchstatus.log") + raise Exception("machine-launch-script.sh failed to run") + + # get the runner version based off the latest tag on the github runner repo + RUNNER_VERSION = local("git ls-remote --refs --tags https://github.com/actions/runner.git | cut --delimiter='/' --fields=3 | tr '-' '~' | sort --version-sort | tail --lines=1", capture=True) + RUNNER_VERSION = RUNNER_VERSION.replace("v", "") + print("Using Github Actions Runner v{}".format(RUNNER_VERSION)) + # create NUM_RUNNER self-hosted runners on the manager that run in parallel + NUM_RUNNERS = 4 + for runner_idx in range(NUM_RUNNERS): + actions_dir = "{}/actions-runner-{}".format(manager_home_dir, runner_idx) + run("mkdir -p {}".format(actions_dir)) + with cd(actions_dir): + run("curl -o actions-runner-linux-x64-{}.tar.gz -L https://github.com/actions/runner/releases/download/v{}/actions-runner-linux-x64-{}.tar.gz".format(RUNNER_VERSION, RUNNER_VERSION, RUNNER_VERSION)) + run("tar xzf ./actions-runner-linux-x64-{}.tar.gz".format(RUNNER_VERSION)) + + # install deps + run("sudo ./bin/installdependencies.sh") + + # get registration token from API + headers = {'Authorization': "token {}".format(ci_personal_api_token.strip())} + r = requests.post("https://api.github.com/repos/firesim/firesim/actions/runners/registration-token", headers=headers) + if r.status_code != 201: + raise Exception("HTTPS error: {} {}. Retrying.".format(r.status_code, r.json())) + + res_dict = r.json() + reg_token = res_dict["token"] + + # config runner + put(".github/scripts/gh-a-runner.expect", actions_dir) + run("chmod +x gh-a-runner.expect") + runner_name = "{}-{}".format(ci_workflow_run_id, runner_idx) # used to teardown runner + unique_label = ci_workflow_run_id # used within the yaml to choose a runner + run("./gh-a-runner.expect {} {} {}".format(reg_token, runner_name, unique_label)) + + # start runner + # Setting pty=False is required to stop the screen from being + # culled when the SSH session associated with the run command ends. + run("screen -S gh-a-runner-{} -L -dm ./run.sh".format(runner_idx), pty=False) + + # double check that screen is setup properly + with settings(warn_only=True): + rc = run("screen -ls | grep \"gh-a-runner-{}\"".format(runner_idx)).return_code + if rc != 0: + run("cat screenlog.*") + raise Exception("There was an issue with setting up Github Actions runner {}".format(runner_idx)) + + except BaseException as e: + traceback.print_exc(file=sys.stdout) + terminate_workflow_instances(ci_personal_api_token, ci_workflow_run_id) + sys.exit(1) + +if __name__ == "__main__": + execute(initialize_manager_hosted, hosts=[manager_hostname(ci_workflow_run_id)]) diff --git a/.github/scripts/workflow-monitor.py b/.github/scripts/workflow-monitor.py new file mode 100755 index 00000000..5aaad103 --- /dev/null +++ b/.github/scripts/workflow-monitor.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +# Runs in the background on a manager instance to determine when it can be torn +# down by polling the workflow's state using GitHub Actions's RESTful api. +# +# Site: https://docs.github.com/en/rest/reference/actions#get-a-workflow-run +# +# Terminate instances if: +# - the workflow is successful (all jobs complete successfully) +# - the workflow is cancelled +# Stop instances if: +# - the workflow has failed (all jobs have completed, but at least one has failed) +# +# Other states to consider: not_run, on_hold, unauthorized + +import time +import sys +import requests + +from common import terminate_workflow_instances, stop_workflow_instances + +# Time between HTTPS requests to github +POLLING_INTERVAL_SECONDS = 60 +# Number of failed requests before stopping the instances +QUERY_FAILURE_THRESHOLD = 10 + +# We should never get to 'not_run' or 'unauthorized' but terminate for good measure +TERMINATE_STATES = ["cancelled", "success", "skipped", "stale"] +STOP_STATES = ["failure", "timed_out"] +NOP_STATES = ["action_required"] # TODO: unsure when this happens + +def main(workflow_id, gha_ci_personal_token): + + state = None + consecutive_failures = 0 + headers = {'Authorization': "token {}".format(gha_ci_personal_token.strip())} + + while True: + time.sleep(POLLING_INTERVAL_SECONDS) + + res = requests.get("https://api.github.com/repos/firesim/firesim/actions/runs/{}".format(workflow_id), headers=headers) + if res.status_code == 200: + consecutive_failures = 0 + res_dict = res.json() + state_status = res_dict["status"] + state_concl = res_dict["conclusion"] + + print("Workflow {} status: {} {}".format(workflow_id, state_status, state_concl)) + if state_status in ['completed']: + if state_concl in TERMINATE_STATES: + terminate_workflow_instances(gha_ci_personal_token, workflow_id) + exit(0) + elif state_concl in STOP_STATES: + stop_workflow_instances(gha_ci_personal_token, workflow_id) + exit(0) + elif state_concl not in NOP_STATES: + print("Unexpected Workflow State On Completed: {}".format(state_concl)) + raise ValueError + elif state_status not in ['in_progress', 'queued', 'waiting', 'requested']: + print("Unexpected Workflow State: {}".format(state_status)) + raise ValueError + + else: + print("HTTP GET error: {}. Retrying.".format(res.json())) + consecutive_failures = consecutive_failures + 1 + if consecutive_failures == QUERY_FAILURE_THRESHOLD: + stop_workflow_instances(gha_ci_personal_token, workflow_id) + exit(1) + +if __name__ == "__main__": + main(sys.argv[1], sys.argv[2]) diff --git a/.github/workflows/firesim-cull-instances.yml b/.github/workflows/firesim-cull-instances.yml new file mode 100644 index 00000000..f87d66f2 --- /dev/null +++ b/.github/workflows/firesim-cull-instances.yml @@ -0,0 +1,26 @@ +name: firesim-cull-instances + +on: + schedule: + - cron: "0 0,12 * * *" + +env: + PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_A_PERSONAL_ACCESS_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + AWS-ACCESS-KEY-ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }} + FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }} + +jobs: + cull-old-ci-instances: + name: cull-old-ci-instances + runs-on: ubuntu-latest + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + with: + ref: dev + - uses: ./.github/actions/repo-setup-aws + - run: .github/scripts/cull-old-ci-instances.py diff --git a/.github/workflows/firesim-publish-scala-doc.yml b/.github/workflows/firesim-publish-scala-doc.yml new file mode 100644 index 00000000..99959a37 --- /dev/null +++ b/.github/workflows/firesim-publish-scala-doc.yml @@ -0,0 +1,33 @@ +name: firesim-publish-scala-doc + +on: + push: + branches: + - dev + tags: + - '[0-9]*.[0-9]*.[0-9]*' + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + AWS-ACCESS-KEY-ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }} + FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }} + LANG: "en_US.UTF-8" # required by SBT when it sees boost directories + LANGUAGE: "en_US:en" + LC_ALL: "en_US.UTF-8" + +jobs: + publish-scala-doc: + name: publish-scala-doc + runs-on: ubuntu-18.04 + container: + image: firesim/firesim-ci:v1.1 + options: --entrypoint /bin/bash + env: + JVM_MEMORY: 3500M # Default JVM maximum heap limit + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/repo-setup + - uses: ./.github/actions/build-scala-doc + - uses: ./.github/actions/push-scaladoc-to-ghpages diff --git a/.github/workflows/firesim-run-tests.yml b/.github/workflows/firesim-run-tests.yml new file mode 100644 index 00000000..b8c284d5 --- /dev/null +++ b/.github/workflows/firesim-run-tests.yml @@ -0,0 +1,204 @@ +name: firesim-ci-process + +on: + # run ci when pring to dev/master/main (note: ci runs on the merge commit of the pr!) + pull_request: + branches: + - dev + - master + - main + +env: + PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_A_PERSONAL_ACCESS_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + AWS-ACCESS-KEY-ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }} + FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }} + LANG: "en_US.UTF-8" # required by SBT when it sees boost directories + LANGUAGE: "en_US:en" + LC_ALL: "en_US.UTF-8" + +jobs: + cancel-prior-workflows: + name: cancel-prior-workflows + runs-on: ubuntu-18.04 + steps: + - name: Cancel previous workflow runs + uses: styfle/cancel-workflow-action@0.9.1 + with: + access_token: ${{ github.token }} + + setup-self-hosted-manager: + name: setup-self-hosted-manager + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Install Python CI requirements + uses: ./.github/actions/repo-setup-aws + if: steps.job-start.outputs.run_result != 'success' + - name: Launch AWS instance used for the FireSim manager (instance info found here) + run: ./.github/scripts/launch-manager-instance.py + if: steps.job-start.outputs.run_result != 'success' + - name: Setup N Github Actions Runners on AWS instance + run: ./.github/scripts/setup-manager-self-hosted.py + if: steps.job-start.outputs.run_result != 'success' + - uses: ./.github/actions/job-end + + setup-manager: + name: setup-manager + needs: [setup-self-hosted-manager] + runs-on: ${{ github.run_id }} + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Setup FireSim repo (.pem, build-setup.sh, AWS credentials, submodules) and CI daemons + uses: ./.github/actions/initialize-manager + if: steps.job-start.outputs.run_result != 'success' + with: + max-runtime-hours: 10 + - name: Initial Scala compilation + uses: ./.github/actions/initial-scala-compile + if: steps.job-start.outputs.run_result != 'success' + - uses: ./.github/actions/job-end + + build-default-workloads: + name: build-default-workloads + needs: [setup-manager] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Build default workloads (FireMarshal and paper workloads) + run: .github/scripts/build-default-workloads.py + if: steps.job-start.outputs.run_result != 'success' + - uses: ./.github/actions/job-end + + run-manager-pytests: + name: run-manager-pytests + needs: [setup-manager] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Run pytests + run: .github/scripts/run-manager-pytests.py + if: steps.job-start.outputs.run_result != 'success' + - uses: ./.github/actions/job-end + + run-test-groupA: + name: run-test-groupA + needs: [setup-manager] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Run CIGroupA Scala tests + uses: ./.github/actions/run-scala-test + if: steps.job-start.outputs.run_result != 'success' + with: + test-name: "CIGroupA" + - uses: ./.github/actions/job-end + + run-test-groupB: + name: run-test-groupB + needs: [run-test-groupA] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Run CIGroupB Scala tests + uses: ./.github/actions/run-scala-test + if: steps.job-start.outputs.run_result != 'success' + with: + test-name: "CIGroupB" + - uses: ./.github/actions/job-end + + run-chipyard-tests: + name: run-chipyard-tests + needs: [run-test-groupB] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Run other (CITests) Scala tests + uses: ./.github/actions/run-scala-test + if: steps.job-start.outputs.run_result != 'success' + with: + target-project: "firesim" + test-package: "firesim.firesim" + test-name: "CITests" + - uses: ./.github/actions/job-end + + run-basic-linux-poweroff: + name: run-basic-linux-poweroff + needs: [build-default-workloads] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + environment: use-fpgas + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Run linux-poweroff test + run: .github/scripts/run-linux-poweroff.py + if: steps.job-start.outputs.run_result != 'success' + - uses: ./.github/actions/job-end + + run-ini-api-tests: + name: run-ini-api-tests + needs: [setup-manager] + runs-on: ${{ github.run_id }} + env: + TERM: xterm-256-color + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Run .ini API verification tests + run: .github/scripts/run-ini-api-tests.py + if: steps.job-start.outputs.run_result != 'success' + - uses: ./.github/actions/job-end + + documentation-check: + name: documentation-check + runs-on: ubuntu-18.04 + container: + image: firesim/firesim-ci:v1.1 + options: --entrypoint /bin/bash + env: + JVM_MEMORY: 3500M # Default JVM maximum heap limit + steps: + - uses: actions/checkout@v2 + - uses: ./.github/actions/job-start + id: job-start + - name: Check that documentation builds with no warnings/errors + if: steps.job-start.outputs.run_result != 'success' + run: | + sudo yum update -y + sudo yum install -y python3-pip make + sudo pip3 install -r docs/requirements.txt + make -C docs html + - name: Show error log from sphinx if failed + if: ${{ steps.job-start.outputs.run_result != 'success' && failure() }} + run: cat /tmp/sphinx-err*.log + - uses: ./.github/actions/job-end diff --git a/README.md b/README.md index 57dc2354..893e4d51 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # FireSim: Easy-to-use, Scalable, FPGA-accelerated Cycle-accurate Hardware Simulation ![FireSim Documentation Status](https://readthedocs.org/projects/firesim/badge/) -[![firesim](https://circleci.com/gh/firesim/firesim.svg?style=shield)](https://app.circleci.com/pipelines/github/firesim/firesim) +![Github Actions Status](https://github.com/firesim/firesim/actions/workflows/firesim-run-tests.yml/badge.svg) ## Contents @@ -15,7 +15,7 @@ ## Using FireSim To get started with using FireSim, see the tutorials on the FireSim documentation -site: https://docs.fires.im/. +site: https://docs.fires.im/. Another good overview (in video format) is our tutorial from the Chisel Community Conference on [YouTube](https://www.youtube.com/watch?v=S3OriQnJXYQ). diff --git a/deploy/awstools/awstools.py b/deploy/awstools/awstools.py old mode 100644 new mode 100755 index 36f6a2dd..f4c69167 --- a/deploy/awstools/awstools.py +++ b/deploy/awstools/awstools.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + from __future__ import print_function import random @@ -151,7 +153,7 @@ def construct_instance_market_options(instancemarket, spotinterruptionbehavior, else: assert False, "INVALID INSTANCE MARKET TYPE." -def launch_instances(instancetype, count, instancemarket, spotinterruptionbehavior, spotmaxprice, blockdevices=None, tags=None, randomsubnet=False): +def launch_instances(instancetype, count, instancemarket, spotinterruptionbehavior, spotmaxprice, blockdevices=None, tags=None, randomsubnet=False, user_data_file=None): """ Launch count instances of type instancetype, optionally with additional block devices mappings and instance tags @@ -192,30 +194,37 @@ def launch_instances(instancetype, count, instancemarket, spotinterruptionbehavi chosensubnet = subnets[startsubnet].subnet_id try: - instance = ec2.create_instances(ImageId=f1_image_id, - EbsOptimized=True, - BlockDeviceMappings=(blockdevices + [ - { - 'DeviceName': '/dev/sdb', - 'NoDevice': '', - }, - ]), - InstanceType=instancetype, MinCount=1, MaxCount=1, - NetworkInterfaces=[ - {'SubnetId': chosensubnet, - 'DeviceIndex':0, - 'AssociatePublicIpAddress':True, - 'Groups':[firesimsecuritygroup]} - ], - KeyName=keyname, - TagSpecifications=([] if tags is None else [ - { - 'ResourceType': 'instance', - 'Tags': [{ 'Key': k, 'Value': v} for k, v in tags.items()], - }, - ]), - InstanceMarketOptions=marketconfig - ) + instance_args = {"ImageId":f1_image_id, + "EbsOptimized":True, + "BlockDeviceMappings":(blockdevices + [ + { + 'DeviceName': '/dev/sdb', + 'NoDevice': '', + }, + ]), + "InstanceType":instancetype, + "MinCount":1, + "MaxCount":1, + "NetworkInterfaces":[ + {'SubnetId': chosensubnet, + 'DeviceIndex':0, + 'AssociatePublicIpAddress':True, + 'Groups':[firesimsecuritygroup]} + ], + "KeyName":keyname, + "TagSpecifications":([] if tags is None else [ + { + 'ResourceType': 'instance', + 'Tags': [{ 'Key': k, 'Value': v} for k, v in tags.items()], + }, + ]), + "InstanceMarketOptions":marketconfig, + } + if user_data_file is not None: + with open(user_data_file, "r") as f: + instance_args["UserData"] = ''.join(f.readlines()) + + instance = ec2.create_instances(**instance_args) instances += instance except client.exceptions.ClientError as e: @@ -472,6 +481,7 @@ def main(args): parser.add_argument("--block_devices", type=yaml.safe_load, default=run_block_device_dict(), help="List of dicts with block device information. Used by \'launch\'.") parser.add_argument("--tags", type=yaml.safe_load, default=run_tag_dict(), help="Dict of tags to add to instances. Used by \'launch\'.") parser.add_argument("--filters", type=yaml.safe_load, default=run_filters_list_dict(), help="List of dicts used to filter instances. Used by \'terminate\'.") + parser.add_argument("--user_data_file", default=None, help="File path to use as user data (run on initialization). Used by \'launch\'.") args = parser.parse_args(args) if args.command == "launch": @@ -483,7 +493,8 @@ def main(args): args.spot_max_price, args.block_devices, args.tags, - args.random_subnet) + args.random_subnet, + args.user_data_file) instids = get_instance_ids_for_instances(insts) print("Instance IDs: {}".format(instids)) wait_on_instance_launches(insts) diff --git a/deploy/buildtools/buildconfig.py b/deploy/buildtools/buildconfig.py index 1a98881a..278371ef 100644 --- a/deploy/buildtools/buildconfig.py +++ b/deploy/buildtools/buildconfig.py @@ -2,7 +2,7 @@ manager """ from time import strftime, gmtime -import ConfigParser +import configparser import pprint from runtools.runtime_config import RuntimeHWDB @@ -89,7 +89,7 @@ class GlobalBuildConfig: self.args = args - global_build_configfile = ConfigParser.ConfigParser(allow_no_value=True) + global_build_configfile = configparser.ConfigParser(allow_no_value=True) # make option names case sensitive global_build_configfile.optionxform = str global_build_configfile.read(args.buildconfigfile) @@ -111,9 +111,9 @@ class GlobalBuildConfig: self.post_build_hook = global_build_configfile.get('afibuild', 'postbuildhook') # this is a list of actual builds to run - builds_to_run_list = map(lambda x: x[0], global_build_configfile.items('builds')) + builds_to_run_list = list(map(lambda x: x[0], global_build_configfile.items('builds'))) - build_recipes_configfile = ConfigParser.ConfigParser(allow_no_value=True) + build_recipes_configfile = configparser.ConfigParser(allow_no_value=True) # make option names case sensitive build_recipes_configfile.optionxform = str build_recipes_configfile.read(args.buildrecipesconfigfile) @@ -167,7 +167,7 @@ class GlobalBuildConfig: def get_build_instance_ips(self): """ Return a list of all the build instance IPs, i.e. hosts to pass to fabric. """ - return map(lambda x: x.get_build_instance_private_ip(), self.builds_list) + return list(map(lambda x: x.get_build_instance_private_ip(), self.builds_list)) def get_builds_list(self): return self.builds_list diff --git a/deploy/firesim b/deploy/firesim index 29416dee..6fad053e 100755 --- a/deploy/firesim +++ b/deploy/firesim @@ -1,8 +1,6 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK -# REQUIRES PYTHON2, because fabric requires python2 - from __future__ import with_statement, print_function import sys import os @@ -83,7 +81,7 @@ def managerinit(): rootLogger.debug(m) rootLogger.debug(m.stderr) - useremail = raw_input("If you are a new user, supply your email address [abc@xyz.abc] for email notifications (leave blank if you do not want email notifications): ") + useremail = input("If you are a new user, supply your email address [abc@xyz.abc] for email notifications (leave blank if you do not want email notifications): ") if useremail != "": subscribe_to_firesim_topic(useremail) else: @@ -118,7 +116,7 @@ def buildafi(globalbuildconf): def terminate_instances_handler(sig, frame): """ Handler that prompts to terminate build instances if you press ctrl-c. """ rootLogger.info("You pressed ctrl-c, so builds have been killed.") - userconfirm = raw_input("Do you also want to terminate your build instances? Type 'yes' to do so.\n") + userconfirm = input("Do you also want to terminate your build instances? Type 'yes' to do so.\n") if userconfirm == "yes": globalbuildconf.terminate_all_build_instances() rootLogger.info("Instances terminated. Please confirm in your AWS Management Console.") diff --git a/deploy/runtools/firesim_topology_with_passes.py b/deploy/runtools/firesim_topology_with_passes.py index c4f42e89..2186aeef 100644 --- a/deploy/runtools/firesim_topology_with_passes.py +++ b/deploy/runtools/firesim_topology_with_passes.py @@ -6,12 +6,13 @@ import pprint import logging import datetime -from switch_model_config import * -from firesim_topology_core import * -from utils import MacAddress +from runtools.switch_model_config import * +from runtools.firesim_topology_core import * +from runtools.utils import MacAddress from fabric.api import * from colorama import Fore, Style import types +from functools import reduce from util.streamlogger import StreamLogger @@ -191,7 +192,7 @@ class FireSimTopologyWithPasses: # Filter out FireSimDummyServerNodes for actually deploying. # Infrastructure after this point will automatically look at the # FireSimDummyServerNodes if a FireSimSuperNodeServerNode is used - downlinknodes = map(lambda x: x.get_downlink_side(), [downlink for downlink in switch.downlinks if not isinstance(downlink.get_downlink_side(), FireSimDummyServerNode)]) + downlinknodes = list(map(lambda x: x.get_downlink_side(), [downlink for downlink in switch.downlinks if not isinstance(downlink.get_downlink_side(), FireSimDummyServerNode)])) if all([isinstance(x, FireSimSwitchNode) for x in downlinknodes]): # all downlinks are switches self.run_farm.m4_16s[m4_16s_used].add_switch(switch) @@ -465,7 +466,7 @@ class FireSimTopologyWithPasses: break # If AutoILA is disabled, use the following condition elif "No Sockets found" in screenoutput: - break + break time.sleep(1) execute(screens, hosts=all_runfarm_ips) @@ -501,22 +502,22 @@ class FireSimTopologyWithPasses: instancestate_map = dict() if terminateoncompletion: - for instip, instdata in instancestates.iteritems(): + for instip, instdata in instancestates.items(): # if terminateoncompletion and all sims are terminated, the inst must have been terminated - instancestate_map[instip] = all([x[1] for x in instdata['sims'].iteritems()]) + instancestate_map[instip] = all([x[1] for x in instdata['sims'].items()]) else: instancestate_map = {inst: False for inst in instancestates.keys()} switchstates = [] - for instip, instdata in instancestates.iteritems(): - for switchname, switchcompleted in instdata['switches'].iteritems(): + for instip, instdata in instancestates.items(): + for switchname, switchcompleted in instdata['switches'].items(): switchstates.append({'hostip': instip, 'switchname': switchname, 'running': not switchcompleted}) simstates = [] - for instip, instdata in instancestates.iteritems(): - for simname, simcompleted in instdata['sims'].iteritems(): + for instip, instdata in instancestates.items(): + for simname, simcompleted in instdata['sims'].items(): simstates.append({'hostip': instip, 'simname': simname, 'running': not simcompleted}) @@ -532,7 +533,7 @@ class FireSimTopologyWithPasses: totalsims = len(simstates) totalinsts = len(instancestate_map.keys()) runningsims = len([x for x in simstates if x['running']]) - runninginsts = len([x for x in instancestate_map.iteritems() if not x[1]]) + runninginsts = len([x for x in instancestate_map.items() if not x[1]]) # clear the screen rootLogger.info('\033[2J') diff --git a/deploy/runtools/run_farm.py b/deploy/runtools/run_farm.py index fc5fe10b..9502e1b1 100644 --- a/deploy/runtools/run_farm.py +++ b/deploy/runtools/run_farm.py @@ -337,7 +337,7 @@ class RunFarm: if not forceterminate: # --forceterminate was not supplied, so confirm with the user - userconfirm = raw_input("Type yes, then press enter, to continue. Otherwise, the operation will be cancelled.\n") + userconfirm = input("Type yes, then press enter, to continue. Otherwise, the operation will be cancelled.\n") else: userconfirm = "yes" diff --git a/deploy/runtools/runtime_config.py b/deploy/runtools/runtime_config.py index a84ac9ed..4badc624 100644 --- a/deploy/runtools/runtime_config.py +++ b/deploy/runtools/runtime_config.py @@ -4,7 +4,7 @@ simulation tasks. """ from __future__ import print_function from time import strftime, gmtime -import ConfigParser +import configparser import pprint import logging @@ -223,7 +223,7 @@ class RuntimeHWDB: as endpoints on the simulation. """ def __init__(self, hardwaredbconfigfile): - agfidb_configfile = ConfigParser.ConfigParser(allow_no_value=True) + agfidb_configfile = configparser.ConfigParser(allow_no_value=True) agfidb_configfile.read(hardwaredbconfigfile) agfidb_dict = {s:dict(agfidb_configfile.items(s)) for s in agfidb_configfile.sections()} @@ -240,7 +240,7 @@ class InnerRuntimeConfiguration: """ Pythonic version of config_runtime.ini """ def __init__(self, runtimeconfigfile, configoverridedata): - runtime_configfile = ConfigParser.ConfigParser(allow_no_value=True) + runtime_configfile = configparser.ConfigParser(allow_no_value=True) runtime_configfile.read(runtimeconfigfile) runtime_dict = {s:dict(runtime_configfile.items(s)) for s in runtime_configfile.sections()} diff --git a/deploy/tests/awstools/test_awstools.py b/deploy/tests/awstools/test_awstools.py index 9908366b..6751027e 100644 --- a/deploy/tests/awstools/test_awstools.py +++ b/deploy/tests/awstools/test_awstools.py @@ -8,7 +8,7 @@ import boto3 from botocore.stub import Stubber from moto import mock_sns -from mock import patch +from unittest.mock import patch import pytest from pytest import raises diff --git a/deploy/util/streamlogger.py b/deploy/util/streamlogger.py index 2fb2e280..d63114bc 100644 --- a/deploy/util/streamlogger.py +++ b/deploy/util/streamlogger.py @@ -7,7 +7,7 @@ which has no license associated with it. """ import sys import logging -import cStringIO +import io class StreamLogger(object): @@ -37,7 +37,7 @@ class StreamLogger(object): self.__name = name self.__stream = getattr(sys, name) self.__logger = logger or logging.getLogger() - self.__buffer = cStringIO.StringIO() + self.__buffer = io.StringIO() self.__unbuffered = unbuffered self.__flush_on_new_line = flush_on_new_line diff --git a/deploy/workloads/Makefile b/deploy/workloads/Makefile index 14ba8d4a..6fe4b34b 100644 --- a/deploy/workloads/Makefile +++ b/deploy/workloads/Makefile @@ -21,44 +21,41 @@ gapbs: input = graph500 $(GAP_DIR)/overlay/$(input): cd $(GAP_DIR) && ./gen_run_scripts.sh --binaries --input $(input) - -gapbs: gapbs.json $(GAP_DIR)/overlay/$(input) + +gapbs: gapbs.json $(GAP_DIR)/overlay/$(input) mkdir -p $@ cp $(BASE_LINUX) $@/bbl-vmlinux - python gen-benchmark-rootfs.py -w $< -r -b $(BASE_IMAGE) \ + python3 gen-benchmark-rootfs.py -w $< -r -b $(BASE_IMAGE) \ -s $(GAP_DIR)/overlay/$(input) \ memcached-thread-imbalance: mkdir -p $@ - sudo yum -y install gengetopt - sudo pip2 install matplotlib - sudo pip2 install pandas cd $@ && git submodule update --init mutilate-loadgen-riscv-release cd $@/mutilate-loadgen-riscv-release && ./build.sh - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/mutilate-loadgen-riscv-release/overlay + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/mutilate-loadgen-riscv-release/overlay bw-test-two-instances: bw-test-two-instances.json - cd ../../sw/network-benchmarks && python build-bw-test.py -n 8 + cd ../../sw/network-benchmarks && python3 build-bw-test.py -n 8 cp ../../sw/network-benchmarks/testbuild/*.riscv $@ bw-test-one-instance: bw-test-one-instance.json - cd ../../sw/network-benchmarks && python build-bw-test.py -n 4 + cd ../../sw/network-benchmarks && python3 build-bw-test.py -n 4 cp ../../sw/network-benchmarks/testbuild/*.riscv $@ ping-latency: mkdir -p $@ - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay simperf-test: mkdir -p $@ - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay linux-poweroff: mkdir -p $@/overlay cd ../../sw/check-rtc && make print-mcycle-linux cp ../../sw/check-rtc/print-mcycle-linux $@/overlay/ - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay simperf-test-scale: simperf-test @@ -69,7 +66,7 @@ flash-stress: simperf-test-latency iperf3: iperf3.json mkdir -p $@ cd $@ && ln -sf ../$(BASE_LINUX) bbl-vmlinux - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) check-rtc: cd ../../sw/check-rtc && make check-rtc @@ -79,14 +76,14 @@ check-rtc-linux: cd ../../sw/check-rtc && make check-rtc-linux cp ../../sw/check-rtc/check-rtc-linux $@/overlay cd $@ && ln -sf ../$(BASE_LINUX) bbl-vmlinux - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/overlay checksum-test: cd ../../target-design/chipyard/tests && make checksum.riscv ccbench-cache-sweep: cd ccbench-cache-sweep/ccbench/caches && make ARCH=riscv - python gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/ + python3 gen-benchmark-rootfs.py -w $@.json -r -b $(BASE_IMAGE) -s $@/ fc-test: cd ../../sw/network-benchmarks/fc-test && make diff --git a/deploy/workloads/ccbench-cache-sweep/plotccbench.py b/deploy/workloads/ccbench-cache-sweep/plotccbench.py index 158d18c0..4ea40c76 100644 --- a/deploy/workloads/ccbench-cache-sweep/plotccbench.py +++ b/deploy/workloads/ccbench-cache-sweep/plotccbench.py @@ -14,9 +14,9 @@ q = f.readlines() f.close() -q = filter(lambda x: x.startswith('App:'), q) -q = map(lambda x: x.strip().split(","), q) -q = map(lambda x: list(map(lambda z: z.split(":"), x)), q) +q = list(filter(lambda x: x.startswith('App:'), q)) +q = list(map(lambda x: x.strip().split(","), q)) +q = list(map(lambda x: list(map(lambda z: z.split(":"), x)), q)) def arr_to_dict(q): @@ -29,9 +29,9 @@ def arr_to_dict(q): as_dict.append(d) return as_dict -cacheline_stride_bmark = filter(lambda x: ['RunType', '[16]'] in x, q) -unit_stride_bmark = filter(lambda x: ['RunType', '[1]'] in x, q) -random_bmark = filter(lambda x: ['RunType', '[0]'] in x, q) +cacheline_stride_bmark = list(filter(lambda x: ['RunType', '[16]'] in x, q)) +unit_stride_bmark = list(filter(lambda x: ['RunType', '[1]'] in x, q)) +random_bmark = list(filter(lambda x: ['RunType', '[0]'] in x, q)) def data_from_full_dict(array_of_dict): times = [] diff --git a/deploy/workloads/gen-benchmark-rootfs.py b/deploy/workloads/gen-benchmark-rootfs.py index d6fb6a5f..941cf9ab 100755 --- a/deploy/workloads/gen-benchmark-rootfs.py +++ b/deploy/workloads/gen-benchmark-rootfs.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 import argparse import json @@ -49,7 +49,7 @@ def copy_base_rootfs(base_rootfs, dest): os.makedirs(BUILD_DIR) except OSError: pass - print "Copying base rootfs {} to {}".format(base_rootfs, dest) + print("Copying base rootfs {} to {}".format(base_rootfs, dest)) shutil.copy2(base_rootfs, dest) def mount_rootfs(rootfs): @@ -60,7 +60,7 @@ def mount_rootfs(rootfs): rc = subprocess.check_output(["sudo", "mount", "-t", EXT_TYPE, rootfs, MOUNT_POINT]) def cp_target(src, target_dest): - print "Copying src: {} to {} in target filesystem.".format(src, target_dest) + print("Copying src: {} to {} in target filesystem.".format(src, target_dest)) if not os.path.isdir(target_dest): dirname = os.path.dirname(target_dest) else: @@ -84,7 +84,7 @@ def generate_init_script(command): init_script_body = init_script_head + " " + command + init_script_tail temp_script = BUILD_DIR + "/temp" - with open(temp_script, 'wb') as f: + with open(temp_script, 'w') as f: f.write(init_script_body) cp_target(temp_script, INIT_SCRIPT_NAME) @@ -106,7 +106,7 @@ class Workload: self.outputs = outputs def generate_rootfs(self, base_rootfs, overlay, gen_init, output_dir): - print "\nGenerating a Rootfs image for " + self.name + print("\nGenerating a Rootfs image for " + self.name) dest_rootfs = output_dir + "/" + self.name + "." + EXT_TYPE copy_base_rootfs(base_rootfs, dest_rootfs) diff --git a/deploy/workloads/memcached-thread-imbalance/mutilate-loadgen-riscv-release b/deploy/workloads/memcached-thread-imbalance/mutilate-loadgen-riscv-release index 150a7769..873e0e5c 160000 --- a/deploy/workloads/memcached-thread-imbalance/mutilate-loadgen-riscv-release +++ b/deploy/workloads/memcached-thread-imbalance/mutilate-loadgen-riscv-release @@ -1 +1 @@ -Subproject commit 150a77698e8e786b8a87c14ae383889a1c24df67 +Subproject commit 873e0e5ca10f8a3f016023562a3d15e91af9229b diff --git a/deploy/workloads/ping-latency/ping-latency-graph.py b/deploy/workloads/ping-latency/ping-latency-graph.py index e17b35d4..7852fa10 100644 --- a/deploy/workloads/ping-latency/ping-latency-graph.py +++ b/deploy/workloads/ping-latency/ping-latency-graph.py @@ -11,7 +11,7 @@ import os basedir = sys.argv[1] + "/" -files = map(lambda x: basedir + x, sorted(os.listdir(basedir), key=int)) +files = list(map(lambda x: basedir + x, sorted(os.listdir(basedir), key=int))) def process_uartlog(uartlogpath): """ process the log and then report the mean RTT for this link latency """ @@ -52,11 +52,11 @@ def get_average_rtt_from_file(basedirname): return [link_latency_us, measured_rtt_in_us, ideal_rtt_in_us] -resultarray = map(get_average_rtt_from_file, files) +resultarray = list(map(get_average_rtt_from_file, files)) -link_latency = map(lambda x: x[0], resultarray) -measured_rtt = map(lambda x: x[1], resultarray) -ideal_rtt = map(lambda x: x[2], resultarray) +link_latency = list(map(lambda x: x[0], resultarray)) +measured_rtt = list(map(lambda x: x[1], resultarray)) +ideal_rtt = list(map(lambda x: x[2], resultarray)) print(resultarray) diff --git a/deploy/workloads/run-bw-test.sh b/deploy/workloads/run-bw-test.sh index a263f5f4..99d56d1e 100755 --- a/deploy/workloads/run-bw-test.sh +++ b/deploy/workloads/run-bw-test.sh @@ -40,7 +40,7 @@ do mv $originalfilename $resultsdir/$i done -python $ORIGDIR/bw-test-two-instances/bw-test-graph.py $(pwd)/$resultsdir +python3 $ORIGDIR/bw-test-two-instances/bw-test-graph.py $(pwd)/$resultsdir cd $ORIGDIR cd ../../ diff --git a/deploy/workloads/run-ping-latency.sh b/deploy/workloads/run-ping-latency.sh index c9655d1e..9d92a869 100755 --- a/deploy/workloads/run-ping-latency.sh +++ b/deploy/workloads/run-ping-latency.sh @@ -39,7 +39,7 @@ do mv $originalfilename $resultsdir/$i done -python $ORIGDIR/ping-latency/ping-latency-graph.py $(pwd)/$resultsdir +python3 $ORIGDIR/ping-latency/ping-latency-graph.py $(pwd)/$resultsdir firesim terminaterunfarm -c workloads/ping-latency-config.ini --forceterminate diff --git a/deploy/workloads/run-simperf-test-latency.sh b/deploy/workloads/run-simperf-test-latency.sh index dcaa845b..7a499aa5 100755 --- a/deploy/workloads/run-simperf-test-latency.sh +++ b/deploy/workloads/run-simperf-test-latency.sh @@ -39,7 +39,7 @@ do mv $originalfilename $resultsdir/$i done -python $ORIGDIR/simperf-test-latency/simperf-test-results.py $(pwd)/$resultsdir +python3 $ORIGDIR/simperf-test-latency/simperf-test-results.py $(pwd)/$resultsdir firesim terminaterunfarm -c workloads/simperf-test-latency-config.ini --forceterminate diff --git a/deploy/workloads/run-simperf-test-scale-supernode.sh b/deploy/workloads/run-simperf-test-scale-supernode.sh index 9a8baae5..18c9c9f8 100755 --- a/deploy/workloads/run-simperf-test-scale-supernode.sh +++ b/deploy/workloads/run-simperf-test-scale-supernode.sh @@ -72,4 +72,4 @@ loopfunc 8 0 0 loopfunc 4 1 0 -python $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir +python3 $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir diff --git a/deploy/workloads/run-simperf-test-scale.sh b/deploy/workloads/run-simperf-test-scale.sh index 752edbbc..f85ce246 100755 --- a/deploy/workloads/run-simperf-test-scale.sh +++ b/deploy/workloads/run-simperf-test-scale.sh @@ -72,4 +72,4 @@ loopfunc 2 0 0 loopfunc 1 1 0 -python $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir +python3 $ORIGDIR/simperf-test-scale/simperf-test-scale-results.py $(pwd)/$resultsdir diff --git a/deploy/workloads/simperf-test-latency/simperf-test-results.py b/deploy/workloads/simperf-test-latency/simperf-test-results.py index c6cd5449..a77845b9 100644 --- a/deploy/workloads/simperf-test-latency/simperf-test-results.py +++ b/deploy/workloads/simperf-test-latency/simperf-test-results.py @@ -11,7 +11,7 @@ import os basedir = sys.argv[1] + "/" -files = map(lambda x: basedir + x, os.listdir(basedir)) +files = list(map(lambda x: basedir + x, os.listdir(basedir))) def process_uartlog(uartlogpath): """ process the log and then report the mean RTT for this link latency """ @@ -62,10 +62,10 @@ def get_simperf_from_file(basedirname): return [link_latency_us, simperf_mhz] -resultarray = map(get_simperf_from_file, files) +resultarray = list(map(get_simperf_from_file, files)) -link_latency = map(lambda x: x[0], resultarray) -simperf_mhz = map(lambda x: x[1][1], resultarray) +link_latency = list(map(lambda x: x[0], resultarray)) +simperf_mhz = list(map(lambda x: x[1][1], resultarray)) resultarray = zip(link_latency, simperf_mhz) diff --git a/deploy/workloads/simperf-test-scale/simperf-test-scale-results.py b/deploy/workloads/simperf-test-scale/simperf-test-scale-results.py index 0600211c..2550df01 100644 --- a/deploy/workloads/simperf-test-scale/simperf-test-scale-results.py +++ b/deploy/workloads/simperf-test-scale/simperf-test-scale-results.py @@ -11,7 +11,7 @@ import os basedir = sys.argv[1] + "/" -files = map(lambda x: basedir + x, os.listdir(basedir)) +files = list(map(lambda x: basedir + x, os.listdir(basedir))) def extract_stats_from_uartlog(uartlogpath): """ read a uartlog and get sim perf results """ @@ -44,10 +44,10 @@ def get_simperf_from_file(basedirname): return [numnodes, simperf_mhz] -resultarray = map(get_simperf_from_file, files) +resultarray = list(map(get_simperf_from_file, files)) -numnodes = map(lambda x: x[0], resultarray) -simperf_mhz = map(lambda x: x[1][1], resultarray) +numnodes = list(map(lambda x: x[0], resultarray)) +simperf_mhz = list(map(lambda x: x[1][1], resultarray)) resultarray = zip(numnodes, simperf_mhz) diff --git a/docs/.gitignore b/docs/.gitignore index e35d8850..042022be 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,2 @@ _build +warnings.txt diff --git a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hanging-Simulators.rst b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hanging-Simulators.rst index fb377af0..090a336b 100644 --- a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hanging-Simulators.rst +++ b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hanging-Simulators.rst @@ -9,7 +9,7 @@ obvious if it's a bug in the target, or somewhere in the host. To make it easier identify the problem, the simulation driver includes a polling watchdog that tracks for simulation progress, and periodically updates an output file, ``heartbeat.csv``, with a target cycle count and a timestamp. When debugging -these issues, we always encourage the use of MIDAS-level simulation to try +these issues, we always encourage the use of meta-simulation to try reproducing the failure if possible. We outline three common cases in the section below. diff --git a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Dromajo.rst b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Dromajo.rst index 919eda61..24db9bfd 100644 --- a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Dromajo.rst +++ b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Dromajo.rst @@ -84,11 +84,11 @@ The commit log trace will by default print to the ``uartlog``. However, you can avoid printing it out by changing ``verbose == false`` in the ``dromajo_cosim.cpp`` file located in ``$CHIPYARD/tools/dromajo/dromajo-src/src/`` folder. -Troubleshooting Dromajo Simulations with MIDAS Simulations +Troubleshooting Dromajo Simulations with Meta-Simulations ---------------------------------------------------------- -If FPGA simulation fails with Dromajo, you can use MIDAS-level simulation to determine if your Dromajo setup is correct. -First refer to :ref:`Debugging & Testing with RTL Simulation` for more information on MIDAS-level simulation. +If FPGA simulation fails with Dromajo, you can use meta-simulation to determine if your Dromajo setup is correct. +First refer to :ref:`meta-simulation` for more information on meta-simulation. The main difference between those instructions and simulations with Dromajo is that you need to manually point to the ``dtb``, ``rom``, and binary files when invoking the simulator. Here is an example of a ``make`` command that can be run to check for a correct setup. diff --git a/docs/Advanced-Usage/Debugging-in-Software/RTL-Simulation.rst b/docs/Advanced-Usage/Debugging-in-Software/RTL-Simulation.rst index 4c32be6f..99226f96 100644 --- a/docs/Advanced-Usage/Debugging-in-Software/RTL-Simulation.rst +++ b/docs/Advanced-Usage/Debugging-in-Software/RTL-Simulation.rst @@ -1,6 +1,7 @@ +.. _meta-simulation: + Debugging & Testing with Meta-Simulation ========================================= -.. _meta-simulation: When we speak of RTL simulation in FireSim, we are generally referring to `meta-simulation`: simulating the FireSim simulator's RTL, typically using VCS or @@ -23,10 +24,10 @@ ones. This illustrated in the chart below. ====== ===== ======= ========= ============= ============= Type Waves VCS Verilator Verilator -O1 Verilator -O2 ====== ===== ======= ========= ============= ============= -Target Off 4.8 kHz 3.9 kHz 6.6 kHz N/A -Target On 0.8 kHz 3.0 kHz 5.1 kHz N/A -Meta Off 3.8 kHz 2.4 kHz 4.5 kHz 5.3 KHz -Meta On 2.9 kHz 1.5 kHz 2.7 kHz 3.4 KHz +Target Off 4.8 kHz 3.9 kHz 6.6 kHz N/A +Target On 0.8 kHz 3.0 kHz 5.1 kHz N/A +Meta Off 3.8 kHz 2.4 kHz 4.5 kHz 5.3 KHz +Meta On 2.9 kHz 1.5 kHz 2.7 kHz 3.4 KHz ====== ===== ======= ========= ============= ============= Note that using more aggressive optimization levels when compiling the @@ -137,7 +138,7 @@ Module Hierarchy To build out a simulator, Golden Gate adds multiple layers of module hierarchy to the target design and performs additional hierarchy mutations to implement bridges and resource optimizations. Meta-simulation uses the ``FPGATop`` module as the -top-level module, which excludes the platform shim layer (``F1Shim``, for EC2 F1). +top-level module, which excludes the platform shim layer (``F1Shim``, for EC2 F1). The original top-level of the input design is nested three levels below FPGATop: .. figure:: /img/metasim-module-hierarchy.png diff --git a/docs/Advanced-Usage/Generating-Different-Targets.rst b/docs/Advanced-Usage/Generating-Different-Targets.rst index cbfb78fe..987f0d2e 100644 --- a/docs/Advanced-Usage/Generating-Different-Targets.rst +++ b/docs/Advanced-Usage/Generating-Different-Targets.rst @@ -14,7 +14,7 @@ Restrictions on Target RTL Current limitations in Golden Gate place the following restrictions on the (FIR)RTL that can be transformed and thus used in FireSim: -#. The top-level module must have no inputs or outputs. Input stimulus and output capture must be +#. The top-level module must have no inputs or outputs. Input stimulus and output capture must be implemented using target RTL or target-to-host Bridges. #. All target clocks must be generated by a single ``RationalClockBridge``. #. Black boxes must be "clock-gateable" by replacing its input clock with a gated equivalent which will be used @@ -23,7 +23,7 @@ transformed and thus used in FireSim: a. As a consequence, target clock-gating cannot be implemented using black-box primitives, and must instead be modeled by adding clock-enables to all state elements of the gated clock domain (i.e., by adding an enable or feedback mux on registers to conditionally block updates, and by gating write-enables on memories). -#. Asynchronous reset must only be implemented using Rocket Chip's black-box async reset. +#. Asynchronous reset must only be implemented using Rocket Chip's black-box async reset. These are replaced with synchronously reset registers using a FIRRTL transformation. .. _verilog-ip: @@ -39,7 +39,7 @@ Verilog Blocks `_ section of the Chipyard documentation. -#. For the transform to work, the Chisel Blackbox that wraps the Verilog IP must have input clocks +#. For the transform to work, the Chisel Blackbox that wraps the Verilog IP must have input clocks that can safely be clock-gated. #. The compiler that produces the decoupled simulator ("FAME Transform") automatically recognizes such blackboxes inside the target design. @@ -132,7 +132,7 @@ Projects have the following directory structure: ├-Makefile # Top-level makefile for projects where FireSim is the top-level repo ├-Makefrag # Target-agnostic makefrag, with recipes to generate drivers and RTL simulators ├-src/main/scala/{target-project}/ - │ └─Makefrag # Defines target-specific make variables and recipes. + │ └─Makefrag # Defines target-specific make variables and recipes. ├-src/main/cc/{target-project}/ │ ├─{driver-csrcs}.cc # The target's simulation driver, and sofware-model sources │ └─{driver-headers}.h @@ -147,8 +147,8 @@ Specifying A Target Instance To generate a specific instance of a target, the build system leverages four Make variables: 1. ``TARGET_PROJECT``: this points the Makefile (`sim/Makefile`) at the right - target-specific Makefrag, which defines the generation and MIDAS-level - software-simulation recipes. The makefrag for the default target project is + target-specific Makefrag, which defines the generation and meta-simulation + software recipes. The makefrag for the default target project is defined at ``sim/src/main/makefrag/firesim``. 2. ``DESIGN``: the name of the top-level Chisel module to generate (a Scala class name). These are defined @@ -164,7 +164,7 @@ To generate a specific instance of a target, the build system leverages four Mak Common platform configs are described in ``firesim-lib/sim/src/main/scala/configs/CompilerConfigs.scala``). ``TARGET_CONFIG`` and ``PLATFORM_CONFIG`` are strings that are used to construct a -``Config`` instance (derives from RocketChip's parameterization system, ``Config``, see +``Config`` instance (derives from RocketChip's parameterization system, ``Config``, see `freechips.rocketchip.config `_). These strings are of the form "{..._}{\_}". Only the final, base class name is @@ -185,7 +185,7 @@ compound Config instance. With this scheme, you don't need to define a Config class for every instance you wish to generate. We use this scheme to specify FPGA frequencies (eg. "BaseF1Config_F90MHz") in manager build recipes, but it's also very useful for doing -sweeping over a parameterization space. +sweeping over a parameterization space. **Note that the precedence of Configs decreases from left to right in a string**. Appending a config to an existing one will only have an effect if it sets a field not already set in higher precendence Configs. For example, "BaseF1Config_F90MHz" is equivalent to @@ -225,7 +225,7 @@ expect is to open the scala REPL, instantiate an instance of the desired Rocket Chip Generator-based SoCs (firesim project) -------------------------------------------------- -Using the Make variables listed above, we give examples of generating different targets using +Using the Make variables listed above, we give examples of generating different targets using the default Rocket Chip-based target project. ----------------- diff --git a/docs/Advanced-Usage/Manager/Manager-Environment-Variables.rst b/docs/Advanced-Usage/Manager/Manager-Environment-Variables.rst index 642e5d08..01af3be4 100644 --- a/docs/Advanced-Usage/Manager/Manager-Environment-Variables.rst +++ b/docs/Advanced-Usage/Manager/Manager-Environment-Variables.rst @@ -6,7 +6,7 @@ Manager Environment Variables This page contains a centralized reference for the environment variables used by the manager. -.. _config-runtime: +.. _runfarm-prefix: ``FIRESIM_RUNFARM_PREFIX`` -------------------------- diff --git a/docs/Advanced-Usage/Manager/Manager-Tasks.rst b/docs/Advanced-Usage/Manager/Manager-Tasks.rst index b7e0c414..f5e0dc63 100644 --- a/docs/Advanced-Usage/Manager/Manager-Tasks.rst +++ b/docs/Advanced-Usage/Manager/Manager-Tasks.rst @@ -80,16 +80,16 @@ This directory will contain: ---------------------- This command can be used to run only steps 9 & 10 from an aborted ``firesim buildafi`` that has been -manually corrected. ``firesim tar2afi`` assumes that you have a +manually corrected. ``firesim tar2afi`` assumes that you have a ``firesim/deploy/results-build/LAUNCHTIME-CONFIG_TRIPLET-BUILD_NAME/cl_firesim`` directory tree that can be submitted to the AWS backend for conversion to an AFI. When using this command, you need to also provide the ``--launchtime LAUNCHTIME`` cmdline argument, -specifying an already existing LAUNCHTIME. +specifying an already existing LAUNCHTIME. -This command will run for the configurations specified in :ref:`config-build` and +This command will run for the configurations specified in :ref:`config-build` and :ref:`config-build-recipes` as with :ref:`firesim-buildafi`. It is likely that you may want -to comment out ``BUILD_NAME`` that successfully completed :ref:`firesim-builafi` before +to comment out ``BUILD_NAME`` that successfully completed :ref:`firesim-buildafi` before running this command. @@ -282,7 +282,7 @@ workload configuration (see the :ref:`defining-custom-workloads` section). For non-networked simulations, it will wait for ALL simulations to complete (copying -back results as each workload completes), then exit. +back results as each workload completes), then exit. For globally-cycle-accurate networked simulations, the global simulation will stop diff --git a/docs/Advanced-Usage/Miscellaneous-Tips.rst b/docs/Advanced-Usage/Miscellaneous-Tips.rst index e5a37d42..e7af8d8b 100644 --- a/docs/Advanced-Usage/Miscellaneous-Tips.rst +++ b/docs/Advanced-Usage/Miscellaneous-Tips.rst @@ -135,3 +135,9 @@ Then, you can move the cursor over something you want to jump to and hit ``ctrl-]`` to jump to the definition and ``ctrl-t`` to jump back out. E.g. in top-level configurations in FireSim, you can jump all the way down through the Rocket Chip codebase and even down to Chisel. + +Using FireSim CI +---------------- + +For more information on how to deal with the FireSim CI and how to run FPGA simulations in the CI, +refer to the the ``CI_README.md`` under the ``.github/`` directory. diff --git a/docs/Advanced-Usage/Workloads/Deprecated-Defining-Custom-Workloads.rst b/docs/Advanced-Usage/Workloads/Deprecated-Defining-Custom-Workloads.rst index f73a0d5f..53ecda97 100644 --- a/docs/Advanced-Usage/Workloads/Deprecated-Defining-Custom-Workloads.rst +++ b/docs/Advanced-Usage/Workloads/Deprecated-Defining-Custom-Workloads.rst @@ -79,7 +79,7 @@ directory name the same. In this case, we have set all of them to Next, the ``common_bootbinary`` field represents the binary that the simulations in this workload are expected to boot from. The manager will copy this binary -for each of the nodes in the simulation (each gets its own copy). The ``common_bootbinary`` path is +for each of the nodes in the simulation (each gets its own copy). The ``common_bootbinary`` path is relative to the workload's directory, in this case ``firesim/deploy/workloads/linux-uniform``. You'll notice in the above output from ``ls -la`` that this is actually just a symlink to ``br-base-bin`` that @@ -123,7 +123,7 @@ be fixed in a future release. Non-uniform Workload JSON (explicit job per simulated node) --------------------------------------------------------------- -Now, we'll look at the ``ping-latency`` workload, which explicitly defines a +Now, we'll look at the ``ping-latency`` workload, which explicitly defines a job per simulated node. .. include:: /../deploy/workloads/ping-latency.json @@ -193,10 +193,10 @@ see in the ``ping-latency`` directory. :: [ from the workloads/ directory ] - python gen-benchmark-rootfs.py -w ping-latency.json -r -b ../../sw/firesim-software/images/br-base.img -s ping-latency/overlay + ./gen-benchmark-rootfs.py -w ping-latency.json -r -b ../../sw/firesim-software/images/br-base.img -s ping-latency/overlay Notice that we tell this script where the json file lives, where the base rootfs image is, and where we expect to find files -that we want to include in the generated disk images. This script will take care of the rest and we'll end up with +that we want to include in the generated disk images. This script will take care of the rest and we'll end up with ``idler-[1-6].ext2``, ``pingee.ext2``, and ``pinger.ext2``! You'll notice a Makefile in the ``workloads/`` directory -- it contains many diff --git a/docs/Golden-Gate/Bridge-Walkthrough.rst b/docs/Golden-Gate/Bridge-Walkthrough.rst index 611487bc..bd49c789 100644 --- a/docs/Golden-Gate/Bridge-Walkthrough.rst +++ b/docs/Golden-Gate/Bridge-Walkthrough.rst @@ -127,7 +127,7 @@ from the BridgeModule may deadlock the simulator. Registering the Driver ++++++++++++++++++++++ -With the Bridge Driver implemented, we now have to register it in the main simulator +With the Bridge Driver implemented, we now have to register it in the main simulator simulator class defined in ``sim/src/main/cc/firesim/firesim_top.cc``. Here, we rely on the C preprocessor macros to instantiate the bridge driver only when the corresponding BridgeModule is present: @@ -157,4 +157,4 @@ Here the main order of business is to add header and source files to :end-before: DOC include end: Bridge Build System Changes That's it! At this point you should be able to both test your bridge in software -simulation using MIDAS-level simulation, or deploy it to an FPGA. +simulation using meta-simulation, or deploy it to an FPGA. diff --git a/docs/Initial-Setup/Configuring-Required-Infrastructure-in-Your-AWS-Account.rst b/docs/Initial-Setup/Configuring-Required-Infrastructure-in-Your-AWS-Account.rst index 7fb6e2ed..a91ce12c 100644 --- a/docs/Initial-Setup/Configuring-Required-Infrastructure-in-Your-AWS-Account.rst +++ b/docs/Initial-Setup/Configuring-Required-Infrastructure-in-Your-AWS-Account.rst @@ -108,17 +108,18 @@ See https://docs.aws.amazon.com/cli/latest/userguide/tutorial-ec2-ubuntu.html#configure-cli-launch-ec2 for more about aws configure. Within the prompt, you should specify the same region that you chose above (one of ``us-east-1``, ``us-west-2``, ``eu-west-1``) and set the default -output format to ``json``. You will need to generate an AWS access key in the "Security Credentials" menu of your AWS settings (as instructed in https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys ). +output format to ``json``. You will need to generate an AWS access key in the "Security Credentials" menu of your AWS settings (as instructed in https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys ). Again on the ``t2.nano`` instance, do the following: :: - sudo yum -y install python-pip - sudo pip install boto3 - sudo pip install --upgrade awscli + sudo yum install -y python36-pip + sudo pip3 install --upgrade pip + sudo python3 -m pip install boto3 + sudo python3 -m pip install --upgrade awscli wget https://raw.githubusercontent.com/firesim/firesim/master/scripts/aws-setup.py - python aws-setup.py + ./aws-setup.py This will create a VPC named ``firesim`` and a security group named ``firesim`` in your account. diff --git a/docs/Makefile b/docs/Makefile index 307ac248..9a103408 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,8 +2,8 @@ # # You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build +SPHINXOPTS = -w warnings.txt -n -W +SPHINXBUILD = python3 -msphinx SPHINXPROJ = FireSim SOURCEDIR = . BUILDDIR = _build diff --git a/docs/README.md b/docs/README.md index ccb23b68..8379cecf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,12 +1,12 @@ Building Docs -------------- - sudo pip install -r requirements.txt + sudo python3 -m pip install -r requirements.txt make html -Look in the `_build/html` directory for output. You can also run +Look in the `_build/html` directory for output. You can also run - python -m SimpleHTTPServer + python3 -m SimpleHTTPServer To get a proper locally-hosted version. diff --git a/docs/conf.py b/docs/conf.py index 4db67c68..5c68fec0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -108,7 +108,7 @@ html_static_path = ['_static'] # # html_sidebars = {} -html_logo = '_static/images/firesim_logo_small.png' +html_logo = '_static/firesim_logo_small.png' # -- Options for HTMLHelp output --------------------------------------------- diff --git a/regression/aws-ec2-tests/.gitignore b/regression/aws-ec2-tests/.gitignore new file mode 100644 index 00000000..4e182681 --- /dev/null +++ b/regression/aws-ec2-tests/.gitignore @@ -0,0 +1,3 @@ +ip_address +machine-launch-script.sh + diff --git a/regression/aws-ec2-tests/README.md b/regression/aws-ec2-tests/README.md new file mode 100644 index 00000000..f3e50378 --- /dev/null +++ b/regression/aws-ec2-tests/README.md @@ -0,0 +1,8 @@ +Assumes the following: +- Are running on an AWS instance (needed to access private IPs + username centos) +- Have a working AWS setup (ran aws configure) +- Have a specific hash to test (hash must be accessible from the mainline firesim repo) + +1. Launch manager using script (should log the IP address in a file to use) +2. Run a specific regression (up to the user to ensure that it ends properly) +3. Terminate manager instance diff --git a/regression/aws-ec2-tests/build-default-afis.sh b/regression/aws-ec2-tests/build-default-afis.sh new file mode 100755 index 00000000..9b633fc6 --- /dev/null +++ b/regression/aws-ec2-tests/build-default-afis.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -ex +set -o pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source $SCRIPT_DIR/defaults.sh + +run "cd firesim/ && source sourceme-f1-manager.sh && firesim buildafi" + +echo "Success" diff --git a/regression/aws-ec2-tests/defaults.sh b/regression/aws-ec2-tests/defaults.sh new file mode 100644 index 00000000..5f751f62 --- /dev/null +++ b/regression/aws-ec2-tests/defaults.sh @@ -0,0 +1,32 @@ +IP_ADDR_FILE=$SCRIPT_DIR/ip_address + +parse_ip_address () { + IP_ADDR=$(grep -E -o "192\.168\.[0-9]{1,3}\.[0-9]{1,3}" $IP_ADDR_FILE | head -n 1) + IP_ADDR="centos@$IP_ADDR" +} + +FIRESIM_PEM_FILE=~/firesim.pem + +copy () { + rsync -avzp -e "ssh -o StrictHostKeyChecking=no -i $FIRESIM_PEM_FILE" --exclude '.git' $1 $2 +} + +copy_no_sym () { + rsync -avzpL -e "ssh -o StrictHostKeyChecking=no -i $FIRESIM_PEM_FILE" --exclude '.git' $1 $2 +} + +run () { + if [ -z $IP_ADDR ]; then + parse_ip_address + fi + ssh -i $FIRESIM_PEM_FILE -o "StrictHostKeyChecking no" -t $IP_ADDR "bash -l -c '$@'" +} + +run_script () { + if [ -z $IP_ADDR ]; then + parse_ip_address + fi + ssh -i $FIRESIM_PEM_FILE -o "StrictHostKeyChecking no" -t $IP_ADDR 'bash -l -s' < $1 "$2" +} + + diff --git a/regression/aws-ec2-tests/firesim-managerinit.expect b/regression/aws-ec2-tests/firesim-managerinit.expect new file mode 100755 index 00000000..18102606 --- /dev/null +++ b/regression/aws-ec2-tests/firesim-managerinit.expect @@ -0,0 +1,5 @@ +#! /usr/bin/env expect +set timeout -1 +spawn firesim managerinit +send -- "\r" +expect eof diff --git a/regression/aws-ec2-tests/launch-setup-manager-instance.sh b/regression/aws-ec2-tests/launch-setup-manager-instance.sh new file mode 100755 index 00000000..a9157c78 --- /dev/null +++ b/regression/aws-ec2-tests/launch-setup-manager-instance.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -ex +set -o pipefail + +if [ $# -ne 1 ]; then + echo "$0 " + exit 1 +fi + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source $SCRIPT_DIR/defaults.sh + +FULL_HASH=$1 + +# get the userdata file to launch manager with +rm -rf machine-launch-script.sh +wget https://raw.githubusercontent.com/firesim/firesim/$FULL_HASH/scripts/machine-launch-script.sh + +# launch manager +$SCRIPT_DIR/../../deploy/awstools/awstools.py \ + launch \ + --inst_type c5.4xlarge \ + --user_data_file $PWD/machine-launch-script.sh \ + 2>&1 | tee $IP_ADDR_FILE + +rm -rf machine-launch-script.sh + +# make sure managerinit finishes properly +run "timeout 10m grep -q \".*machine launch script complete.*\" <(tail -f machine-launchstatus)" + +# setup the repo (similar to ci) + +run "git clone https://github.com/firesim/firesim.git" +run "cd firesim/ && git checkout $FULL_HASH" +run "cd firesim/ && ./build-setup.sh --fast" +run "cd firesim/sw/firesim-software && ./init-submodules.sh" +# use local aws permissions (for now bypass the manager) +copy ~/.aws/ $IP_ADDR:~/.aws +copy ~/firesim.pem $IP_ADDR:~/firesim.pem +copy firesim-managerinit.expect $IP_ADDR:~/firesim-managerinit.expect +run "cd firesim && source sourceme-f1-manager.sh && cd ../ && ./firesim-managerinit.expect" + +echo "Success" diff --git a/regression/aws-ec2-tests/run-isca-2018.sh b/regression/aws-ec2-tests/run-isca-2018.sh new file mode 100755 index 00000000..23d947c8 --- /dev/null +++ b/regression/aws-ec2-tests/run-isca-2018.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -ex +set -o pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source $SCRIPT_DIR/defaults.sh + +cd .. # firesim + +run "cd firesim/ && source sourceme-f1-manager.sh && sw/firesim-software && ./marshal -v build br-base.json && ./marshal -v install br-base.json" +run "cd firesim/ && source sourceme-f1-manager.sh && cd deploy/workloads/ && make allpaper" +run "cd firesim/ && source sourceme-f1-manager.sh && cd deploy/workloads/ && ./run-all.sh" + +echo "Success" diff --git a/scripts/aws-setup.py b/scripts/aws-setup.py old mode 100644 new mode 100755 index a2974c43..f229b812 --- a/scripts/aws-setup.py +++ b/scripts/aws-setup.py @@ -1,3 +1,5 @@ +#/usr/bin/env python3 + """ This script configures your AWS account to run FireSim. """ import boto3 diff --git a/scripts/machine-launch-script.sh b/scripts/machine-launch-script.sh index 86783243..9fc13b3f 100644 --- a/scripts/machine-launch-script.sh +++ b/scripts/machine-launch-script.sh @@ -4,6 +4,8 @@ set -ex set -o pipefail echo "machine launch script started" > /home/centos/machine-launchstatus +sudo chgrp centos /home/centos/machine-launchstatus +sudo chown centos /home/centos/machine-launchstatus { sudo yum install -y ca-certificates @@ -48,36 +50,28 @@ sudo yum -y install graphviz python-devel # used for CI sudo yum -y install expect -# pip2 no longer installed on FPGA developer AMIs -sudo yum -y install python-pip -# In the event it is (as on an older AMI), upgrade it just in case -sudo pip2 install --upgrade pip==20.3.4 -# these need to match what's in deploy/requirements.txt -sudo pip2 install fabric==1.14.0 -sudo pip2 install boto3==1.6.2 -sudo pip2 install colorama==0.3.7 -sudo pip2 install argcomplete==1.9.3 -sudo pip2 install graphviz==0.8.3 +# upgrade pip +sudo pip3 install --upgrade pip==21.3.1 +# install requirements +sudo python3 -m pip install fab-classic==1.19.1 +sudo python3 -m pip install boto3==1.20.21 +sudo python3 -m pip install colorama==0.4.3 +sudo python3 -m pip install argcomplete==1.12.3 +sudo python3 -m pip install graphviz==0.19 # for some of our workload plotting scripts -sudo pip2 install --upgrade --ignore-installed pyparsing -sudo pip2 install numpy==1.16.6 -sudo pip2 install kiwisolver==1.1.0 -sudo pip2 install matplotlib==2.2.2 -sudo pip2 install pandas==0.22.0 -# new awscli on 1.6.0 AMI is broken with our versions of boto3 -sudo pip2 install awscli==1.15.76 -# pip2 should install pytest 4.6.X as it's the last py2 release. see: -# https://pytest.org/en/latest/py27-py34-deprecation.html#what-this-means-for-general-users -sudo pip2 install pytest -# moto 1.3.1 is newest version that will work with boto3 1.6.2 -sudo pip2 install moto==1.3.1 +sudo python3 -m pip install pyparsing==3.0.6 +sudo python3 -m pip install numpy==1.19.5 +sudo python3 -m pip install kiwisolver==1.3.1 +sudo python3 -m pip install matplotlib==3.3.4 +sudo python3 -m pip install pandas==1.1.5 +sudo python3 -m pip install awscli==1.22.21 +sudo python3 -m pip install pytest==6.2.5 +sudo python3 -m pip install moto==2.2.17 # needed for the awstools cmdline parsing -sudo pip2 install pyyaml +sudo python3 -m pip install pyyaml==5.4.1 -sudo activate-global-python-argcomplete - -# Upgrading pip2 clobbers the pip3 installation paths. -sudo yum reinstall -y python36-pip +# setup argcomplete +activate-global-python-argcomplete } 2>&1 | tee /home/centos/machine-launchstatus.log diff --git a/sim/midas/src/main/resources/midas/models/dram/parse_micron_headers.py b/sim/midas/src/main/resources/midas/models/dram/parse_micron_headers.py index 5dfcefe0..5422bed4 100755 --- a/sim/midas/src/main/resources/midas/models/dram/parse_micron_headers.py +++ b/sim/midas/src/main/resources/midas/models/dram/parse_micron_headers.py @@ -1,4 +1,5 @@ -#!/usr/bin/python +#!/usr/bin/env python3 + import optparse import subprocess import json diff --git a/sim/scripts/create-afi.py b/sim/scripts/create-afi.py index 33f425f7..01c62a39 100755 --- a/sim/scripts/create-afi.py +++ b/sim/scripts/create-afi.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os.path import argparse diff --git a/sim/scripts/create-bucket.py b/sim/scripts/create-bucket.py index 02eb23b5..c0292a7c 100755 --- a/sim/scripts/create-bucket.py +++ b/sim/scripts/create-bucket.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os.path import argparse diff --git a/sim/src/main/resources/midasexamples/generate_memory_init.py b/sim/src/main/resources/midasexamples/generate_memory_init.py index cc921555..fdab4be5 100755 --- a/sim/src/main/resources/midasexamples/generate_memory_init.py +++ b/sim/src/main/resources/midasexamples/generate_memory_init.py @@ -1,4 +1,5 @@ -#!/usr/bin/python +#!/usr/bin/env python3 + import argparse import random from itertools import izip, count diff --git a/target-design/switch/Makefile b/target-design/switch/Makefile index 03b00685..fc5bcf3d 100644 --- a/target-design/switch/Makefile +++ b/target-design/switch/Makefile @@ -4,16 +4,6 @@ all: switch switch: switch.cc baseport.h shmemport.h flit.h socketport.h switchconfig.h g++ -g3 -O3 -std=gnu++11 -o switch switch.cc -fopenmp -lrt - -sim: - python emitconfig.py - -#switchtor: switch.cc baseport.h shmemport.h flit.h socketport.h switchconfig.h -# g++ -DSWITCHTOR -g3 -O3 -std=gnu++11 -o switchtor switch.cc -fopenmp -lrt -# -#switchroot: switch.cc baseport.h shmemport.h flit.h socketport.h switchconfig.h -# g++ -DSWITCHROOT -g3 -O3 -std=gnu++11 -o switchroot switch.cc -fopenmp -lrt - runswitch: echo "removing old /dev/shm/*" rm -rf /dev/shm/* From e0306c081d6db6e9e169c2918aca100eb064d8c9 Mon Sep 17 00:00:00 2001 From: Abraham Gonzalez Date: Thu, 20 Jan 2022 18:47:53 -0800 Subject: [PATCH 13/17] Use prefix to cd (#911) --- deploy/runtools/run_farm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deploy/runtools/run_farm.py b/deploy/runtools/run_farm.py index 9502e1b1..923702da 100644 --- a/deploy/runtools/run_farm.py +++ b/deploy/runtools/run_farm.py @@ -384,7 +384,9 @@ class InstanceDeployManager: def get_and_install_aws_fpga_sdk(self): """ Installs the aws-sdk. This gets us access to tools to flash the fpga. """ - with cd("../"): + with prefix('cd ../'), \ + StreamLogger('stdout'), \ + StreamLogger('stderr'): # use local version of aws_fpga on runfarm nodes aws_fpga_upstream_version = local('git -C platforms/f1/aws-fpga describe --tags --always --dirty', capture=True) if "-dirty" in aws_fpga_upstream_version: From 351054c97152a29a3fa9833e4891f916b0daee0b Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 19 Jan 2022 19:09:31 -0800 Subject: [PATCH 14/17] Fix Driver Return Code --- sim/src/main/cc/fasedtests/fasedtests_top.cc | 20 ++++++++++++------- sim/src/main/cc/firesim/firesim_top.cc | 15 +++++++++----- .../main/cc/firesim/systematic_scheduler.h | 14 +++++++++++-- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/sim/src/main/cc/fasedtests/fasedtests_top.cc b/sim/src/main/cc/fasedtests/fasedtests_top.cc index a7a2e770..f360803d 100644 --- a/sim/src/main/cc/fasedtests/fasedtests_top.cc +++ b/sim/src/main/cc/fasedtests/fasedtests_top.cc @@ -117,7 +117,7 @@ int fasedtests_top_t::run() { fprintf(stderr, "Commencing simulation.\n"); record_start_times(); - while (!simulation_complete() && !has_timed_out()) { + while (!simulation_complete() && !finished_scheduled_tasks()) { run_scheduled_tasks(); step(get_largest_stepsize(), false); while(!done() && !simulation_complete()){ @@ -129,13 +129,19 @@ int fasedtests_top_t::run() { fprintf(stderr, "\nSimulation complete.\n"); uint64_t end_cycle = actual_tcycle(); + int exitcode = exit_code(); - if (exitcode) { - fprintf(stderr, "*** FAILED *** (code = %d) after %llu cycles\n", exitcode, end_cycle); - } else if (!simulation_complete() && has_timed_out()) { - fprintf(stderr, "*** FAILED *** (timeout) after %llu cycles\n", end_cycle); + + // If the simulator is idle and we've gotten here without any bridge + // indicating doneness, we've advanced to the +max_cycles limit in the fastest target clock domain. + bool max_cycles_timeout = !simulation_complete() && done() && finished_scheduled_tasks(); + + if (exitcode != 0) { + fprintf(stderr, "*** FAILED *** (code = %d) after %" PRIu64 " cycles\n", exitcode, get_end_tcycle()); + } else if (max_cycles_timeout) { + fprintf(stderr, "*** FAILED *** +max_cycles specified timeout after %" PRIu64 " cycles\n", get_end_tcycle()); } else { - fprintf(stderr, "*** PASSED *** after %llu cycles\n", end_cycle); + fprintf(stderr, "*** PASSED *** after %" PRIu64 " cycles\n", get_end_tcycle()); } print_simulation_performance_summary(); @@ -148,7 +154,7 @@ int fasedtests_top_t::run() { e->finish(); } this->host_finish(); - return (exitcode || has_timed_out()) ? EXIT_SUCCESS : EXIT_FAILURE; + return ((exitcode != 0) || max_cycles_timeout) ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/sim/src/main/cc/firesim/firesim_top.cc b/sim/src/main/cc/firesim/firesim_top.cc index 8549fc50..df9de1a5 100644 --- a/sim/src/main/cc/firesim/firesim_top.cc +++ b/sim/src/main/cc/firesim/firesim_top.cc @@ -568,7 +568,7 @@ void firesim_top_t::run() { fprintf(stderr, "Commencing simulation.\n"); record_start_times(); - while (!simulation_complete() && !has_timed_out()) { + while (!simulation_complete() && !finished_scheduled_tasks()) { run_scheduled_tasks(); take_steps(get_largest_stepsize(), false); while(!done() && !simulation_complete()){ @@ -582,10 +582,15 @@ void firesim_top_t::run() { int firesim_top_t::teardown() { int exitcode = exit_code(); - if (exitcode) { + + // If the simulator is idle and we've gotten here without any bridge + // indicating doneness, we've advanced to the +max_cycles limit in the fastest target clock domain. + bool max_cycles_timeout = !simulation_complete() && done() && finished_scheduled_tasks(); + + if (exitcode != 0) { fprintf(stderr, "*** FAILED *** (code = %d) after %" PRIu64 " cycles\n", exitcode, get_end_tcycle()); - } else if (!simulation_complete() && has_timed_out()) { - fprintf(stderr, "*** FAILED *** (timeout) after %" PRIu64 " cycles\n", get_end_tcycle()); + } else if (max_cycles_timeout) { + fprintf(stderr, "*** FAILED *** +max_cycles specified timeout after %" PRIu64 " cycles\n", get_end_tcycle()); } else { fprintf(stderr, "*** PASSED *** after %" PRIu64 " cycles\n", get_end_tcycle()); } @@ -601,5 +606,5 @@ int firesim_top_t::teardown() { } this->host_finish(); - return (exitcode || has_timed_out()) ? EXIT_SUCCESS : EXIT_FAILURE; + return ((exitcode != 0) || max_cycles_timeout) ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/sim/src/main/cc/firesim/systematic_scheduler.h b/sim/src/main/cc/firesim/systematic_scheduler.h index 08028a66..35731b67 100644 --- a/sim/src/main/cc/firesim/systematic_scheduler.h +++ b/sim/src/main/cc/firesim/systematic_scheduler.h @@ -8,6 +8,15 @@ // Maximum step size in MIDAS's master is capped to width of the simulation bus constexpr uint64_t MAX_MIDAS_STEP = (1LL << sizeof(data_t) * 8) - 1; +// Schedules a series of tasks that must run a specific cycles on the FPGA, but +// may be associated with multiple bridges / touch non-bridge simulator +// collateral (e.g, reservoir sampling for strober.) +// +// This class does that by providing cycle deltas (i.e., a credit) that can be +// fed into some mechanism (e.g., peek/poke or a bridge) to advance the +// simulator to the time of the desired task. The onus is on the parent class / +// instantiator to ensure the simulator has advanced to the desired cycle +// before running the scheduled task. class systematic_scheduler_t { typedef std::function task_t; @@ -24,9 +33,10 @@ class systematic_scheduler_t // Assumption: The simulator is idle. (simif::done() == true) // Invokes all tasks that wish to be executed on our current target cycle void run_scheduled_tasks(); + // Unless overriden, assume the simulator will run (effectively) forever uint64_t max_cycles = -1; - // As above, assumes the simulator is idle. - bool has_timed_out() { return current_cycle == max_cycles; }; + // Returns true if no further tasks are scheduled before specified horizon (max_cycles). + bool finished_scheduled_tasks() { return current_cycle == max_cycles; }; private: uint64_t default_step_size = MAX_MIDAS_STEP; From 56e853dadd531398320cebbf0892034ef2490fc2 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 25 Jan 2022 00:43:21 +0000 Subject: [PATCH 15/17] Remove WithAutoILA from default recipes Users should explicitly mix this in like other debug features --- .../sample_config_build_recipes.ini | 16 ++++++++-------- .../Debugging-Hardware-Using-ILA.rst | 7 ++++--- .../src/main/scala/configs/CompilerConfigs.scala | 1 - 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/deploy/sample-backup-configs/sample_config_build_recipes.ini b/deploy/sample-backup-configs/sample_config_build_recipes.ini index 450bb77c..d816503d 100644 --- a/deploy/sample-backup-configs/sample_config_build_recipes.ini +++ b/deploy/sample-backup-configs/sample_config_build_recipes.ini @@ -14,7 +14,7 @@ [firesim-rocket-quadcore-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=WithNIC_DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimHighPerfConfigTweaks_chipyard.QuadRocketConfig -PLATFORM_CONFIG=WithAutoILA_F90MHz_BaseF1Config +PLATFORM_CONFIG=F90MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -23,7 +23,7 @@ deploytriplet=None [firesim-rocket-quadcore-no-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimTestChipConfigTweaks_chipyard.QuadRocketConfig -PLATFORM_CONFIG=WithAutoILA_F140MHz_BaseF1Config +PLATFORM_CONFIG=F140MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -32,7 +32,7 @@ deploytriplet=None [firesim-boom-singlecore-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=WithNIC_DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimHighPerfConfigTweaks_chipyard.LargeBoomConfig -PLATFORM_CONFIG=WithAutoILA_F65MHz_BaseF1Config +PLATFORM_CONFIG=F65MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -41,7 +41,7 @@ deploytriplet=None [firesim-boom-singlecore-no-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimTestChipConfigTweaks_chipyard.LargeBoomConfig -PLATFORM_CONFIG=WithAutoILA_F75MHz_BaseF1Config +PLATFORM_CONFIG=F75MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -50,7 +50,7 @@ deploytriplet=None [firesim-cva6-singlecore-no-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimConfigTweaks_chipyard.CVA6Config -PLATFORM_CONFIG=WithAutoILA_F90MHz_BaseF1Config +PLATFORM_CONFIG=F90MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -59,7 +59,7 @@ deploytriplet=None [firesim-rocket-singlecore-gemmini-no-nic-l2-llc4mb-ddr3] DESIGN=FireSim TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimConfigTweaks_chipyard.GemminiRocketConfig -PLATFORM_CONFIG=WithAutoILA_F30MHz_BaseF1Config +PLATFORM_CONFIG=F30MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -68,7 +68,7 @@ deploytriplet=None [firesim-boom-singlecore-no-nic-l2-llc4mb-ddr3-ramopts] DESIGN=FireSim TARGET_CONFIG=DDR3FRFCFSLLC4MB_WithDefaultFireSimBridges_WithFireSimTestChipConfigTweaks_chipyard.LargeBoomConfig -PLATFORM_CONFIG=WithAutoILA_MCRams_F90MHz_BaseF1Config +PLATFORM_CONFIG=MCRams_F90MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None @@ -77,7 +77,7 @@ deploytriplet=None [firesim-supernode-rocket-singlecore-nic-l2-lbp] DESIGN=FireSim TARGET_CONFIG=WithNIC_SupernodeFireSimRocketConfig -PLATFORM_CONFIG=WithAutoILA_F85MHz_BaseF1Config +PLATFORM_CONFIG=F85MHz_BaseF1Config instancetype=z1d.2xlarge deploytriplet=None diff --git a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst index 752a0c45..f40dac96 100644 --- a/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst +++ b/docs/Advanced-Usage/Debugging-and-Profiling-on-FPGA/Debugging-Hardware-Using-ILA.rst @@ -13,7 +13,7 @@ and provided and interface for setting trigger and viewing samples waveforms from the FPGA. For more information about ILAs, please refer to the Xilinx guide on the topic. -MIDAS, in its ``targetutils`` package, provides annotations for labeling +The ``midas.targetutils`` package provides annotations for labeling signals directly in the Chisel source. These will be consumed by a downstream FIRRTL pass which wires out the annotated signals, and binds them to an appropriately sized ILA instance. @@ -21,7 +21,8 @@ appropriately sized ILA instance. Enabling AutoILA ---------------- -To enable AutoILA, mixin `WithAutoILA` must be appended to the `PLATFORM_CONFIG`. This is appended by default to the `BaseF1Config`. +To enable AutoILA, mixin `WithAutoILA` must be prepended to the +`PLATFORM_CONFIG`. Prior to version 1.13, this was done by default. Annotating Signals ------------------------ @@ -40,7 +41,7 @@ vararg of chisel3.Data. Invoke it as follows: FpgaDebug(out1, in1) } -You can annotate signals throughout FireSim, including in MIDAS and +You can annotate signals throughout FireSim, including in Golden Gate Rocket-Chip Chisel sources, with the only exception being the Chisel3 sources themselves (eg. in Chisel3.util.Queue). diff --git a/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala b/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala index 2b717fdc..8ccd0543 100644 --- a/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala +++ b/sim/firesim-lib/src/main/scala/configs/CompilerConfigs.scala @@ -86,6 +86,5 @@ class BaseF1Config extends Config( new WithAsyncResetReplacement ++ new WithEC2F1Artefacts ++ new WithILATopWiringTransform ++ - new WithAutoILA ++ new midas.F1Config ) From e91d9e42b744034f974157a454b21dc68ea146a6 Mon Sep 17 00:00:00 2001 From: Abraham Gonzalez Date: Mon, 31 Jan 2022 15:31:42 -0800 Subject: [PATCH 16/17] Fix Scaladoc GH-A Build (#912) * Bump CI Git (+ launch script) | Fix scaladoc GH-A * Move git uninstall stuff to the CI docker image --- .github/actions/machinelaunchscript/action.yml | 4 +++- .../push-scaladoc-to-ghpages/action.yml | 18 ++++++++---------- .github/scripts/Dockerfile | 3 ++- .../workflows/firesim-publish-scala-doc.yml | 3 ++- .github/workflows/firesim-run-tests.yml | 2 +- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/actions/machinelaunchscript/action.yml b/.github/actions/machinelaunchscript/action.yml index df737b65..596166a7 100644 --- a/.github/actions/machinelaunchscript/action.yml +++ b/.github/actions/machinelaunchscript/action.yml @@ -4,5 +4,7 @@ description: "Run FireSim's machine-launch-script.sh" runs: using: "composite" steps: - - run: cd scripts/ && /usr/bin/bash machine-launch-script.sh + - run: | + sudo yum -y remove git git224 git224-core ius-release.noarch # remove any older git versions and collateral first + cd scripts/ && /usr/bin/bash machine-launch-script.sh shell: bash diff --git a/.github/actions/push-scaladoc-to-ghpages/action.yml b/.github/actions/push-scaladoc-to-ghpages/action.yml index 2f1a772d..c7204c27 100644 --- a/.github/actions/push-scaladoc-to-ghpages/action.yml +++ b/.github/actions/push-scaladoc-to-ghpages/action.yml @@ -4,26 +4,24 @@ description: "Pushes scaladoc to ghphage branch" runs: using: "composite" steps: - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 + - name: Install SSH key for Github.com + uses: webfactory/ssh-agent@v0.5.4 with: - key: ${{ secrets.SERVER_KEY }} - known_hosts: ${{ secrets.SERVER_NAME }} + ssh-private-key: ${{ env.FIRESIM-REPO-DEP-KEY }} - run: | - git config --global user.email "biancolin@berkeley.edu" + git config --global user.email "abe.gonzalez@berkeley.edu" git config --global user.name "github-actions" shell: bash - run: | - if [[ "${{ GITHUB_REF_TYPE }}" != 'tag' ]]; then + if [[ "${{ github.ref_type }}" != 'tag' ]]; then source env.sh - export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for ${{ GITHUB_REF_NAME }} release" - make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND='set apiDirectory := \"${{ GITHUB_REF_NAME }}\"; ghpagesPushSite' + export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for ${{ github.ref_type }} release" + make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND='set apiDirectory := \"${{ github.ref_type }}\"; ghpagesPushSite' else source env.sh - export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for dev:${{ GITHUB_SHA }}" + export SBT_GHPAGES_COMMIT_MESSAGE="[ci skip] Update scaladoc for dev:${{ github.sha }}" make -C sim TARGET_PROJECT=midasexamples sbt SBT_COMMAND="ghpagesPushSite" fi shell: bash - diff --git a/.github/scripts/Dockerfile b/.github/scripts/Dockerfile index 35a30019..ee318d36 100644 --- a/.github/scripts/Dockerfile +++ b/.github/scripts/Dockerfile @@ -12,7 +12,8 @@ RUN pip2 install --upgrade pip==18.0 # non-intuitive results # (the chipyard submodule is initialized to an apparently random commit) # If we want to get rid of this we could reclone the repo under the updated git -RUN yum -y install git +RUN yum -y install https://repo.ius.io/ius-release-el7.rpm +RUN yum -y install git224 RUN adduser centos RUN usermod -aG wheel centos diff --git a/.github/workflows/firesim-publish-scala-doc.yml b/.github/workflows/firesim-publish-scala-doc.yml index 99959a37..e08fe7d2 100644 --- a/.github/workflows/firesim-publish-scala-doc.yml +++ b/.github/workflows/firesim-publish-scala-doc.yml @@ -13,6 +13,7 @@ env: AWS-SECRET-ACCESS-KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS-DEFAULT-REGION: ${{ secrets.AWS_DEFAULT_REGION }} FIRESIM_PEM: ${{ secrets.FIRESIM_PEM }} + FIRESIM-REPO-DEP-KEY: ${{ secrets.FIRESIM_REPO_DEP_KEY }} LANG: "en_US.UTF-8" # required by SBT when it sees boost directories LANGUAGE: "en_US:en" LC_ALL: "en_US.UTF-8" @@ -22,7 +23,7 @@ jobs: name: publish-scala-doc runs-on: ubuntu-18.04 container: - image: firesim/firesim-ci:v1.1 + image: firesim/firesim-ci:v1.3 options: --entrypoint /bin/bash env: JVM_MEMORY: 3500M # Default JVM maximum heap limit diff --git a/.github/workflows/firesim-run-tests.yml b/.github/workflows/firesim-run-tests.yml index b8c284d5..7602015d 100644 --- a/.github/workflows/firesim-run-tests.yml +++ b/.github/workflows/firesim-run-tests.yml @@ -183,7 +183,7 @@ jobs: name: documentation-check runs-on: ubuntu-18.04 container: - image: firesim/firesim-ci:v1.1 + image: firesim/firesim-ci:v1.3 options: --entrypoint /bin/bash env: JVM_MEMORY: 3500M # Default JVM maximum heap limit From aafc01688149480e422d656a470a059598d9e474 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 2 Feb 2022 10:56:05 -0800 Subject: [PATCH 17/17] [readme] Add a link to scala doc. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 893e4d51..8a4352ef 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ You can learn more about FireSim in the following places: * **FireSim website**: https://fires.im * **FireSim ISCA 2018 Paper**: [Paper PDF](https://sagark.org/assets/pubs/firesim-isca2018.pdf) | [IEEE Xplore](https://ieeexplore.ieee.org/document/8416816) | [ACM DL](https://dl.acm.org/citation.cfm?id=3276543) | [BibTeX](https://sagark.org/assets/pubs/firesim-isca2018.bib.txt) | Selected as one of IEEE Micro’s “Top Picks from Computer Architecture Conferences, 2018”. * **FireSim documentation**: https://docs.fires.im +* **Scala API Documentation**: https://fires.im/firesim/latest/api/ * **Two-minute lightning talk from ISCA 2018** (FireSim simulating a datacenter): [YouTube](https://www.youtube.com/watch?v=4XwoSe5c8lY) * **Chisel Community Conference Tutorial**: [YouTube](https://www.youtube.com/watch?v=S3OriQnJXYQ) * **Updates/News**: [Changelog](/CHANGELOG.md) | [FireSim Blog](https://fires.im/blog/) | [Twitter](https://twitter.com/firesimproject)