Introduced a full verilator/vcs/debug matrix (#1435)
This PR moves the paramterization of test harnesses to the toplevel. Slightly re-wrote tests to avoid duplication of running logic.
This commit is contained in:
parent
9818dbcae3
commit
797e6e41bc
|
@ -19,7 +19,6 @@ project {
|
|||
"glob:**firesim-lib/src/main/scala/passes/ILATopWiring.scala",
|
||||
"glob:**firesim-lib/src/main/scala/util/Configs.scala",
|
||||
"glob:**firesim-lib/src/test/scala/EmitCIElaborationScript.scala",
|
||||
"glob:**firesim-lib/src/test/scala/TestSuiteCommon.scala",
|
||||
"glob:**midas/src/main/scala/junctions/Nasti2AXI4.scala",
|
||||
"glob:**midas/src/main/scala/junctions/ReorderQueue.scala",
|
||||
"glob:**midas/src/main/scala/junctions/addrmap.scala",
|
||||
|
@ -195,8 +194,6 @@ project {
|
|||
"glob:**src/main/scala/midasexamples/Util.scala",
|
||||
"glob:**src/main/scala/midasexamples/VerilogAccumulator.scala",
|
||||
"glob:**src/main/scala/midasexamples/WireInterconnect.scala",
|
||||
"glob:**src/test/scala/fasedtests/FASEDTestSuite.scala",
|
||||
"glob:**src/test/scala/midasexamples/TutorialSuite.scala"
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -6,39 +6,35 @@ import scala.io.Source
|
|||
import scala.sys.process.{stringSeqToProcess, ProcessLogger}
|
||||
import freechips.rocketchip.config.Config
|
||||
|
||||
/**
|
||||
* A base class that captures the platform-specific parts of the configuration of a test.
|
||||
/** A base class that captures the platform-specific parts of the configuration of a test.
|
||||
*
|
||||
* @param platformName Name of the target platform (f1 or vitis)
|
||||
* @param configs List of platform-specific configuration classes
|
||||
* @param platformName
|
||||
* Name of the target platform (f1 or vitis)
|
||||
* @param configs
|
||||
* List of platform-specific configuration classes
|
||||
*/
|
||||
abstract class BasePlatformConfig(val platformName: String, val configs: Seq[Class[_ <: Config]])
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/** 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.
|
||||
* Some tests inspect the simulation outputs, or run other simulators. See the [[TutorialSuite]] for examples of that.
|
||||
*
|
||||
* NB: Not thread-safe.
|
||||
*/
|
||||
abstract class TestSuiteBase extends org.scalatest.flatspec.AnyFlatSpec {
|
||||
|
||||
def commonMakeArgs: Seq[String]
|
||||
def targetName: String
|
||||
def targetConfigs: String = "NoConfig"
|
||||
def targetName: String
|
||||
def targetConfigs: String = "NoConfig"
|
||||
def platformMakeArgs: Seq[String] = Seq()
|
||||
|
||||
val replayBackends = Seq("rtl")
|
||||
|
||||
// Check if we are running out of Chipyard by checking for the existence of a firesim/sim directory
|
||||
val firesimDir = {
|
||||
val cwd = System.getProperty("user.dir")
|
||||
val cwd = System.getProperty("user.dir")
|
||||
val firesimAsLibDir = new File(cwd, "sims/firesim/sim")
|
||||
if (firesimAsLibDir.exists()) {
|
||||
firesimAsLibDir
|
||||
|
@ -50,9 +46,10 @@ abstract class TestSuiteBase extends org.scalatest.flatspec.AnyFlatSpec {
|
|||
var ciSkipElaboration: Boolean = false
|
||||
var transitiveFailure: Boolean = false
|
||||
|
||||
override def withFixture(test: NoArgTest) = {
|
||||
// Perform setup
|
||||
ciSkipElaboration = test.configMap.getOptional[String]("ci-skip-elaboration")
|
||||
override def withFixture(test: NoArgTest) = {
|
||||
// Perform setup
|
||||
ciSkipElaboration = test.configMap
|
||||
.getOptional[String]("ci-skip-elaboration")
|
||||
.map { _.toBoolean }
|
||||
.getOrElse(false)
|
||||
if (transitiveFailure) {
|
||||
|
@ -60,9 +57,9 @@ abstract class TestSuiteBase extends org.scalatest.flatspec.AnyFlatSpec {
|
|||
} else {
|
||||
super.withFixture(test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit def toStr(f: File): String = f.toString replace (File.separator, "/")
|
||||
implicit def toStr(f: File): String = f.toString.replace(File.separator, "/")
|
||||
|
||||
// Defines a make target that will build all prerequistes for downstream
|
||||
// tests that require a Scala invocation.
|
||||
|
@ -74,8 +71,8 @@ abstract class TestSuiteBase extends org.scalatest.flatspec.AnyFlatSpec {
|
|||
|
||||
// Runs make passing default args to specify the right target design, project and platform
|
||||
def make(makeArgs: String*): Int = {
|
||||
val cmd = makeCommand(makeArgs:_*)
|
||||
println("Running: %s".format(cmd mkString " "))
|
||||
val cmd = makeCommand(makeArgs: _*)
|
||||
println("Running: %s".format(cmd.mkString(" ")))
|
||||
cmd.!
|
||||
}
|
||||
|
||||
|
@ -83,7 +80,7 @@ abstract class TestSuiteBase extends org.scalatest.flatspec.AnyFlatSpec {
|
|||
// is used to prevent re-invoking make on a target with a dependency on the
|
||||
// result of this recipe. Which would lead to a second failure.
|
||||
def makeCriticalDependency(makeArgs: String*): Int = {
|
||||
val returnCode = make(makeArgs:_*)
|
||||
val returnCode = make(makeArgs: _*)
|
||||
transitiveFailure = returnCode != 0
|
||||
returnCode
|
||||
}
|
||||
|
@ -100,16 +97,7 @@ abstract class TestSuiteBase extends org.scalatest.flatspec.AnyFlatSpec {
|
|||
// Under CI, if make failed during elaboration we catch it here without
|
||||
// attempting to rebuild
|
||||
val target = (if (ciSkipElaboration) Seq("-q") else Seq()) ++ elaborateMakeTarget
|
||||
assert(makeCriticalDependency(target:_*) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a MIDAS-level RTL simulator of the target
|
||||
def compileMlSimulator(b: String, debug: Boolean = false) {
|
||||
if (isCmdAvailable(b)) {
|
||||
it should s"compile sucessfully to ${b}" + { if (debug) " with waves enabled" else "" } in {
|
||||
assert(makeCriticalDependency(s"$b%s".format(if (debug) "-debug" else "")) == 0)
|
||||
}
|
||||
assert(makeCriticalDependency(target: _*) == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,20 +106,70 @@ abstract class TestSuiteCommon(targetProject: String) extends TestSuiteBase {
|
|||
def platformConfigs: Seq[Class[_ <: Config]] = Seq()
|
||||
def basePlatformConfig: BasePlatformConfig
|
||||
|
||||
def run(
|
||||
backend: String,
|
||||
debug: Boolean = false,
|
||||
logFile: Option[File] = None,
|
||||
waveform: Option[File] = None,
|
||||
args: Seq[String] = Nil,
|
||||
) = {
|
||||
val makeArgs = Seq(
|
||||
s"run-$backend%s".format(if (debug) "-debug" else ""),
|
||||
"LOGFILE=%s".format(logFile.map(toStr).getOrElse("")),
|
||||
"WAVEFORM=%s".format(waveform.map(toStr).getOrElse("")),
|
||||
"ARGS=%s".format(args.mkString(" ")),
|
||||
)
|
||||
if (isCmdAvailable(backend)) {
|
||||
make(makeArgs: _*)
|
||||
} else 0
|
||||
}
|
||||
|
||||
def platformConfigString = (platformConfigs ++ basePlatformConfig.configs).map(_.getSimpleName).mkString("_")
|
||||
|
||||
override val platformMakeArgs = Seq(s"PLATFORM=${basePlatformConfig.platformName}")
|
||||
override val commonMakeArgs = Seq(s"TARGET_PROJECT=${targetProject}",
|
||||
s"DESIGN=${targetName}",
|
||||
s"TARGET_CONFIG=${targetConfigs}",
|
||||
s"PLATFORM_CONFIG=${platformConfigString}")
|
||||
override val commonMakeArgs = Seq(
|
||||
s"TARGET_PROJECT=${targetProject}",
|
||||
s"DESIGN=${targetName}",
|
||||
s"TARGET_CONFIG=${targetConfigs}",
|
||||
s"PLATFORM_CONFIG=${platformConfigString}",
|
||||
)
|
||||
|
||||
val targetTuple = s"${targetName}-${targetConfigs}-${platformConfigString}"
|
||||
|
||||
// These mirror those in the make files; invocation of the MIDAS compiler
|
||||
// is the one stage of the tests we don't invoke the Makefile for
|
||||
lazy val genDir = new File(firesimDir, s"generated-src/${basePlatformConfig.platformName}/${targetTuple}")
|
||||
lazy val outDir = new File(firesimDir, s"output/${basePlatformConfig.platformName}/${targetTuple}")
|
||||
lazy val genDir = new File(firesimDir, s"generated-src/${basePlatformConfig.platformName}/${targetTuple}")
|
||||
lazy val outDir = new File(firesimDir, s"output/${basePlatformConfig.platformName}/${targetTuple}")
|
||||
|
||||
def mkdirs() { genDir.mkdirs; outDir.mkdirs }
|
||||
|
||||
// Compiles a MIDAS-level RTL simulator of the target
|
||||
def compileMlSimulator(b: String, debug: Boolean) {
|
||||
if (isCmdAvailable(b)) {
|
||||
it should s"compile sucessfully to ${b}" + { if (debug) " with waves enabled" else "" } in {
|
||||
assert(makeCriticalDependency(s"$b%s".format(if (debug) "-debug" else "")) == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Method to be implemented by tests, providing an invocation to a simulation run and checks.
|
||||
*/
|
||||
def defineTests(backend: String, debug: Boolean): Unit
|
||||
|
||||
// Overrideable methods to specify test configurations.
|
||||
def simulators: Seq[String] = Seq("verilator", "vcs")
|
||||
def debugFlags: Seq[Boolean] = Seq(false)
|
||||
|
||||
// Define test rules across the matrix of simulators and debug flags.
|
||||
mkdirs()
|
||||
for (simulator <- simulators) {
|
||||
if (isCmdAvailable(simulator)) {
|
||||
for (debugFlag <- debugFlags) {
|
||||
behavior.of(s"$targetName with ${simulator}${if (debugFlag) "-debug" else ""}")
|
||||
elaborateAndCompile()
|
||||
compileMlSimulator(simulator, debugFlag)
|
||||
defineTests(simulator, debugFlag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,10 +54,13 @@ TARGET_SOURCE_DIRS ?=
|
|||
# while showing error messages, then it runs sbt again with errors disabled to
|
||||
# capture the classpath from the output (errors are dumped to stdout otherwise).
|
||||
define build_classpath
|
||||
$(SBT_NON_THIN) \
|
||||
bash -c '\
|
||||
export TMP=$(shell mktemp); \
|
||||
($(SBT_NON_THIN) \
|
||||
--error \
|
||||
"set showSuccess := false; project $(1); compile; package; export $(2):fullClasspath" \
|
||||
| head -n 1 | tr -d '\n' > $@
|
||||
> $$TMP || (cat $$TMP | head -n -1 && rm $$TMP && exit 1)) && \
|
||||
((cat $$TMP | head -n 1 | tr -d "\n" > $@) 2> /dev/null || true; rm -f $$TMP)'
|
||||
endef
|
||||
|
||||
# Helpers to identify all source files of the FireSim project.
|
||||
|
|
|
@ -9,10 +9,11 @@ import freechips.rocketchip.config.Parameters
|
|||
|
||||
import midas.targetutils.{TriggerSource, TriggerSink, SynthesizePrintf}
|
||||
|
||||
trait TriggerPredicatedPrintfConsts {
|
||||
val assertTriggerCycle = 100
|
||||
val deassertTriggerCycle = 1000
|
||||
object TriggerPredicatedPrintfConsts {
|
||||
val assertTriggerCycle: Int = 100
|
||||
val deassertTriggerCycle: Int = 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* An example module that uses the trigger system to enable a printf in a
|
||||
* desired region of interest.
|
||||
|
@ -24,7 +25,8 @@ trait TriggerPredicatedPrintfConsts {
|
|||
* instances of this module.
|
||||
*/
|
||||
class TriggerPredicatedPrintfDUT(printfPrefix: String = "SYNTHESIZED_PRINT ")
|
||||
extends Module with TriggerPredicatedPrintfConsts{
|
||||
extends Module {
|
||||
import TriggerPredicatedPrintfConsts._
|
||||
|
||||
val io = IO(new Bundle{})
|
||||
// An inner class to reduce namespace bloat in midasexamples package
|
||||
|
|
|
@ -25,33 +25,6 @@ abstract class BridgeSuite(
|
|||
) extends TestSuiteCommon("bridges")
|
||||
with Matchers {
|
||||
|
||||
def run(
|
||||
backend: String,
|
||||
debug: Boolean = false,
|
||||
logFile: Option[File] = None,
|
||||
waveform: Option[File] = None,
|
||||
args: Seq[String] = Nil,
|
||||
) = {
|
||||
val makeArgs = Seq(
|
||||
s"run-$backend%s".format(if (debug) "-debug" else ""),
|
||||
"LOGFILE=%s".format(logFile.map(toStr).getOrElse("")),
|
||||
"WAVEFORM=%s".format(waveform.map(toStr).getOrElse("")),
|
||||
"ARGS=%s".format(args.mkString(" ")),
|
||||
)
|
||||
if (isCmdAvailable(backend)) {
|
||||
make(makeArgs: _*)
|
||||
} else 0
|
||||
}
|
||||
|
||||
/** Runs MIDAS-level simulation on the design.
|
||||
*
|
||||
* @param backend
|
||||
* Backend simulator: "verilator" or "vcs"
|
||||
* @param debug
|
||||
* When true, captures waves from the simulation
|
||||
*/
|
||||
def runTest(backend: String, debug: Boolean = false)
|
||||
|
||||
/** Helper to generate tests strings.
|
||||
*/
|
||||
def getTestString(length: Int): String = {
|
||||
|
@ -59,45 +32,30 @@ abstract class BridgeSuite(
|
|||
val alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
(1 to length).map(_ => alpha(gen.nextInt(alpha.length))).mkString
|
||||
}
|
||||
|
||||
mkdirs()
|
||||
behavior.of(s"$targetName")
|
||||
elaborateAndCompile()
|
||||
for (backend <- Seq("vcs", "verilator")) {
|
||||
compileMlSimulator(backend)
|
||||
|
||||
val testEnvStr = s"pass in ${backend} MIDAS-level simulation"
|
||||
|
||||
if (isCmdAvailable(backend)) {
|
||||
it should testEnvStr in {
|
||||
runTest(backend)
|
||||
}
|
||||
} else {
|
||||
ignore should testEnvStr in {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UARTTest(targetConfig: BasePlatformConfig) extends BridgeSuite("UARTModule", "UARTConfig", targetConfig) {
|
||||
override def runTest(backend: String, debug: Boolean) {
|
||||
// Generate a short test string.
|
||||
val data = getTestString(16)
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "echo input to output" in {
|
||||
// Generate a short test string.
|
||||
val data = getTestString(16)
|
||||
|
||||
// Create an input file.
|
||||
val input = File.createTempFile("input", ".txt")
|
||||
input.deleteOnExit()
|
||||
val inputWriter = new BufferedWriter(new FileWriter(input))
|
||||
inputWriter.write(data)
|
||||
inputWriter.flush()
|
||||
inputWriter.close()
|
||||
// Create an input file.
|
||||
val input = File.createTempFile("input", ".txt")
|
||||
input.deleteOnExit()
|
||||
val inputWriter = new BufferedWriter(new FileWriter(input))
|
||||
inputWriter.write(data)
|
||||
inputWriter.flush()
|
||||
inputWriter.close()
|
||||
|
||||
// Create an output file to write to.
|
||||
val output = File.createTempFile("output", ".txt")
|
||||
// Create an output file to write to.
|
||||
val output = File.createTempFile("output", ".txt")
|
||||
|
||||
val runResult = run(backend, debug, args = Seq(s"+uart-in0=${input.getPath}", s"+uart-out0=${output.getPath}"))
|
||||
assert(runResult == 0)
|
||||
val result = scala.io.Source.fromFile(output.getPath).mkString
|
||||
result should equal(data)
|
||||
val runResult = run(backend, debug, args = Seq(s"+uart-in0=${input.getPath}", s"+uart-out0=${output.getPath}"))
|
||||
assert(runResult == 0)
|
||||
val result = scala.io.Source.fromFile(output.getPath).mkString
|
||||
result should equal(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,32 +64,34 @@ class UARTVitisTest extends UARTTest(BaseConfigs.Vitis)
|
|||
|
||||
class BlockDevTest(targetConfig: BasePlatformConfig)
|
||||
extends BridgeSuite("BlockDevModule", "BlockDevConfig", targetConfig) {
|
||||
override def runTest(backend: String, debug: Boolean) {
|
||||
// Generate a random string spanning 2 sectors with a fixed seed.
|
||||
val data = getTestString(1024)
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "copy from one device to another" in {
|
||||
// Generate a random string spanning 2 sectors with a fixed seed.
|
||||
val data = getTestString(1024)
|
||||
|
||||
// Create an input file.
|
||||
val input = File.createTempFile("input", ".txt")
|
||||
input.deleteOnExit()
|
||||
val inputWriter = new BufferedWriter(new FileWriter(input))
|
||||
inputWriter.write(data)
|
||||
inputWriter.flush()
|
||||
inputWriter.close()
|
||||
// Create an input file.
|
||||
val input = File.createTempFile("input", ".txt")
|
||||
input.deleteOnExit()
|
||||
val inputWriter = new BufferedWriter(new FileWriter(input))
|
||||
inputWriter.write(data)
|
||||
inputWriter.flush()
|
||||
inputWriter.close()
|
||||
|
||||
// Pre-allocate space in the output.
|
||||
val output = File.createTempFile("output", ".txt")
|
||||
output.deleteOnExit()
|
||||
val outputWriter = new BufferedWriter(new FileWriter(output))
|
||||
for (i <- 1 to data.size) {
|
||||
outputWriter.write('x')
|
||||
// Pre-allocate space in the output.
|
||||
val output = File.createTempFile("output", ".txt")
|
||||
output.deleteOnExit()
|
||||
val outputWriter = new BufferedWriter(new FileWriter(output))
|
||||
for (i <- 1 to data.size) {
|
||||
outputWriter.write('x')
|
||||
}
|
||||
outputWriter.flush()
|
||||
outputWriter.close()
|
||||
|
||||
val runResult = run(backend, debug, args = Seq(s"+blkdev0=${input.getPath}", s"+blkdev1=${output.getPath}"))
|
||||
assert(runResult == 0)
|
||||
val result = scala.io.Source.fromFile(output.getPath).mkString
|
||||
result should equal(data)
|
||||
}
|
||||
outputWriter.flush()
|
||||
outputWriter.close()
|
||||
|
||||
val runResult = run(backend, debug, args = Seq(s"+blkdev0=${input.getPath}", s"+blkdev1=${output.getPath}"))
|
||||
assert(runResult == 0)
|
||||
val result = scala.io.Source.fromFile(output.getPath).mkString
|
||||
result should equal(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,38 +19,40 @@ abstract class TracerVTestBase(
|
|||
trace: Option[Int] = None,
|
||||
start: Option[String] = None,
|
||||
) extends BridgeSuite("TracerVModule", s"TracerVModuleTestCount${width}", platformConfig) {
|
||||
override def runTest(backend: String, debug: Boolean) {
|
||||
// Create an expected file.
|
||||
val expected = File.createTempFile("expected", ".txt")
|
||||
expected.deleteOnExit()
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "dump traced instructions" in {
|
||||
// Create an expected file.
|
||||
val expected = File.createTempFile("expected", ".txt")
|
||||
expected.deleteOnExit()
|
||||
|
||||
// Create the output file. tracerv will always append '-C0' to the end of the path provided in the plusarg
|
||||
val output = File.createTempFile("output", ".txt-C0")
|
||||
output.deleteOnExit()
|
||||
val outputPath = output.getPath.stripSuffix("-C0")
|
||||
// Create the output file. tracerv will always append '-C0' to the end of the path provided in the plusarg
|
||||
val output = File.createTempFile("output", ".txt-C0")
|
||||
output.deleteOnExit()
|
||||
val outputPath = output.getPath.stripSuffix("-C0")
|
||||
|
||||
// group the optional function args together with the correct plusarg string names
|
||||
val optionalArgs = Seq(
|
||||
("+trace-select=", trace),
|
||||
("+trace-start=", start),
|
||||
)
|
||||
// group the optional function args together with the correct plusarg string names
|
||||
val optionalArgs = Seq(
|
||||
("+trace-select=", trace),
|
||||
("+trace-start=", start),
|
||||
)
|
||||
|
||||
// a seq starting with fixed plusargs, ending with optional plusargs
|
||||
// the optional plusargs are properly constructed or dropped
|
||||
val args = Seq(
|
||||
s"+tracefile=${outputPath}",
|
||||
s"+tracerv-expected-output=${expected.getPath}",
|
||||
) ++ optionalArgs.collect { case (a, Some(b)) =>
|
||||
s"${a}${b}"
|
||||
// a seq starting with fixed plusargs, ending with optional plusargs
|
||||
// the optional plusargs are properly constructed or dropped
|
||||
val args = Seq(
|
||||
s"+tracefile=${outputPath}",
|
||||
s"+tracerv-expected-output=${expected.getPath}",
|
||||
) ++ optionalArgs.collect { case (a, Some(b)) =>
|
||||
s"${a}${b}"
|
||||
}
|
||||
|
||||
val runResult =
|
||||
run(backend, false, args = args)
|
||||
assert(runResult == 0)
|
||||
|
||||
val expectedContents = scala.io.Source.fromFile(expected.getPath).mkString
|
||||
val outputContents = scala.io.Source.fromFile(output.getPath).mkString
|
||||
outputContents should equal(expectedContents)
|
||||
}
|
||||
|
||||
val runResult =
|
||||
run(backend, false, args = args)
|
||||
assert(runResult == 0)
|
||||
|
||||
val expectedContents = scala.io.Source.fromFile(expected.getPath).mkString
|
||||
val outputContents = scala.io.Source.fromFile(output.getPath).mkString
|
||||
outputContents should equal(expectedContents)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ package firesim.fasedtests
|
|||
import java.io.File
|
||||
|
||||
import scala.io.Source
|
||||
import scala.concurrent.{Future, Await, ExecutionContext}
|
||||
import scala.concurrent.{Await, ExecutionContext, Future}
|
||||
import scala.util.Random
|
||||
import org.scalatest.Suites
|
||||
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.system.{RocketTestSuite, BenchmarkTestSuite}
|
||||
import freechips.rocketchip.system.{BenchmarkTestSuite, RocketTestSuite}
|
||||
import freechips.rocketchip.system.TestGeneration._
|
||||
import freechips.rocketchip.system.DefaultTestSuites._
|
||||
|
||||
|
@ -19,7 +19,7 @@ import firesim.TestSuiteUtil._
|
|||
import firesim.BasePlatformConfig
|
||||
import firesim.midasexamples.BaseConfigs
|
||||
|
||||
/** Different runtime-configuration modes extend this trait. See below. */
|
||||
/** Different runtime-configuration modes extend this trait. See below. */
|
||||
sealed trait RuntimeConfig {
|
||||
def behaviorString: String
|
||||
}
|
||||
|
@ -35,27 +35,31 @@ case object EmptyRuntimeConfig extends RuntimeConfig {
|
|||
}
|
||||
|
||||
/** Specific an alternate path to a conf file. */
|
||||
case class CustomRuntimeConfig(pathRelativeToSim: String) extends RuntimeConfig {
|
||||
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..
|
||||
/** A specialization of TestSuiteCommon for FASED-specific testing. Mostly handles differences in the makefrag vs
|
||||
* midasexamples..
|
||||
*
|
||||
* @param targetName 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
|
||||
* @param targetName
|
||||
* 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(
|
||||
override val targetName: String,
|
||||
override val targetConfigs: String,
|
||||
platformConfigs: String = "",
|
||||
baseRuntimeConfig: RuntimeConfig = DefaultRuntimeConfig,
|
||||
additionalPlusArgs: Seq[String] = Seq(),
|
||||
) extends firesim.TestSuiteCommon("fasedtests") {
|
||||
override val targetName: String,
|
||||
override val targetConfigs: String,
|
||||
platformConfigs: String = "",
|
||||
baseRuntimeConfig: RuntimeConfig = DefaultRuntimeConfig,
|
||||
additionalPlusArgs: Seq[String] = Seq(),
|
||||
) extends firesim.TestSuiteCommon("fasedtests") {
|
||||
|
||||
override def basePlatformConfig = BaseConfigs.F1
|
||||
|
||||
|
@ -63,41 +67,38 @@ abstract class FASEDTest(
|
|||
import ExecutionContext.Implicits.global
|
||||
|
||||
def invokeMlSimulator(backend: String, debug: Boolean, args: Seq[String]) = {
|
||||
make((s"run-${backend}%s".format(if (debug) "-debug" else "") +: args):_*)
|
||||
make((s"run-${backend}%s".format(if (debug) "-debug" else "") +: args): _*)
|
||||
}
|
||||
|
||||
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) = {
|
||||
backend: String,
|
||||
debug: Boolean,
|
||||
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"COMMON_SIM_ARGS=")
|
||||
case DefaultRuntimeConfig => None
|
||||
case EmptyRuntimeConfig => Some(s"COMMON_SIM_ARGS=")
|
||||
case CustomRuntimeConfig(path) => Some(s"COMMON_SIM_ARGS=${Source.fromFile(path).getLines.mkString(" ")}")
|
||||
}
|
||||
val plusArgs = Seq(s"""EXTRA_SIM_ARGS=${additionalPlusArgs.mkString(" ")}""")
|
||||
val logArg = logFile.map { logName => s"LOGFILE=${logName}" }
|
||||
val plusArgs = Seq(s"""EXTRA_SIM_ARGS=${additionalPlusArgs.mkString(" ")}""")
|
||||
val logArg = logFile.map { logName => s"LOGFILE=${logName}" }
|
||||
|
||||
val makeArgs =
|
||||
runtimeConfArg ++:
|
||||
plusArgs ++:
|
||||
logArg ++:
|
||||
additionalMakeArgs
|
||||
plusArgs ++:
|
||||
logArg ++:
|
||||
additionalMakeArgs
|
||||
|
||||
|
||||
if (isCmdAvailable(backend)) {
|
||||
it should behaviorSpec.getOrElse(s"run on $backend") in {
|
||||
assert(invokeMlSimulator(backend, debug, makeArgs) == 0)
|
||||
}
|
||||
it should behaviorSpec.getOrElse(s"run") in {
|
||||
assert(invokeMlSimulator(backend, debug, makeArgs) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
compileMlSimulator("verilator", false)
|
||||
runTest("verilator", false)
|
||||
override def defineTests(backend: String, debug: Boolean): Unit = runTest(backend, debug)
|
||||
}
|
||||
|
||||
class AXI4FuzzerLBPTest extends FASEDTest("AXI4Fuzzer", "DefaultConfig")
|
||||
|
@ -105,31 +106,32 @@ class AXI4FuzzerLBPTest extends FASEDTest("AXI4Fuzzer", "DefaultConfig")
|
|||
// 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") {
|
||||
val logA = new File(s"$outDir/using-runtime-conf.out")
|
||||
runTest(
|
||||
"verilator",
|
||||
logFile = Some(logA),
|
||||
behaviorSpec = Some("run using a runtime.conf"))
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
val logA = new File(s"$outDir/using-runtime-conf.out")
|
||||
runTest(backend, debug, 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"))
|
||||
val logB = new File(s"$outDir/using-hardwired-settings.out")
|
||||
runTest(
|
||||
backend,
|
||||
debug,
|
||||
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)
|
||||
"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")
|
||||
class AXI4FuzzerFCFSTest extends FASEDTest("AXI4Fuzzer", "FCFSConfig")
|
||||
class AXI4FuzzerFRFCFSTest extends FASEDTest("AXI4Fuzzer", "FRFCFSConfig")
|
||||
class AXI4FuzzerLLCDRAMTest extends FASEDTest("AXI4Fuzzer", "LLCDRAMConfig") {
|
||||
class AXI4FuzzerFCFSTest extends FASEDTest("AXI4Fuzzer", "FCFSConfig")
|
||||
class AXI4FuzzerFRFCFSTest extends FASEDTest("AXI4Fuzzer", "FRFCFSConfig")
|
||||
class AXI4FuzzerLLCDRAMTest extends FASEDTest("AXI4Fuzzer", "LLCDRAMConfig") {
|
||||
//override def runTests = {
|
||||
// // Check that the memory model uses the correct number of MSHRs
|
||||
// val maxMSHRs = targetParams(LlcKey).get.mshrs.max
|
||||
|
@ -144,23 +146,21 @@ class AXI4FuzzerLLCDRAMTest extends FASEDTest("AXI4Fuzzer", "LLCDRAMConfig") {
|
|||
}
|
||||
|
||||
// Generate a target memory system that uses the whole host memory system.
|
||||
class BaselineMultichannelTest extends FASEDTest(
|
||||
"AXI4Fuzzer",
|
||||
"AddrBits22_QuadFuzzer_DefaultConfig",
|
||||
"AddrBits22_SmallQuadChannelHostConfig") {
|
||||
runTest("vcs", true)
|
||||
}
|
||||
class BaselineMultichannelTest
|
||||
extends FASEDTest("AXI4Fuzzer", "AddrBits22_QuadFuzzer_DefaultConfig", "AddrBits22_SmallQuadChannelHostConfig")
|
||||
|
||||
// Checks that id-reallocation works for platforms with limited ID space
|
||||
class NarrowIdConstraint extends FASEDTest("AXI4Fuzzer", "DefaultConfig", "ConstrainedIdHostConfig")
|
||||
|
||||
// Suite Collections for CI
|
||||
class CIGroupA extends Suites(
|
||||
new AXI4FuzzerLBPTest,
|
||||
new AXI4FuzzerFRFCFSTest
|
||||
)
|
||||
class CIGroupA
|
||||
extends Suites(
|
||||
new AXI4FuzzerLBPTest,
|
||||
new AXI4FuzzerFRFCFSTest,
|
||||
)
|
||||
|
||||
class CIGroupB extends Suites(
|
||||
new AXI4FuzzerLLCDRAMTest,
|
||||
new NarrowIdConstraint
|
||||
)
|
||||
class CIGroupB
|
||||
extends Suites(
|
||||
new AXI4FuzzerLLCDRAMTest,
|
||||
new NarrowIdConstraint,
|
||||
)
|
||||
|
|
|
@ -30,7 +30,6 @@ abstract class FireSimTestSuite(
|
|||
override val platformConfigs: Seq[Class[_ <: Config]] = Seq(),
|
||||
N: Int = 8,
|
||||
) extends TestSuiteCommon("firesim") {
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import ExecutionContext.Implicits.global
|
||||
|
||||
|
@ -47,68 +46,32 @@ abstract class FireSimTestSuite(
|
|||
)
|
||||
}
|
||||
|
||||
def runTest(backend: String, name: String, debug: Boolean, additionalArgs: Seq[String] = Nil) = {
|
||||
compileMlSimulator(backend, debug)
|
||||
if (isCmdAvailable(backend)) {
|
||||
it should s"pass in ML simulation on ${backend}" in {
|
||||
assert(invokeMlSimulator(backend, name, debug, additionalArgs) == 0)
|
||||
def runTest(backend: String, debug: Boolean, name: String, additionalArgs: Seq[String] = Nil) = {
|
||||
it should s"pass in ML simulation on ${backend}" in {
|
||||
assert(invokeMlSimulator(backend, name, debug, additionalArgs) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
def runSuite(backend: String, debug: Boolean)(suite: RocketTestSuite) {
|
||||
val postfix = suite match {
|
||||
case _: BenchmarkTestSuite | _: BlockdevTestSuite | _: NICTestSuite => ".riscv"
|
||||
case _ => ""
|
||||
}
|
||||
it should s"pass all tests in ${suite.makeTargetName}" in {
|
||||
val results = suite.names.toSeq.sliding(N, N).map { t =>
|
||||
val subresults = t.map(name => Future(name -> invokeMlSimulator(backend, s"$name$postfix", debug)))
|
||||
Await.result(Future.sequence(subresults), Duration.Inf)
|
||||
}
|
||||
results.flatten.foreach { case (name, exitcode) =>
|
||||
assert(exitcode == 0, s"Failed $name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def runSuite(backend: String, debug: Boolean = false)(suite: RocketTestSuite) {
|
||||
// compile emulators
|
||||
behavior.of(s"${suite.makeTargetName} running on $backend")
|
||||
if (isCmdAvailable(backend)) {
|
||||
val postfix = suite match {
|
||||
case _: BenchmarkTestSuite | _: BlockdevTestSuite | _: NICTestSuite => ".riscv"
|
||||
case _ => ""
|
||||
}
|
||||
it should s"pass all tests in ${suite.makeTargetName}" in {
|
||||
val results = suite.names.toSeq.sliding(N, N).map { t =>
|
||||
val subresults = t.map(name => Future(name -> invokeMlSimulator(backend, s"$name$postfix", debug)))
|
||||
Await.result(Future.sequence(subresults), Duration.Inf)
|
||||
}
|
||||
results.flatten.foreach { case (name, exitcode) =>
|
||||
assert(exitcode == 0, s"Failed $name")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ignore should s"pass $backend"
|
||||
}
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
runTest(backend, debug, "rv64ui-p-simple", Seq(s"""EXTRA_SIM_ARGS=+trace-humanreadable0"""))
|
||||
runSuite(backend, debug)(benchmarks)
|
||||
}
|
||||
|
||||
// Checks the collected trace log matches the behavior of a chisel printf
|
||||
def diffTracelog(verilatedLog: String) {
|
||||
behavior.of("captured instruction trace")
|
||||
it should s"match the chisel printf in ${verilatedLog}" in {
|
||||
def getLines(file: File): Seq[String] = Source.fromFile(file).getLines.toList
|
||||
|
||||
val printfPrefix = "TRACEPORT 0: "
|
||||
val verilatedOutput = getLines(new File(outDir, s"/${verilatedLog}")).collect({
|
||||
case line if line.startsWith(printfPrefix) => line.stripPrefix(printfPrefix)
|
||||
})
|
||||
|
||||
// Last bit indicates the core was under reset; reject those tokens
|
||||
// Tail to drop the first token which is initialized in the channel
|
||||
val synthPrintOutput = getLines(new File(genDir, s"/TRACEFILE")).tail.filter(line => (line.last.toInt & 1) == 0)
|
||||
|
||||
assert(
|
||||
math.abs(verilatedOutput.size - synthPrintOutput.size) <= 1,
|
||||
s"\nPrintf Length: ${verilatedOutput.size}, Trace Length: ${synthPrintOutput.size}",
|
||||
)
|
||||
assert(verilatedOutput.nonEmpty)
|
||||
for ((vPrint, sPrint) <- verilatedOutput.zip(synthPrintOutput)) {
|
||||
assert(vPrint == sPrint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mkdirs
|
||||
behavior.of(s"Tuple: ${targetTuple}")
|
||||
elaborateAndCompile()
|
||||
runTest("verilator", "rv64ui-p-simple", false, Seq(s"""EXTRA_SIM_ARGS=+trace-humanreadable0"""))
|
||||
runSuite("verilator")(benchmarks)
|
||||
}
|
||||
|
||||
class SimpleRocketF1Tests
|
||||
|
@ -178,7 +141,6 @@ class CVA6F1Tests
|
|||
BaseConfigs.F1,
|
||||
)
|
||||
|
||||
// This test suite only mirrors what is run in CI. CI invokes each test individually, using a testOnly call.
|
||||
class CITests
|
||||
extends Suites(
|
||||
new SimpleRocketF1Tests,
|
||||
|
|
|
@ -11,18 +11,21 @@ class AssertModuleF1Test extends TutorialSuite("AssertModule")
|
|||
class MulticlockAssertF1Test extends TutorialSuite("MulticlockAssertModule")
|
||||
|
||||
class AssertTortureTest extends TutorialSuite("AssertTorture") with AssertTortureConstants {
|
||||
def checkClockDomainAssertionOrder(clockIdx: Int): Unit = {
|
||||
it should s"capture asserts in the same order as the reference printfs in clock domain $clockIdx" in {
|
||||
val verilatedLogFile = new File(outDir, s"/${targetName}.verilator.out")
|
||||
// Diff parts of the simulation's stdout against itself, as the synthesized
|
||||
// assertion messages are dumped to the same file as printfs in the RTL
|
||||
val expected = extractLines(verilatedLogFile, prefix = s"${printfPrefix}${clockPrefix(clockIdx)}")
|
||||
val actual = extractLines(verilatedLogFile, prefix = s"Assertion failed: ${clockPrefix(clockIdx)}")
|
||||
diffLines(expected, actual)
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "run in the simulator" in {
|
||||
assert(run(backend, debug, args = simulationArgs) == 0)
|
||||
}
|
||||
for (clockIdx <- 0 until 4) {
|
||||
it should s"capture asserts in the same order as the reference printfs in clock domain $clockIdx" in {
|
||||
val logFile = new File(outDir, s"/${targetName}.${backend}.out")
|
||||
// Diff parts of the simulation's stdout against itself, as the synthesized
|
||||
// assertion messages are dumped to the same file as printfs in the RTL
|
||||
val expected = extractLines(logFile, prefix = s"${printfPrefix}${clockPrefix(clockIdx)}")
|
||||
val actual = extractLines(logFile, prefix = s"Assertion failed: ${clockPrefix(clockIdx)}")
|
||||
diffLines(expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Create a target-parameters instance we can inspect here
|
||||
Seq.tabulate(4)(i => checkClockDomainAssertionOrder(i))
|
||||
}
|
||||
|
||||
class AssertGlobalResetConditionF1Test extends TutorialSuite("AssertGlobalResetCondition")
|
||||
|
|
|
@ -11,6 +11,7 @@ import firesim.BasePlatformConfig
|
|||
|
||||
abstract class AutoCounterSuite(
|
||||
targetName: String,
|
||||
checks: Seq[(String, String)],
|
||||
targetConfigs: String = "NoConfig",
|
||||
platformConfigs: Seq[Class[_ <: Config]] = Seq(),
|
||||
basePlatformConfig: BasePlatformConfig = BaseConfigs.F1,
|
||||
|
@ -19,7 +20,7 @@ abstract class AutoCounterSuite(
|
|||
|
||||
/** Compares an AutoCounter output CSV against a reference generated using in-circuit printfs.
|
||||
*/
|
||||
def checkAutoCounterCSV(filename: String, stdoutPrefix: String) {
|
||||
def checkAutoCounterCSV(backend: String, filename: String, stdoutPrefix: String) {
|
||||
it should s"produce a csv file (${filename}) that matches in-circuit printf output" in {
|
||||
val scrubWhitespace = raw"\s*(.*)\s*".r
|
||||
def splitAtCommas(s: String) = {
|
||||
|
@ -32,7 +33,7 @@ abstract class AutoCounterSuite(
|
|||
.map(scrubWhitespace.findFirstMatchIn(_).get.group(1))
|
||||
}
|
||||
|
||||
val refLogFile = new File(outDir, s"/${targetName}.${backendSimulator}.out")
|
||||
val refLogFile = new File(outDir, s"/${targetName}.${backend}.out")
|
||||
val acFile = new File(genDir, s"/${filename}")
|
||||
|
||||
val refVersion :: refClockInfo :: refLabelLine :: refDescLine :: refOutput =
|
||||
|
@ -65,35 +66,54 @@ abstract class AutoCounterSuite(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "run in the simulator" in {
|
||||
assert(run(backend, debug, args = simulationArgs) == 0)
|
||||
}
|
||||
for ((filename, prefix) <- checks) {
|
||||
checkAutoCounterCSV(backend, filename, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoCounterModuleF1Test
|
||||
extends AutoCounterSuite(
|
||||
"AutoCounterModule",
|
||||
Seq(("autocounter0.csv", "AUTOCOUNTER_PRINT ")),
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
) {
|
||||
checkAutoCounterCSV("autocounter0.csv", "AUTOCOUNTER_PRINT ")
|
||||
}
|
||||
)
|
||||
|
||||
class AutoCounter32bRolloverTest
|
||||
extends AutoCounterSuite(
|
||||
"AutoCounter32bRollover",
|
||||
Seq(("autocounter0.csv", "AUTOCOUNTER_PRINT ")),
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
) {
|
||||
checkAutoCounterCSV("autocounter0.csv", "AUTOCOUNTER_PRINT ")
|
||||
}
|
||||
)
|
||||
|
||||
class AutoCounterCoverModuleF1Test
|
||||
extends AutoCounterSuite(
|
||||
"AutoCounterCoverModule",
|
||||
Seq(("autocounter0.csv", "AUTOCOUNTER_PRINT ")),
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
) {
|
||||
checkAutoCounterCSV("autocounter0.csv", "AUTOCOUNTER_PRINT ")
|
||||
}
|
||||
)
|
||||
|
||||
class MulticlockAutoCounterF1Test
|
||||
extends AutoCounterSuite(
|
||||
"MulticlockAutoCounterModule",
|
||||
Seq(
|
||||
("autocounter0.csv", "AUTOCOUNTER_PRINT "),
|
||||
("autocounter1.csv", "AUTOCOUNTER_PRINT_SLOWCLOCK "),
|
||||
),
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
)
|
||||
|
||||
class AutoCounterGlobalResetConditionF1Test
|
||||
extends AutoCounterSuite(
|
||||
extends TutorialSuite(
|
||||
"AutoCounterGlobalResetCondition",
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
) {
|
||||
|
||||
def assertCountsAreZero(filename: String, clockDivision: Int) {
|
||||
s"Counts reported in ${filename}" should "always be zero" in {
|
||||
val log = new File(genDir, s"/${filename}")
|
||||
|
@ -110,17 +130,14 @@ class AutoCounterGlobalResetConditionF1Test
|
|||
}
|
||||
}
|
||||
}
|
||||
assertCountsAreZero("autocounter0.csv", clockDivision = 1)
|
||||
assertCountsAreZero("autocounter1.csv", clockDivision = 2)
|
||||
}
|
||||
|
||||
class MulticlockAutoCounterF1Test
|
||||
extends AutoCounterSuite(
|
||||
"MulticlockAutoCounterModule",
|
||||
simulationArgs = Seq("+autocounter-readrate=1000", "+autocounter-filename-base=autocounter"),
|
||||
) {
|
||||
checkAutoCounterCSV("autocounter0.csv", "AUTOCOUNTER_PRINT ")
|
||||
checkAutoCounterCSV("autocounter1.csv", "AUTOCOUNTER_PRINT_SLOWCLOCK ")
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "run in the simulator" in {
|
||||
assert(run(backend, debug, args = simulationArgs) == 0)
|
||||
}
|
||||
assertCountsAreZero("autocounter0.csv", clockDivision = 1)
|
||||
assertCountsAreZero("autocounter1.csv", clockDivision = 2)
|
||||
}
|
||||
}
|
||||
|
||||
class AutoCounterCITests
|
||||
|
|
|
@ -10,35 +10,35 @@ class GCDF1Test extends TutorialSuite("GCD", basePlatformConfig = BaseConfigs
|
|||
class GCDVitisTest extends TutorialSuite("GCD", basePlatformConfig = BaseConfigs.Vitis)
|
||||
|
||||
// Hijack Parity to test all of the Midas-level backends
|
||||
class ParityF1Test extends TutorialSuite("Parity", basePlatformConfig = BaseConfigs.F1) {
|
||||
runTest("verilator", true)
|
||||
runTest("vcs", true)
|
||||
}
|
||||
class ParityVitisTest extends TutorialSuite("Parity", basePlatformConfig = BaseConfigs.Vitis) {
|
||||
runTest("verilator", true)
|
||||
runTest("vcs", true)
|
||||
}
|
||||
class ParityF1Test extends TutorialSuite("Parity", basePlatformConfig = BaseConfigs.F1)
|
||||
class ParityVitisTest extends TutorialSuite("Parity", basePlatformConfig = BaseConfigs.Vitis)
|
||||
|
||||
class CustomConstraintsF1Test extends TutorialSuite("CustomConstraints") {
|
||||
def readLines(filename: String): List[String] = {
|
||||
val file = new File(genDir, s"/${filename}")
|
||||
Source.fromFile(file).getLines.toList
|
||||
}
|
||||
it should s"generate synthesis XDC file" in {
|
||||
val xdc = readLines("FireSim-generated.synthesis.xdc")
|
||||
xdc should contain("constrain_synth1")
|
||||
(atLeast(1, xdc) should fullyMatch).regex("constrain_synth2 \\[reg firesim_top/.*/dut/r0\\]".r)
|
||||
}
|
||||
it should s"generate implementation XDC file" in {
|
||||
val xdc = readLines("FireSim-generated.implementation.xdc")
|
||||
xdc should contain("constrain_impl1")
|
||||
(atLeast(1, xdc) should fullyMatch).regex("constrain_impl2 \\[reg WRAPPER_INST/CL/firesim_top/.*/dut/r1]".r)
|
||||
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
def readLines(filename: String): List[String] = {
|
||||
val file = new File(genDir, s"/${filename}")
|
||||
Source.fromFile(file).getLines.toList
|
||||
}
|
||||
it should s"generate synthesis XDC file" in {
|
||||
val xdc = readLines("FireSim-generated.synthesis.xdc")
|
||||
xdc should contain("constrain_synth1")
|
||||
(atLeast(1, xdc) should fullyMatch).regex("constrain_synth2 \\[reg firesim_top/.*/dut/r0\\]".r)
|
||||
}
|
||||
it should s"generate implementation XDC file" in {
|
||||
val xdc = readLines("FireSim-generated.implementation.xdc")
|
||||
xdc should contain("constrain_impl1")
|
||||
(atLeast(1, xdc) should fullyMatch).regex("constrain_impl2 \\[reg WRAPPER_INST/CL/firesim_top/.*/dut/r1]".r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TerminationF1Test extends TutorialSuite("TerminationModule") {
|
||||
(1 to 10).foreach { x =>
|
||||
runTest(backendSimulator, args = Seq("+termination-bridge-tick-rate=10", s"+fuzz-seed=${x}"), shouldPass = true)
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
(1 to 10).foreach { x =>
|
||||
val args = Seq("+termination-bridge-tick-rate=10", s"+fuzz-seed=${x}")
|
||||
assert(run(backend, debug, args = args) == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,15 +11,20 @@ import firesim.BasePlatformConfig
|
|||
|
||||
abstract class FMRSuite(
|
||||
targetName: String,
|
||||
expectedValue: Double,
|
||||
error: Double = 0.0,
|
||||
targetConfigs: String = "NoConfig",
|
||||
platformConfigs: Seq[Class[_ <: Config]] = Seq(),
|
||||
basePlatformConfig: BasePlatformConfig = BaseConfigs.F1,
|
||||
simulationArgs: Seq[String] = Seq(),
|
||||
) extends TutorialSuite(targetName, targetConfigs, platformConfigs, basePlatformConfig, simulationArgs) {
|
||||
// Checks that a bridge generated log in ${genDir}/${synthLog} is empty
|
||||
def expectedFMR(expectedValue: Double, error: Double = 0.0) {
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "run in the simulator" in {
|
||||
assert(run(backend, debug, args = simulationArgs) == 0)
|
||||
}
|
||||
|
||||
it should s"run with an FMR between ${expectedValue - error} and ${expectedValue + error}" in {
|
||||
val verilatedLogFile = new File(outDir, s"/${targetName}.${backendSimulator}.out")
|
||||
val verilatedLogFile = new File(outDir, s"/${targetName}.${backend}.out")
|
||||
val lines = Source.fromFile(verilatedLogFile).getLines.toList.reverse
|
||||
val fmrRegex = raw"^FMR: (\d*\.\d*)".r
|
||||
val fmr = lines.collectFirst { case fmrRegex(value) =>
|
||||
|
@ -32,28 +37,21 @@ abstract class FMRSuite(
|
|||
}
|
||||
}
|
||||
|
||||
class MultiRegfileFMRF1Test extends FMRSuite("MultiRegfileFMR") {
|
||||
class MultiRegfileFMRF1Test extends FMRSuite("MultiRegfileFMR", expectedValue = 2.0 * MultiRegfile.nCopiesToTime) {
|
||||
// A threaded model that relies on another model to implement an internal
|
||||
// combinational path (like an extracted memory model) will only simulate
|
||||
// one target thread-cycle every two host cycles.
|
||||
expectedFMR(2.0 * MultiRegfile.nCopiesToTime)
|
||||
}
|
||||
|
||||
class MultiSRAMFMRF1Test extends FMRSuite("MultiSRAMFMR") {
|
||||
expectedFMR(MultiRegfile.nCopiesToTime) // No comb paths -> 1:1
|
||||
class MultiSRAMFMRF1Test extends FMRSuite("MultiSRAMFMR", expectedValue = MultiRegfile.nCopiesToTime) {
|
||||
// No comb paths -> 1:1
|
||||
}
|
||||
|
||||
class PassthroughModelTest extends FMRSuite("PassthroughModel") {
|
||||
expectedFMR(2.0)
|
||||
}
|
||||
class PassthroughModelTest extends FMRSuite("PassthroughModel", expectedValue = 2.0)
|
||||
|
||||
class PassthroughModelNestedTest extends FMRSuite("PassthroughModelNested") {
|
||||
expectedFMR(2.0)
|
||||
}
|
||||
class PassthroughModelNestedTest extends FMRSuite("PassthroughModelNested", expectedValue = 2.0)
|
||||
|
||||
class PassthroughModelBridgeSourceTest extends FMRSuite("PassthroughModelBridgeSource") {
|
||||
expectedFMR(1.0)
|
||||
}
|
||||
class PassthroughModelBridgeSourceTest extends FMRSuite("PassthroughModelBridgeSource", expectedValue = 1.0)
|
||||
|
||||
class FMRCITests
|
||||
extends Suites(
|
||||
|
|
|
@ -5,10 +5,7 @@ package firesim.midasexamples
|
|||
import org.scalatest.Suites
|
||||
|
||||
class WireInterconnectF1Test extends TutorialSuite("WireInterconnect")
|
||||
class TrivialMulticlockF1Test extends TutorialSuite("TrivialMulticlock") {
|
||||
runTest("verilator", true)
|
||||
runTest("vcs", true)
|
||||
}
|
||||
class TrivialMulticlockF1Test extends TutorialSuite("TrivialMulticlock")
|
||||
|
||||
class TriggerWiringModuleF1Test extends TutorialSuite("TriggerWiringModule")
|
||||
|
||||
|
|
|
@ -15,31 +15,9 @@ import firesim.{BasePlatformConfig, TestSuiteCommon}
|
|||
|
||||
abstract class LoadMemTest(
|
||||
override val basePlatformConfig: BasePlatformConfig
|
||||
) extends TestSuiteCommon("midasexamples")
|
||||
) extends TutorialSuite("LoadMemModule", platformConfigs = Seq(classOf[NoSynthAsserts]))
|
||||
with Matchers {
|
||||
|
||||
override def targetConfigs = "NoConfig"
|
||||
override def targetName = "LoadMemModule"
|
||||
override def platformConfigs = Seq(classOf[NoSynthAsserts])
|
||||
|
||||
def run(
|
||||
backend: String,
|
||||
debug: Boolean = false,
|
||||
logFile: Option[File] = None,
|
||||
waveform: Option[File] = None,
|
||||
args: Seq[String] = Nil,
|
||||
) = {
|
||||
val makeArgs = Seq(
|
||||
s"run-$backend%s".format(if (debug) "-debug" else ""),
|
||||
"LOGFILE=%s".format(logFile.map(toStr).getOrElse("")),
|
||||
"WAVEFORM=%s".format(waveform.map(toStr).getOrElse("")),
|
||||
"ARGS=%s".format(args.mkString(" ")),
|
||||
)
|
||||
if (isCmdAvailable(backend)) {
|
||||
make(makeArgs: _*)
|
||||
} else 0
|
||||
}
|
||||
|
||||
/** Helper to generate tests strings.
|
||||
*/
|
||||
def getTestInput(length: Int): String = {
|
||||
|
@ -59,50 +37,33 @@ abstract class LoadMemTest(
|
|||
* @param debug
|
||||
* When true, captures waves from the simulation
|
||||
*/
|
||||
def runTest(backend: String, debug: Boolean) {
|
||||
// Generate a random string spanning 2 sectors with a fixed seed.
|
||||
val numLines = 128
|
||||
val data = getTestInput(numLines)
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "read data provided by LoadMem" in {
|
||||
// Generate a random string spanning 2 sectors with a fixed seed.
|
||||
val numLines = 128
|
||||
val data = getTestInput(numLines)
|
||||
|
||||
// Create an input file.
|
||||
val input = File.createTempFile("input", ".txt")
|
||||
//input.deleteOnExit()
|
||||
val inputWriter = new BufferedWriter(new FileWriter(input))
|
||||
inputWriter.write(data)
|
||||
inputWriter.flush()
|
||||
inputWriter.close()
|
||||
// Create an input file.
|
||||
val input = File.createTempFile("input", ".txt")
|
||||
input.deleteOnExit()
|
||||
val inputWriter = new BufferedWriter(new FileWriter(input))
|
||||
inputWriter.write(data)
|
||||
inputWriter.flush()
|
||||
inputWriter.close()
|
||||
|
||||
// Pre-allocate space in the output.
|
||||
val output = File.createTempFile("output", ".txt")
|
||||
//output.deleteOnExit()
|
||||
val outputWriter = new BufferedWriter(new FileWriter(output))
|
||||
for (i <- 1 to data.size) {
|
||||
outputWriter.write('x')
|
||||
}
|
||||
outputWriter.flush()
|
||||
outputWriter.close()
|
||||
// Create an output file.
|
||||
val output = File.createTempFile("output", ".txt")
|
||||
|
||||
val runResult =
|
||||
run(backend, debug, args = Seq(s"+n=${numLines} +loadmem=${input.getPath}", s"+test-dump-file=${output.getPath}"))
|
||||
assert(runResult == 0)
|
||||
val result = scala.io.Source.fromFile(output.getPath).mkString
|
||||
result should equal(data + "\n")
|
||||
}
|
||||
|
||||
mkdirs()
|
||||
behavior.of(s"$targetName")
|
||||
elaborateAndCompile()
|
||||
for (backend <- Seq("vcs", "verilator")) {
|
||||
compileMlSimulator(backend)
|
||||
|
||||
val testEnvStr = s"pass in ${backend} MIDAS-level simulation"
|
||||
|
||||
if (isCmdAvailable(backend)) {
|
||||
it should testEnvStr in {
|
||||
runTest(backend, false)
|
||||
}
|
||||
} else {
|
||||
ignore should testEnvStr in {}
|
||||
// Run the test and compare the two files.
|
||||
assert(
|
||||
run(
|
||||
backend,
|
||||
debug,
|
||||
args = Seq(s"+n=${numLines} +loadmem=${input.getPath}", s"+test-dump-file=${output.getPath}"),
|
||||
) == 0
|
||||
)
|
||||
val result = scala.io.Source.fromFile(output.getPath).mkString
|
||||
result should equal(data + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,29 +14,34 @@ trait PlusArgsKey {
|
|||
class PlusArgsGroup68Bit
|
||||
extends TutorialSuite("PlusArgsModule", "PlusArgsModuleTestConfigGroup68Bit")
|
||||
with PlusArgsKey {
|
||||
it should "provide the correct default value, 3 slice" in {
|
||||
assert(run("verilator", false, args = Seq(getKey(0, 0))) == 0)
|
||||
}
|
||||
|
||||
it should "accept an int from the command line" in {
|
||||
assert(run("verilator", false, args = Seq(s"+plusar_v=3", getKey(0, 1))) == 0)
|
||||
assert(run("verilator", false, args = Seq(s"+plusar_v=${BigInt("f00000000", 16)}", getKey(0, 2))) == 0)
|
||||
assert(run("verilator", false, args = Seq(s"+plusar_v=${BigInt("f0000000000000000", 16)}", getKey(0, 3))) == 0)
|
||||
}
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "provide the correct default value, 3 slice" in {
|
||||
assert(run(backend, debug, args = Seq(getKey(0, 0))) == 0)
|
||||
}
|
||||
|
||||
it should "reject large runtime values" in {
|
||||
assert(run("verilator", false, args = Seq(s"+plusar_v=${BigInt("ff0000000000000000", 16)}", getKey(0, 4))) != 0)
|
||||
it should "accept an int from the command line" in {
|
||||
assert(run(backend, debug, args = Seq(s"+plusar_v=3", getKey(0, 1))) == 0)
|
||||
assert(run(backend, debug, args = Seq(s"+plusar_v=${BigInt("f00000000", 16)}", getKey(0, 2))) == 0)
|
||||
assert(run(backend, debug, args = Seq(s"+plusar_v=${BigInt("f0000000000000000", 16)}", getKey(0, 3))) == 0)
|
||||
}
|
||||
|
||||
it should "reject large runtime values" in {
|
||||
assert(run(backend, debug, args = Seq(s"+plusar_v=${BigInt("ff0000000000000000", 16)}", getKey(0, 4))) != 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PlusArgsGroup29Bit
|
||||
extends TutorialSuite("PlusArgsModule", "PlusArgsModuleTestConfigGroup29Bit")
|
||||
with PlusArgsKey {
|
||||
it should "provide the correct default value, 1 slice" in {
|
||||
assert(run("verilator", false, args = Seq(getKey(1, 0))) == 0)
|
||||
}
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "provide the correct default value, 1 slice" in {
|
||||
assert(run(backend, debug, args = Seq(getKey(1, 0))) == 0)
|
||||
}
|
||||
|
||||
it should "accept an int from the command line, 1 slice" in {
|
||||
assert(run("verilator", false, args = Seq(s"+plusar_v=${BigInt("1eadbeef", 16)}", getKey(1, 1))) == 0)
|
||||
it should "accept an int from the command line, 1 slice" in {
|
||||
assert(run(backend, debug, args = Seq(s"+plusar_v=${BigInt("1eadbeef", 16)}", getKey(1, 1))) == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,19 +21,17 @@ abstract class PrintfSuite(
|
|||
* target-side cycle prefix
|
||||
*/
|
||||
def checkPrintCycles(filename: String, startCycle: Int, endCycle: Int, linesPerCycle: Int) {
|
||||
it should "have synthesized printfs in the desired cycles" in {
|
||||
val synthLogFile = new File(genDir, s"/${filename}")
|
||||
val synthPrintOutput = extractLines(synthLogFile, prefix = "")
|
||||
val length = synthPrintOutput.size
|
||||
assert(length == linesPerCycle * (endCycle - startCycle + 1))
|
||||
for ((line, idx) <- synthPrintOutput.zipWithIndex) {
|
||||
val currentCycle = idx / linesPerCycle + startCycle
|
||||
val printRegex = raw"^CYCLE:\s*(\d*) SYNTHESIZED_PRINT CYCLE:\s*(\d*).*".r
|
||||
line match {
|
||||
case printRegex(cycleA, cycleB) =>
|
||||
assert(cycleA.toInt == currentCycle)
|
||||
assert(cycleB.toInt == currentCycle)
|
||||
}
|
||||
val synthLogFile = new File(genDir, s"/${filename}")
|
||||
val synthPrintOutput = extractLines(synthLogFile, prefix = "")
|
||||
val length = synthPrintOutput.size
|
||||
assert(length == linesPerCycle * (endCycle - startCycle + 1))
|
||||
for ((line, idx) <- synthPrintOutput.zipWithIndex) {
|
||||
val currentCycle = idx / linesPerCycle + startCycle
|
||||
val printRegex = raw"^CYCLE:\s*(\d*) SYNTHESIZED_PRINT CYCLE:\s*(\d*).*".r
|
||||
line match {
|
||||
case printRegex(cycleA, cycleB) =>
|
||||
assert(cycleA.toInt == currentCycle)
|
||||
assert(cycleB.toInt == currentCycle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,26 +39,27 @@ abstract class PrintfSuite(
|
|||
// Checks that a bridge generated log in ${genDir}/${synthLog} matches output
|
||||
// generated directly by the RTL simulator (usually with printfs)
|
||||
def diffSynthesizedLog(
|
||||
backend: String,
|
||||
synthLog: String,
|
||||
stdoutPrefix: String = "SYNTHESIZED_PRINT ",
|
||||
synthPrefix: String = "SYNTHESIZED_PRINT ",
|
||||
synthLinesToDrop: Int = 0,
|
||||
) {
|
||||
behavior.of(s"${synthLog}")
|
||||
it should "match the prints generated by the verilated design" in {
|
||||
val verilatedLogFile = new File(outDir, s"/${targetName}.${backendSimulator}.out")
|
||||
val synthLogFile = new File(genDir, s"/${synthLog}")
|
||||
val verilatedOutput = extractLines(verilatedLogFile, stdoutPrefix).sorted
|
||||
val synthPrintOutput = extractLines(synthLogFile, synthPrefix, synthLinesToDrop).sorted
|
||||
diffLines(verilatedOutput, synthPrintOutput)
|
||||
}
|
||||
val verilatedLogFile = new File(outDir, s"/${targetName}.${backend}.out")
|
||||
val synthLogFile = new File(genDir, s"/${synthLog}")
|
||||
val verilatedOutput = extractLines(verilatedLogFile, stdoutPrefix).sorted
|
||||
val synthPrintOutput = extractLines(synthLogFile, synthPrefix, synthLinesToDrop).sorted
|
||||
diffLines(verilatedOutput, synthPrintOutput)
|
||||
}
|
||||
|
||||
def assertSynthesizedLogEmpty(synthLog: String) {
|
||||
s"${synthLog}" should "be empty" in {
|
||||
val synthLogFile = new File(genDir, s"/${synthLog}")
|
||||
val lines = extractLines(synthLogFile, prefix = "")
|
||||
assert(lines.isEmpty)
|
||||
def addChecks(backend: String): Unit
|
||||
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "run in the simulator" in {
|
||||
assert(run(backend, debug, args = simulationArgs) == 0)
|
||||
}
|
||||
it should "should produce the expected output" in {
|
||||
addChecks(backend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,8 +70,9 @@ abstract class PrintModuleTest(val platform: BasePlatformConfig)
|
|||
simulationArgs = Seq("+print-no-cycle-prefix", "+print-file=synthprinttest.out"),
|
||||
basePlatformConfig = platform,
|
||||
) {
|
||||
runTest("vcs", true)
|
||||
diffSynthesizedLog("synthprinttest.out0")
|
||||
override def addChecks(backend: String) {
|
||||
diffSynthesizedLog(backend, "synthprinttest.out0")
|
||||
}
|
||||
}
|
||||
|
||||
class PrintfModuleF1Test extends PrintModuleTest(BaseConfigs.F1)
|
||||
|
@ -84,7 +84,9 @@ abstract class NarrowPrintfModuleTest(val platform: BasePlatformConfig)
|
|||
simulationArgs = Seq("+print-no-cycle-prefix", "+print-file=synthprinttest.out"),
|
||||
basePlatformConfig = platform,
|
||||
) {
|
||||
diffSynthesizedLog("synthprinttest.out0")
|
||||
override def addChecks(backend: String) {
|
||||
diffSynthesizedLog(backend, "synthprinttest.out0")
|
||||
}
|
||||
}
|
||||
|
||||
class NarrowPrintfModuleF1Test extends NarrowPrintfModuleTest(BaseConfigs.F1)
|
||||
|
@ -96,58 +98,79 @@ abstract class MulticlockPrintfModuleTest(val platform: BasePlatformConfig)
|
|||
simulationArgs = Seq("+print-file=synthprinttest.out", "+print-no-cycle-prefix"),
|
||||
basePlatformConfig = platform,
|
||||
) {
|
||||
diffSynthesizedLog("synthprinttest.out0")
|
||||
diffSynthesizedLog(
|
||||
"synthprinttest.out1",
|
||||
stdoutPrefix = "SYNTHESIZED_PRINT_HALFRATE ",
|
||||
synthPrefix = "SYNTHESIZED_PRINT_HALFRATE ",
|
||||
// Corresponds to a single cycle of extra output.
|
||||
synthLinesToDrop = 4,
|
||||
)
|
||||
override def addChecks(backend: String) {
|
||||
diffSynthesizedLog(backend, "synthprinttest.out0")
|
||||
diffSynthesizedLog(
|
||||
backend,
|
||||
"synthprinttest.out1",
|
||||
stdoutPrefix = "SYNTHESIZED_PRINT_HALFRATE ",
|
||||
synthPrefix = "SYNTHESIZED_PRINT_HALFRATE ",
|
||||
// Corresponds to a single cycle of extra output.
|
||||
synthLinesToDrop = 4,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MulticlockPrintF1Test extends MulticlockPrintfModuleTest(BaseConfigs.F1)
|
||||
class MulticlockPrintVitisTest extends MulticlockPrintfModuleTest(BaseConfigs.Vitis)
|
||||
|
||||
class PrintfCycleBoundsF1Test extends PrintfCycleBoundsTestBase(startCycle = 172, endCycle = 9377)
|
||||
class AutoCounterPrintfF1Test
|
||||
extends PrintfSuite(
|
||||
"AutoCounterPrintfModule",
|
||||
simulationArgs = Seq("+print-file=synthprinttest.out"),
|
||||
platformConfigs = Seq(classOf[AutoCounterPrintf]),
|
||||
) {
|
||||
override def addChecks(backend: String) {
|
||||
diffSynthesizedLog(backend, "synthprinttest.out0", stdoutPrefix = "AUTOCOUNTER_PRINT CYCLE", synthPrefix = "CYCLE")
|
||||
}
|
||||
}
|
||||
|
||||
class TriggerPredicatedPrintfF1Test
|
||||
extends PrintfSuite("TriggerPredicatedPrintf", simulationArgs = Seq("+print-file=synthprinttest.out"))
|
||||
with TriggerPredicatedPrintfConsts {
|
||||
val startCycle = assertTriggerCycle + 2
|
||||
val endCycle = deassertTriggerCycle + 2
|
||||
checkPrintCycles("synthprinttest.out0", startCycle, endCycle, linesPerCycle = 2)
|
||||
extends PrintfSuite("TriggerPredicatedPrintf", simulationArgs = Seq("+print-file=synthprinttest.out")) {
|
||||
override def addChecks(backend: String) {
|
||||
import TriggerPredicatedPrintfConsts._
|
||||
checkPrintCycles("synthprinttest.out0", assertTriggerCycle + 2, deassertTriggerCycle + 2, linesPerCycle = 2)
|
||||
}
|
||||
}
|
||||
|
||||
class PrintfCycleBoundsTestBase(startCycle: Int, endCycle: Int)
|
||||
extends PrintfSuite(
|
||||
"PrintfModule",
|
||||
simulationArgs = Seq(
|
||||
simulationArgs = Seq(
|
||||
"+print-file=synthprinttest.out",
|
||||
s"+print-start=${startCycle}",
|
||||
s"+print-end=${endCycle}",
|
||||
),
|
||||
) {
|
||||
checkPrintCycles("synthprinttest.out0", startCycle, endCycle, linesPerCycle = 4)
|
||||
override def addChecks(backend: String) {
|
||||
checkPrintCycles("synthprinttest.out0", startCycle, endCycle, linesPerCycle = 4)
|
||||
}
|
||||
}
|
||||
|
||||
class PrintfCycleBoundsF1Test extends PrintfCycleBoundsTestBase(startCycle = 172, endCycle = 9377)
|
||||
|
||||
class PrintfGlobalResetConditionTest
|
||||
extends PrintfSuite(
|
||||
extends TutorialSuite(
|
||||
"PrintfGlobalResetCondition",
|
||||
simulationArgs = Seq("+print-no-cycle-prefix", "+print-file=synthprinttest.out"),
|
||||
) {
|
||||
// The log should be empty.
|
||||
assertSynthesizedLogEmpty("synthprinttest.out0")
|
||||
assertSynthesizedLogEmpty("synthprinttest.out1")
|
||||
}
|
||||
|
||||
class AutoCounterPrintfF1Test
|
||||
extends PrintfSuite(
|
||||
"AutoCounterPrintfModule",
|
||||
simulationArgs = Seq("+print-file=synthprinttest.out"),
|
||||
platformConfigs = Seq(classOf[AutoCounterPrintf]),
|
||||
) {
|
||||
diffSynthesizedLog("synthprinttest.out0", stdoutPrefix = "AUTOCOUNTER_PRINT CYCLE", synthPrefix = "CYCLE")
|
||||
def assertSynthesizedLogEmpty(synthLog: String) {
|
||||
s"${synthLog}" should "be empty" in {
|
||||
val synthLogFile = new File(genDir, s"/${synthLog}")
|
||||
val lines = extractLines(synthLogFile, prefix = "")
|
||||
assert(lines.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
it should "run in the simulator" in {
|
||||
assert(run(backend, debug, args = simulationArgs) == 0)
|
||||
}
|
||||
// The log should be empty.
|
||||
assertSynthesizedLogEmpty("synthprinttest.out0")
|
||||
assertSynthesizedLogEmpty("synthprinttest.out1")
|
||||
}
|
||||
}
|
||||
|
||||
class PrintfSynthesisCITests
|
||||
|
|
|
@ -2,19 +2,24 @@
|
|||
|
||||
package firesim.midasexamples
|
||||
|
||||
import org.scalatest.Suites
|
||||
|
||||
class ResetPulseBridgeActiveHighTest
|
||||
extends TutorialSuite(
|
||||
"ResetPulseBridgeTest",
|
||||
// Disable assertion synthesis to rely on native chisel assertions to catch bad behavior
|
||||
platformConfigs = Seq(classOf[NoSynthAsserts]),
|
||||
simulationArgs = Seq(s"+reset-pulse-length0=${ResetPulseBridgeTestConsts.maxPulseLength}"),
|
||||
) {
|
||||
runTest(
|
||||
backendSimulator,
|
||||
args = Seq(s"+reset-pulse-length0=${ResetPulseBridgeTestConsts.maxPulseLength + 1}"),
|
||||
shouldPass = false,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
class ResetPulseBridgeActiveHighFailTest
|
||||
extends TutorialSuite(
|
||||
"ResetPulseBridgeTest",
|
||||
// Disable assertion synthesis to rely on native chisel assertions to catch bad behavior
|
||||
platformConfigs = Seq(classOf[NoSynthAsserts]),
|
||||
simulationArgs = Seq(s"+reset-pulse-length0=${ResetPulseBridgeTestConsts.maxPulseLength + 1}"),
|
||||
shouldPass = false,
|
||||
)
|
||||
|
||||
class ResetPulseBridgeActiveLowTest
|
||||
extends TutorialSuite(
|
||||
|
@ -22,10 +27,21 @@ class ResetPulseBridgeActiveLowTest
|
|||
targetConfigs = "ResetPulseBridgeActiveLowConfig",
|
||||
platformConfigs = Seq(classOf[NoSynthAsserts]),
|
||||
simulationArgs = Seq(s"+reset-pulse-length0=${ResetPulseBridgeTestConsts.maxPulseLength}"),
|
||||
) {
|
||||
runTest(
|
||||
backendSimulator,
|
||||
args = Seq(s"+reset-pulse-length0=${ResetPulseBridgeTestConsts.maxPulseLength + 1}"),
|
||||
shouldPass = false,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
class ResetPulseBridgeActiveLowFailTest
|
||||
extends TutorialSuite(
|
||||
"ResetPulseBridgeTest",
|
||||
targetConfigs = "ResetPulseBridgeActiveLowConfig",
|
||||
platformConfigs = Seq(classOf[NoSynthAsserts]),
|
||||
simulationArgs = Seq(s"+reset-pulse-length0=${ResetPulseBridgeTestConsts.maxPulseLength + 1}"),
|
||||
shouldPass = false,
|
||||
)
|
||||
|
||||
class ResetPulseBridgeTests
|
||||
extends Suites(
|
||||
new ResetPulseBridgeActiveHighTest,
|
||||
new ResetPulseBridgeActiveHighFailTest,
|
||||
new ResetPulseBridgeActiveLowTest,
|
||||
new ResetPulseBridgeActiveLowFailTest,
|
||||
)
|
||||
|
|
|
@ -9,94 +9,62 @@ import org.scalatest.Suites
|
|||
import org.scalatest.matchers.should._
|
||||
|
||||
import freechips.rocketchip.config.Config
|
||||
import firesim.{TestSuiteCommon, BasePlatformConfig}
|
||||
import firesim.{BasePlatformConfig, TestSuiteCommon}
|
||||
|
||||
object BaseConfigs {
|
||||
case object F1 extends BasePlatformConfig("f1", Seq(classOf[DefaultF1Config]))
|
||||
case object F1 extends BasePlatformConfig("f1", Seq(classOf[DefaultF1Config]))
|
||||
case object Vitis extends BasePlatformConfig("vitis", Seq(classOf[DefaultVitisConfig]))
|
||||
}
|
||||
|
||||
abstract class TutorialSuite(
|
||||
val targetName: String,
|
||||
override val targetConfigs: String = "NoConfig",
|
||||
override val platformConfigs: Seq[Class[_ <: Config]] = Seq(),
|
||||
override val basePlatformConfig: BasePlatformConfig = BaseConfigs.F1,
|
||||
val simulationArgs: Seq[String] = Seq()
|
||||
) extends TestSuiteCommon("midasexamples") with Matchers {
|
||||
val targetName: String,
|
||||
override val targetConfigs: String = "NoConfig",
|
||||
override val platformConfigs: Seq[Class[_ <: Config]] = Seq(),
|
||||
override val basePlatformConfig: BasePlatformConfig = BaseConfigs.F1,
|
||||
val simulationArgs: Seq[String] = Seq(),
|
||||
val shouldPass: Boolean = true,
|
||||
) extends TestSuiteCommon("midasexamples")
|
||||
with Matchers {
|
||||
|
||||
val backendSimulator = "verilator"
|
||||
|
||||
def run(backend: String,
|
||||
debug: Boolean = false,
|
||||
logFile: Option[File] = None,
|
||||
waveform: Option[File] = None,
|
||||
args: Seq[String] = Nil) = {
|
||||
val makeArgs = Seq(
|
||||
s"run-$backend%s".format(if (debug) "-debug" else ""),
|
||||
"LOGFILE=%s".format(logFile map toStr getOrElse ""),
|
||||
"WAVEFORM=%s".format(waveform map toStr getOrElse ""),
|
||||
"ARGS=%s".format(args mkString " "))
|
||||
if (isCmdAvailable(backend)) {
|
||||
make(makeArgs:_*)
|
||||
} else 0
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs MIDAS-level simulation on the design.
|
||||
*
|
||||
* @param b Backend simulator: "verilator" or "vcs"
|
||||
* @param debug When true, captures waves from the simulation
|
||||
* @param args A seq of PlusArgs to pass to the simulator.
|
||||
* @param shouldPass When false, asserts the test returns a non-zero code
|
||||
*/
|
||||
def runTest(b: String, debug: Boolean = false, args: Seq[String] = simulationArgs, shouldPass: Boolean = true) {
|
||||
val prefix = if (shouldPass) "pass in " else "fail in "
|
||||
val testEnvStr = s"${b} MIDAS-level simulation"
|
||||
override def defineTests(backend: String, debug: Boolean) {
|
||||
val prefix = if (shouldPass) "pass in " else "fail "
|
||||
val wavesStr = if (debug) " with waves enabled" else ""
|
||||
val argStr = " with args: " + args.mkString(" ")
|
||||
val argStr = " with args: " + simulationArgs.mkString(" ")
|
||||
|
||||
val haveThisBehavior = prefix + testEnvStr + wavesStr + argStr
|
||||
val haveThisBehavior = prefix + wavesStr + argStr
|
||||
|
||||
if (isCmdAvailable(b)) {
|
||||
it should haveThisBehavior in {
|
||||
assert((run(b, debug, args = args) == 0) == shouldPass)
|
||||
}
|
||||
} else {
|
||||
ignore should haveThisBehavior in { }
|
||||
it should haveThisBehavior in {
|
||||
assert((run(backend, debug, args = simulationArgs) == 0) == shouldPass)
|
||||
}
|
||||
}
|
||||
|
||||
mkdirs()
|
||||
behavior of s"$targetName"
|
||||
elaborateAndCompile()
|
||||
compileMlSimulator(backendSimulator)
|
||||
runTest(backendSimulator)
|
||||
}
|
||||
|
||||
class VitisCITests extends Suites (
|
||||
new GCDVitisTest,
|
||||
new ParityVitisTest,
|
||||
new PrintfModuleVitisTest,
|
||||
new MulticlockPrintVitisTest,
|
||||
)
|
||||
class VitisCITests
|
||||
extends Suites(
|
||||
new GCDVitisTest,
|
||||
new ParityVitisTest,
|
||||
new PrintfModuleVitisTest,
|
||||
new MulticlockPrintVitisTest,
|
||||
)
|
||||
|
||||
// These groups are vestigial from CircleCI container limits
|
||||
class CIGroupA extends Suites(
|
||||
new ChiselExampleDesigns,
|
||||
new PrintfSynthesisCITests,
|
||||
new firesim.fasedtests.CIGroupA,
|
||||
new AutoCounterCITests,
|
||||
new ResetPulseBridgeActiveHighTest,
|
||||
new ResetPulseBridgeActiveLowTest,
|
||||
)
|
||||
class CIGroupA
|
||||
extends Suites(
|
||||
new ChiselExampleDesigns,
|
||||
new PrintfSynthesisCITests,
|
||||
new firesim.fasedtests.CIGroupA,
|
||||
new AutoCounterCITests,
|
||||
new ResetPulseBridgeActiveHighTest,
|
||||
new ResetPulseBridgeActiveLowTest,
|
||||
)
|
||||
|
||||
class CIGroupB extends Suites(
|
||||
new AssertionSynthesisCITests,
|
||||
new GoldenGateMiscCITests,
|
||||
new firesim.fasedtests.CIGroupB,
|
||||
new firesim.AllMidasUnitTests,
|
||||
new firesim.FailingUnitTests,
|
||||
new FMRCITests,
|
||||
new VitisCITests
|
||||
)
|
||||
class CIGroupB
|
||||
extends Suites(
|
||||
new AssertionSynthesisCITests,
|
||||
new GoldenGateMiscCITests,
|
||||
new firesim.fasedtests.CIGroupB,
|
||||
new firesim.AllMidasUnitTests,
|
||||
new firesim.FailingUnitTests,
|
||||
new FMRCITests,
|
||||
new VitisCITests,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue