diff --git a/Makefrag b/Makefrag index f1d8d8efa2..d4b6520925 100644 --- a/Makefrag +++ b/Makefrag @@ -6,9 +6,14 @@ endif MODEL ?= TestHarness PROJECT ?= freechips.rocketchip.system CFG_PROJECT ?= $(PROJECT) -CONFIG ?= DefaultConfig +CONFIG ?= $(CFG_PROJECT).DefaultConfig # TODO: For now must match rocketchip.Generator -long_name = $(PROJECT).$(CONFIG) +comma := , +space := $() $() +splitConfigs := $(subst $(comma), ,$(CONFIG)) +configBases := $(foreach config,$(splitConfigs),$(lastword $(subst ., ,$(config)))) +CONFIG_STR := $(subst $(space),_,$(configBases)) +long_name = $(PROJECT).$(CONFIG_STR) VLSI_MEM_GEN ?= $(base_dir)/scripts/vlsi_mem_gen diff --git a/emulator/Makefrag-verilator b/emulator/Makefrag-verilator index d6423ada0a..8011a42aef 100644 --- a/emulator/Makefrag-verilator +++ b/emulator/Makefrag-verilator @@ -10,7 +10,7 @@ verilog = \ $(generated_dir)/%.fir $(generated_dir)/%.d: $(FIRRTL_JAR) $(chisel_srcs) $(bootrom_img) mkdir -p $(dir $@) - cd $(base_dir) && $(SBT) "runMain $(PROJECT).Generator $(generated_dir) $(PROJECT) $(MODEL) $(CFG_PROJECT) $(CONFIG)" + cd $(base_dir) && $(SBT) "runMain $(PROJECT).Generator -td $(generated_dir) -T $(PROJECT).$(MODEL) -C $(CONFIG)" %.v %.conf: %.fir $(FIRRTL_JAR) mkdir -p $(dir $@) @@ -69,7 +69,7 @@ VERILATOR_FLAGS := --top-module $(MODEL) \ --threads $(VERILATOR_THREADS) -Wno-UNOPTTHREADS \ -Wno-STMTDLY --x-assign unique \ -I$(vsrc) \ - -O3 -CFLAGS "$(CXXFLAGS) -DVERILATOR -DTEST_HARNESS=V$(MODEL) -include $(csrc)/verilator.h -include $(generated_dir)/$(PROJECT).$(CONFIG).plusArgs" + -O3 -CFLAGS "$(CXXFLAGS) -DVERILATOR -DTEST_HARNESS=V$(MODEL) -include $(csrc)/verilator.h -include $(generated_dir)/$(long_name).plusArgs" cppfiles = $(addprefix $(csrc)/, $(addsuffix .cc, $(CXXSRCS))) headers = $(wildcard $(csrc)/*.h) diff --git a/regression/Makefile b/regression/Makefile index 67983ad9e7..3b660c9ccd 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -46,22 +46,22 @@ endif ifeq ($(SUITE),RocketSuiteA) PROJECT=freechips.rocketchip.system -CONFIGS=DefaultConfig +CONFIGS=$(PROJECT).DefaultConfig endif ifeq ($(SUITE),RocketSuiteB) PROJECT=freechips.rocketchip.system -CONFIGS=DefaultBufferlessConfig +CONFIGS=$(PROJECT).DefaultBufferlessConfig endif ifeq ($(SUITE),RocketSuiteC) PROJECT=freechips.rocketchip.system -CONFIGS=TinyConfig +CONFIGS=$(PROJECT).TinyConfig endif ifeq ($(SUITE),UnittestSuite) PROJECT=freechips.rocketchip.unittest -CONFIGS=AMBAUnitTestConfig TLSimpleUnitTestConfig TLWidthUnitTestConfig +CONFIGS=$(PROJECT).AMBAUnitTestConfig $(PROJECT).TLSimpleUnitTestConfig $(PROJECT).TLWidthUnitTestConfig endif ifeq ($(SUITE), JtagDtmSuite) @@ -69,13 +69,13 @@ PROJECT=freechips.rocketchip.system export JTAG_DTM_ENABLE_SBA ?= off ifeq ($(JTAG_DTM_ENABLE_SBA), off) -CONFIGS_32=WithJtagDTMSystem_DefaultRV32Config -CONFIGS_64=WithJtagDTMSystem_DefaultConfig +CONFIGS_32=$(PROJECT).WithJtagDTMSystem,$(PROJECT).DefaultRV32Config +CONFIGS_64=$(PROJECT).WithJtagDTMSystem,$(PROJECT).DefaultConfig endif ifeq ($(JTAG_DTM_ENABLE_SBA), on) -CONFIGS_32=WithJtagDTMSystem_WithDebugSBASystem_DefaultRV32Config -CONFIGS_64=WithJtagDTMSystem_WithDebugSBASystem_DefaultConfig +CONFIGS_32=$(PROJECT).WithJtagDTMSystem,$(PROJECT).WithDebugSBASystem,$(PROJECT).DefaultRV32Config +CONFIGS_64=$(PROJECT).WithJtagDTMSystem,$(PROJECT).WithDebugSBASystem,$(PROJECT).DefaultConfig endif CONFIGS += $(CONFIGS_32) @@ -89,18 +89,18 @@ endif ifeq ($(SUITE), Miscellaneous) PROJECT=freechips.rocketchip.system CONFIGS=\ - DefaultSmallConfig \ - DualBankConfig \ - DualChannelConfig \ - DualChannelDualBankConfig \ - RoccExampleConfig \ - Edge128BitConfig \ - Edge32BitConfig \ - QuadChannelBenchmarkConfig \ - EightChannelConfig \ - DualCoreConfig \ - MemPortOnlyConfig \ - MMIOPortOnlyConfig + $(PROJECT).DefaultSmallConfig \ + $(PROJECT).DualBankConfig \ + $(PROJECT).DualChannelConfig \ + $(PROJECT).DualChannelDualBankConfig \ + $(PROJECT).RoccExampleConfig \ + $(PROJECT).Edge128BitConfig \ + $(PROJECT).Edge32BitConfig \ + $(PROJECT).QuadChannelBenchmarkConfig \ + $(PROJECT).EightChannelConfig \ + $(PROJECT).DualCoreConfig \ + $(PROJECT).MemPortOnlyConfig \ + $(PROJECT).MMIOPortOnlyConfig endif # These are the named regression targets. While it's expected you run them in diff --git a/src/main/scala/groundtest/Generator.scala b/src/main/scala/groundtest/Generator.scala index b3ffeb8616..6279c67b4e 100644 --- a/src/main/scala/groundtest/Generator.scala +++ b/src/main/scala/groundtest/Generator.scala @@ -2,11 +2,7 @@ package freechips.rocketchip.groundtest -import freechips.rocketchip.util.GeneratorApp +import firrtl.options.StageMain +import freechips.rocketchip.system.RocketChipStage -object Generator extends GeneratorApp { - generateFirrtl - generateAnno - generateTestSuiteMakefrags // TODO: Needed only for legacy make targets - generateArtefacts -} +object Generator extends StageMain(new RocketChipStage) diff --git a/src/main/scala/stage/RocketChipAnnotations.scala b/src/main/scala/stage/RocketChipAnnotations.scala new file mode 100644 index 0000000000..f0c2c1302c --- /dev/null +++ b/src/main/scala/stage/RocketChipAnnotations.scala @@ -0,0 +1,52 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage + +import chisel3.experimental.BaseModule +import firrtl.annotations.{Annotation, NoTargetAnnotation} +import firrtl.options.{HasShellOptions, ShellOption, Unserializable} + +sealed trait RocketChipOption extends Unserializable { this: Annotation => } + +/* required options */ + +/** Path to top module class */ +case class TopModuleAnnotation(clazz: Class[_ <: Any]) extends NoTargetAnnotation with RocketChipOption +private[stage] object TopModuleAnnotation extends HasShellOptions { + override val options = Seq( + new ShellOption[String]( + longOption = "top-module", + toAnnotationSeq = a => Seq(TopModuleAnnotation(Class.forName(a).asInstanceOf[Class[_ <: BaseModule]])), + helpText = "", + shortOption = Some("T") + ) + ) +} + +/** Paths to config classes */ +case class ConfigsAnnotation(configNames: Seq[String]) extends NoTargetAnnotation with RocketChipOption +private[stage] object ConfigsAnnotation extends HasShellOptions { + override val options = Seq( + new ShellOption[Seq[String]]( + longOption = "configs", + toAnnotationSeq = a => Seq(ConfigsAnnotation(a)), + helpText = "", + shortOption = Some("C") + ) + ) +} + +/* optional options */ + +/** Optional base name for generated files' filenames */ +case class OutputBaseNameAnnotation(outputBaseName: String) extends NoTargetAnnotation with RocketChipOption +private[stage] object OutputBaseNameAnnotation extends HasShellOptions { + override val options = Seq( + new ShellOption[String]( + longOption = "name", + toAnnotationSeq = a => Seq(OutputBaseNameAnnotation(a)), + helpText = "", + shortOption = Some("n") + ) + ) +} diff --git a/src/main/scala/stage/RocketChipCli.scala b/src/main/scala/stage/RocketChipCli.scala new file mode 100644 index 0000000000..30becb6cee --- /dev/null +++ b/src/main/scala/stage/RocketChipCli.scala @@ -0,0 +1,17 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage + +import firrtl.options.Shell + +trait RocketChipCli { this: Shell => + + parser.note("Rocket Chip Compiler Options") + Seq( + TopModuleAnnotation, + ConfigsAnnotation, + OutputBaseNameAnnotation, + ) + .foreach(_.addOptions(parser)) + +} diff --git a/src/main/scala/stage/RocketChipOptions.scala b/src/main/scala/stage/RocketChipOptions.scala new file mode 100644 index 0000000000..2fac6a276f --- /dev/null +++ b/src/main/scala/stage/RocketChipOptions.scala @@ -0,0 +1,41 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage + +class RocketChipOptions private[stage] ( + val topModule: Option[Class[_ <: Any]] = None, + val configNames: Option[Seq[String]] = None, + val outputBaseName: Option[String] = None) { + + private[stage] def copy( + topModule: Option[Class[_ <: Any]] = topModule, + configNames: Option[Seq[String]] = configNames, + outputBaseName: Option[String] = outputBaseName, + ): RocketChipOptions = { + + new RocketChipOptions( + topModule=topModule, + configNames=configNames, + outputBaseName=outputBaseName, + ) + } + + lazy val topPackage: Option[String] = topModule match { + case Some(a) => Some(a.getPackage.getName) + case _ => None + } + + lazy val configClass: Option[String] = configNames match { + case Some(names) => + val classNames = names.map{ n => n.split('.').last } + Some(classNames.mkString("_")) + case _ => None + } + + lazy val longName: Option[String] = outputBaseName match { + case Some(name) => Some(name) + case _ => + if (!topPackage.isEmpty && !configClass.isEmpty) Some(s"${topPackage.get}.${configClass.get}") else None + } +} + diff --git a/src/main/scala/stage/package.scala b/src/main/scala/stage/package.scala new file mode 100644 index 0000000000..db5b42234d --- /dev/null +++ b/src/main/scala/stage/package.scala @@ -0,0 +1,24 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip + +import firrtl.AnnotationSeq +import firrtl.options.OptionsView + +package object stage { + + implicit object RocketChipOptionsView extends OptionsView[RocketChipOptions] { + + def view(annotations: AnnotationSeq): RocketChipOptions = annotations + .collect { case a: RocketChipOption => a } + .foldLeft(new RocketChipOptions()){ (c, x) => + x match { + case TopModuleAnnotation(a) => c.copy(topModule = Some(a)) + case ConfigsAnnotation(a) => c.copy(configNames = Some(a)) + case OutputBaseNameAnnotation(a) => c.copy(outputBaseName = Some(a)) + } + } + + } + +} diff --git a/src/main/scala/stage/phases/AddDefaultTests.scala b/src/main/scala/stage/phases/AddDefaultTests.scala new file mode 100644 index 0000000000..1fdd8b9a44 --- /dev/null +++ b/src/main/scala/stage/phases/AddDefaultTests.scala @@ -0,0 +1,140 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + + +import chipsalliance.rocketchip.config.Parameters +import chisel3.stage.phases.Elaborate +import firrtl.AnnotationSeq +import firrtl.annotations.NoTargetAnnotation +import firrtl.options.{Phase, PreservesAll} +import firrtl.options.Viewer.view +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.subsystem.RocketTilesKey +import freechips.rocketchip.system.{DefaultTestSuites, RegressionTestSuite, RocketTestSuite, TestGeneration} +import freechips.rocketchip.tile.XLen +import freechips.rocketchip.util.HasRocketChipStageUtils +import freechips.rocketchip.system.DefaultTestSuites._ + +import scala.collection.mutable + +/** Annotation that contains a list of [[RocketTestSuite]]s to run */ +case class RocketTestSuiteAnnotation(tests: Seq[RocketTestSuite]) extends NoTargetAnnotation + +/** Generates [[RocketTestSuiteAnnotation]] depending on whether the top-module project is part of + * [[freechips.rocketchip.system]] or not (e.g. for unit tests). + */ +class AddDefaultTests extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks], classOf[Elaborate]) + override val dependents = Seq(classOf[GenerateTestSuiteMakefrags]) + + def GenerateDefaultTestSuites(): List[RocketTestSuite] = { + List(DefaultTestSuites.groundtest64("p"), DefaultTestSuites.emptyBmarks, DefaultTestSuites.singleRegression) + } + + def GenerateSystemTestSuites(annotations: AnnotationSeq): scala.collection.mutable.Buffer[RocketTestSuite] = { + val params: Parameters = getConfig(view[RocketChipOptions](annotations).configNames.get).toInstance + val xlen = params(XLen) + val tests = scala.collection.mutable.Buffer[RocketTestSuite]() + + val regressionTests = mutable.LinkedHashSet( + "rv64ud-v-fcvt", + "rv64ud-p-fdiv", + "rv64ud-v-fadd", + "rv64uf-v-fadd", + "rv64um-v-mul", + "rv64mi-p-breakpoint", + "rv64uc-v-rvc", + "rv64ud-v-structural", + "rv64si-p-wfi", + "rv64um-v-divw", + "rv64ua-v-lrsc", + "rv64ui-v-fence_i", + "rv64ud-v-fcvt_w", + "rv64uf-v-fmin", + "rv64ui-v-sb", + "rv64ua-v-amomax_d", + "rv64ud-v-move", + "rv64ud-v-fclass", + "rv64ua-v-amoand_d", + "rv64ua-v-amoxor_d", + "rv64si-p-sbreak", + "rv64ud-v-fmadd", + "rv64uf-v-ldst", + "rv64um-v-mulh", + "rv64si-p-dirty", + "rv32mi-p-ma_addr", + "rv32mi-p-csr", + "rv32ui-p-sh", + "rv32ui-p-lh", + "rv32uc-p-rvc", + "rv32mi-p-sbreak", + "rv32ui-p-sll") + + // TODO: for now only generate tests for the first core in the first subsystem + params(RocketTilesKey).headOption.map { tileParams => + val coreParams = tileParams.core + val vm = coreParams.useVM + val env = if (vm) List("p", "v") else List("p") + coreParams.fpu foreach { case cfg => + if (xlen == 32) { + tests ++= env.map(rv32uf) + if (cfg.fLen >= 64) + tests ++= env.map(rv32ud) + } else { + tests += rv32udBenchmarks + tests ++= env.map(rv64uf) + if (cfg.fLen >= 64) + tests ++= env.map(rv64ud) + } + } + if (coreParams.useAtomics) { + if (tileParams.dcache.flatMap(_.scratch).isEmpty) + tests ++= env.map(if (xlen == 64) rv64ua else rv32ua) + else + tests ++= env.map(if (xlen == 64) rv64uaSansLRSC else rv32uaSansLRSC) + } + if (coreParams.useCompressed) tests ++= env.map(if (xlen == 64) rv64uc else rv32uc) + val (rvi, rvu) = + if (xlen == 64) ((if (vm) rv64i else rv64pi), rv64u) + else ((if (vm) rv32i else rv32pi), rv32u) + + tests ++= rvi.map(_ ("p")) + tests ++= (if (vm) List("v") else List()).flatMap(env => rvu.map(_ (env))) + tests += benchmarks + + /* Filter the regression tests based on what the Rocket Chip configuration supports */ + val extensions = { + val fd = coreParams.fpu.map { + case cfg if cfg.fLen >= 64 => "fd" + case _ => "f" + } + val m = coreParams.mulDiv.map { case _ => "m" } + fd ++ m ++ Seq(if (coreParams.useRVE) Some("e") else Some("i"), + if (coreParams.useAtomics) Some("a") else None, + if (coreParams.useCompressed) Some("c") else None) + .flatten + .mkString("") + } + val re = s"""^rv$xlen[usm][$extensions].+""".r + regressionTests.retain { + case re() => true + case _ => false + } + tests += new RegressionTestSuite(regressionTests) + } + tests + } + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val ropts = view[RocketChipOptions](annotations) + val tests = ropts.topPackage.get match { + case "freechips.rocketchip.system" => GenerateSystemTestSuites(annotations) + case _ => GenerateDefaultTestSuites() + } + + RocketTestSuiteAnnotation(tests) +: annotations + } + +} diff --git a/src/main/scala/stage/phases/Checks.scala b/src/main/scala/stage/phases/Checks.scala new file mode 100644 index 0000000000..fbfd8bb757 --- /dev/null +++ b/src/main/scala/stage/phases/Checks.scala @@ -0,0 +1,47 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import firrtl.AnnotationSeq +import firrtl.annotations.Annotation +import firrtl.options.{OptionsException, Phase, PreservesAll, TargetDirAnnotation} +import freechips.rocketchip.stage._ + +import scala.collection.mutable + +/** Checks for the correct type and number of command line arguments */ +class Checks extends Phase with PreservesAll[Phase] { + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val targetDir, topModule, configNames, outputBaseName = mutable.ListBuffer[Annotation]() + + annotations.foreach { + case a: TargetDirAnnotation => a +=: targetDir + case a: TopModuleAnnotation => a +=: topModule + case a: ConfigsAnnotation => a +=: configNames + case a: OutputBaseNameAnnotation => a +=: outputBaseName + case _ => + } + + def required(annoList: mutable.ListBuffer[Annotation], option: String): Unit = { + if (annoList.size != 1) { + throw new OptionsException(s"Exactly one $option required") + } + } + + def optional(annoList: mutable.ListBuffer[Annotation], option: String): Unit = { + if (annoList.size > 1) { + throw new OptionsException(s"Too many $option options have been specified") + } + } + + required(targetDir, "target directory") + required(topModule, "top module") + required(configNames, "configs string (','-delimited)") + + optional(outputBaseName, "output base name") + + annotations + } + +} diff --git a/src/main/scala/stage/phases/GenerateArtefacts.scala b/src/main/scala/stage/phases/GenerateArtefacts.scala new file mode 100644 index 0000000000..b678906d2d --- /dev/null +++ b/src/main/scala/stage/phases/GenerateArtefacts.scala @@ -0,0 +1,27 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import chisel3.stage.phases.Elaborate +import firrtl.AnnotationSeq +import firrtl.options.{Phase, PreservesAll, StageOptions} +import firrtl.options.Viewer.view +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.util.{ElaborationArtefacts, HasRocketChipStageUtils} + +/** Writes [[ElaborationArtefacts]] into files */ +class GenerateArtefacts extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks], classOf[Elaborate]) + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val targetDir = view[StageOptions](annotations).targetDir + + ElaborationArtefacts.files.foreach { case (extension, contents) => + writeOutputFile(targetDir, s"${view[RocketChipOptions](annotations).longName.get}.${extension}", contents ()) + } + + annotations + } + +} diff --git a/src/main/scala/stage/phases/GenerateFirrtl.scala b/src/main/scala/stage/phases/GenerateFirrtl.scala new file mode 100644 index 0000000000..be564ee10c --- /dev/null +++ b/src/main/scala/stage/phases/GenerateFirrtl.scala @@ -0,0 +1,33 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import java.io.File + +import chisel3.Driver +import chisel3.stage.ChiselCircuitAnnotation +import chisel3.stage.phases.Elaborate +import firrtl.AnnotationSeq +import firrtl.options.{Phase, PreservesAll, StageOptions} +import firrtl.options.Viewer.view +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.util.HasRocketChipStageUtils + +/** Dumps circuit as FIRRTL string into a file */ +class GenerateFirrtl extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks], classOf[Elaborate]) + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val targetDir = view[StageOptions](annotations).targetDir + val file = new File(targetDir, s"${view[RocketChipOptions](annotations).longName.get}.fir") + + annotations.flatMap { + case a: ChiselCircuitAnnotation => + Driver.dumpFirrtl(a.circuit, Some(file)) + Some(a) + case a => Some(a) + } + } + +} diff --git a/src/main/scala/stage/phases/GenerateFirrtlAnnos.scala b/src/main/scala/stage/phases/GenerateFirrtlAnnos.scala new file mode 100644 index 0000000000..588e284ce4 --- /dev/null +++ b/src/main/scala/stage/phases/GenerateFirrtlAnnos.scala @@ -0,0 +1,35 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import java.io.{File, FileWriter} + +import chisel3.stage.ChiselCircuitAnnotation +import chisel3.stage.phases.Elaborate +import firrtl.AnnotationSeq +import firrtl.annotations.JsonProtocol +import firrtl.options.Viewer.view +import firrtl.options.{Phase, PreservesAll, StageOptions} +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.util.HasRocketChipStageUtils + +/** Writes FIRRTL annotations into a file */ +class GenerateFirrtlAnnos extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks], classOf[Elaborate]) + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val targetDir = view[StageOptions](annotations).targetDir + val file = new File(targetDir, s"${view[RocketChipOptions](annotations).longName.get}.anno.json") + + annotations.flatMap { + case a: ChiselCircuitAnnotation => + val af = new FileWriter(file) + af.write(JsonProtocol.serialize(a.circuit.annotations.map(_.toFirrtl))) + af.close() + Some(a) + case a => Some(a) + } + } + +} diff --git a/src/main/scala/stage/phases/GenerateROMs.scala b/src/main/scala/stage/phases/GenerateROMs.scala new file mode 100644 index 0000000000..9d6f65fedf --- /dev/null +++ b/src/main/scala/stage/phases/GenerateROMs.scala @@ -0,0 +1,30 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import chisel3.stage.ChiselCircuitAnnotation +import chisel3.stage.phases.Elaborate +import firrtl.AnnotationSeq +import firrtl.options.{Phase, PreservesAll, StageOptions} +import firrtl.options.Viewer.view +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.util.HasRocketChipStageUtils + +/** Dumps ROM information into a file */ +class GenerateROMs extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks], classOf[Elaborate]) + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val targetDir = view[StageOptions](annotations).targetDir + val fileName = s"${view[RocketChipOptions](annotations).longName.get}.rom.conf" + + annotations.flatMap { + case a: ChiselCircuitAnnotation => + writeOutputFile(targetDir, fileName, enumerateROMs(a.circuit)) + Some(a) + case a => Some(a) + } + } + +} diff --git a/src/main/scala/stage/phases/GenerateTestSuiteMakefrags.scala b/src/main/scala/stage/phases/GenerateTestSuiteMakefrags.scala new file mode 100644 index 0000000000..94f7866770 --- /dev/null +++ b/src/main/scala/stage/phases/GenerateTestSuiteMakefrags.scala @@ -0,0 +1,33 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import chisel3.stage.phases.Elaborate +import firrtl.AnnotationSeq +import firrtl.options.{Phase, PreservesAll, StageOptions} +import firrtl.options.Viewer.view +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.system.TestGeneration +import freechips.rocketchip.util.HasRocketChipStageUtils + +/** Generates a make script to run tests in [[RocketTestSuiteAnnotation]]. */ +class GenerateTestSuiteMakefrags extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks], classOf[Elaborate]) + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + val targetDir = view[StageOptions](annotations).targetDir + val fileName = s"${view[RocketChipOptions](annotations).longName.get}.d" + + annotations.flatMap { + case a: RocketTestSuiteAnnotation => + val makefrag = a.tests.groupBy(_.kind) + .map { case (kind, s) => TestGeneration.gen(kind, s) } + .mkString("\n") + writeOutputFile(targetDir, fileName, makefrag) + Some(a) + case a => Some(a) + } + } + +} diff --git a/src/main/scala/stage/phases/PreElaboration.scala b/src/main/scala/stage/phases/PreElaboration.scala new file mode 100644 index 0000000000..3a9435dca1 --- /dev/null +++ b/src/main/scala/stage/phases/PreElaboration.scala @@ -0,0 +1,39 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.stage.phases + +import chisel3.RawModule +import chisel3.stage.ChiselGeneratorAnnotation +import firrtl.AnnotationSeq +import firrtl.options.Viewer.view +import firrtl.options.{Phase, PreservesAll} +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.stage.RocketChipOptions +import freechips.rocketchip.util.HasRocketChipStageUtils + +/** Constructs a generator function that returns a top module with given config parameters */ +class PreElaboration extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils { + + override val prerequisites = Seq(classOf[Checks]) + override val dependents = Seq(classOf[chisel3.stage.phases.Elaborate]) + + override def transform(annotations: AnnotationSeq): AnnotationSeq = { + + val rOpts = view[RocketChipOptions](annotations) + val topMod = rOpts.topModule.get + + val config = getConfig(rOpts.configNames.get) + + val gen = () => + topMod + .getConstructor(classOf[Parameters]) + .newInstance(config) match { + case a: RawModule => a + case a: LazyModule => LazyModule(a).module + } + + ChiselGeneratorAnnotation(gen) +: annotations + } + +} diff --git a/src/main/scala/system/Generator.scala b/src/main/scala/system/Generator.scala deleted file mode 100644 index f430d985f0..0000000000 --- a/src/main/scala/system/Generator.scala +++ /dev/null @@ -1,111 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.system - -import freechips.rocketchip.subsystem.RocketTilesKey -import freechips.rocketchip.tile.XLen -import freechips.rocketchip.util.GeneratorApp - -import scala.collection.mutable.LinkedHashSet - -/** A Generator for platforms containing Rocket Subsystemes */ -object Generator extends GeneratorApp { - - override def addTestSuites { - import DefaultTestSuites._ - val xlen = params(XLen) - - val regressionTests = LinkedHashSet( - "rv64ud-v-fcvt", - "rv64ud-p-fdiv", - "rv64ud-v-fadd", - "rv64uf-v-fadd", - "rv64um-v-mul", - "rv64mi-p-breakpoint", - "rv64uc-v-rvc", - "rv64ud-v-structural", - "rv64si-p-wfi", - "rv64um-v-divw", - "rv64ua-v-lrsc", - "rv64ui-v-fence_i", - "rv64ud-v-fcvt_w", - "rv64uf-v-fmin", - "rv64ui-v-sb", - "rv64ua-v-amomax_d", - "rv64ud-v-move", - "rv64ud-v-fclass", - "rv64ua-v-amoand_d", - "rv64ua-v-amoxor_d", - "rv64si-p-sbreak", - "rv64ud-v-fmadd", - "rv64uf-v-ldst", - "rv64um-v-mulh", - "rv64si-p-dirty", - "rv32mi-p-ma_addr", - "rv32mi-p-csr", - "rv32ui-p-sh", - "rv32ui-p-lh", - "rv32uc-p-rvc", - "rv32mi-p-sbreak", - "rv32ui-p-sll") - - // TODO: for now only generate tests for the first core in the first subsystem - params(RocketTilesKey).headOption.map { tileParams => - val coreParams = tileParams.core - val vm = coreParams.useVM - val env = if (vm) List("p","v") else List("p") - coreParams.fpu foreach { case cfg => - if (xlen == 32) { - TestGeneration.addSuites(env.map(rv32uf)) - if (cfg.fLen >= 64) - TestGeneration.addSuites(env.map(rv32ud)) - } else { - TestGeneration.addSuite(rv32udBenchmarks) - TestGeneration.addSuites(env.map(rv64uf)) - if (cfg.fLen >= 64) - TestGeneration.addSuites(env.map(rv64ud)) - } - } - if (coreParams.useAtomics) { - if (tileParams.dcache.flatMap(_.scratch).isEmpty) - TestGeneration.addSuites(env.map(if (xlen == 64) rv64ua else rv32ua)) - else - TestGeneration.addSuites(env.map(if (xlen == 64) rv64uaSansLRSC else rv32uaSansLRSC)) - } - if (coreParams.useCompressed) TestGeneration.addSuites(env.map(if (xlen == 64) rv64uc else rv32uc)) - val (rvi, rvu) = - if (xlen == 64) ((if (vm) rv64i else rv64pi), rv64u) - else ((if (vm) rv32i else rv32pi), rv32u) - - TestGeneration.addSuites(rvi.map(_("p"))) - TestGeneration.addSuites((if (vm) List("v") else List()).flatMap(env => rvu.map(_(env)))) - TestGeneration.addSuite(benchmarks) - - /* Filter the regression tests based on what the Rocket Chip configuration supports */ - val extensions = { - val fd = coreParams.fpu.map { - case cfg if cfg.fLen >= 64 => "fd" - case _ => "f" - } - val m = coreParams.mulDiv.map{ case _ => "m" } - fd ++ m ++ Seq( if (coreParams.useRVE) Some("e") else Some("i"), - if (coreParams.useAtomics) Some("a") else None, - if (coreParams.useCompressed) Some("c") else None ) - .flatten - .mkString("") - } - val re = s"""^rv$xlen[usm][$extensions].+""".r - regressionTests.retain{ - case re() => true - case _ => false - } - TestGeneration.addSuite(new RegressionTestSuite(regressionTests)) - } - } - - generateFirrtl - generateAnno - generateTestSuiteMakefrags - generateROMs - generateArtefacts -} diff --git a/src/main/scala/system/RocketChipStageGenerator.scala b/src/main/scala/system/RocketChipStageGenerator.scala new file mode 100644 index 0000000000..d725275fd8 --- /dev/null +++ b/src/main/scala/system/RocketChipStageGenerator.scala @@ -0,0 +1,31 @@ +// See LICENSE.SiFive for license details. + +package freechips.rocketchip.system + +import chisel3.stage.{ChiselCli, ChiselStage} +import firrtl.options.PhaseManager.PhaseDependency +import firrtl.options.{Phase, PreservesAll, Shell, StageMain} +import firrtl.stage.FirrtlCli +import freechips.rocketchip.stage.RocketChipCli + +class RocketChipStage extends ChiselStage with PreservesAll[Phase] { + + override val shell = new Shell("rocket-chip") with RocketChipCli with ChiselCli with FirrtlCli + override val targets: Seq[PhaseDependency] = Seq( + classOf[freechips.rocketchip.stage.phases.Checks], + classOf[freechips.rocketchip.stage.phases.PreElaboration], + classOf[chisel3.stage.phases.Checks], + classOf[chisel3.stage.phases.Elaborate], + classOf[chisel3.stage.phases.MaybeAspectPhase], + classOf[freechips.rocketchip.stage.phases.GenerateFirrtl], + classOf[freechips.rocketchip.stage.phases.GenerateFirrtlAnnos], + classOf[freechips.rocketchip.stage.phases.AddDefaultTests], + classOf[freechips.rocketchip.stage.phases.GenerateTestSuiteMakefrags], + classOf[freechips.rocketchip.stage.phases.GenerateROMs], + classOf[freechips.rocketchip.stage.phases.GenerateArtefacts], + ) + + // TODO: need a RunPhaseAnnotation to inject phases into ChiselStage +} + +object Generator extends StageMain(new RocketChipStage) diff --git a/src/main/scala/system/RocketTestSuite.scala b/src/main/scala/system/RocketTestSuite.scala index d4c809c971..3129e83e11 100644 --- a/src/main/scala/system/RocketTestSuite.scala +++ b/src/main/scala/system/RocketTestSuite.scala @@ -61,16 +61,15 @@ object TestGeneration { def addSuites(s: Seq[RocketTestSuite]) { s.foreach(addSuite) } - def generateMakefrag: String = { - def gen(kind: String, s: Seq[RocketTestSuite]) = { - if(s.length > 0) { - val envs = s.groupBy(_.envName) - val targets = s.map(t => s"$$(${t.makeTargetName})").mkString(" ") - s.map(_.toString).mkString("\n") + - envs.filterKeys(_ != "").map( { - case (env,envsuites) => { - val suites = envsuites.map(t => s"$$(${t.makeTargetName})").mkString(" ") - s""" + private[rocketchip] def gen(kind: String, s: Seq[RocketTestSuite]) = { + if(s.length > 0) { + val envs = s.groupBy(_.envName) + val targets = s.map(t => s"$$(${t.makeTargetName})").mkString(" ") + s.map(_.toString).mkString("\n") + + envs.filterKeys(_ != "").map( { + case (env,envsuites) => { + val suites = envsuites.map(t => s"$$(${t.makeTargetName})").mkString(" ") + s""" run-$kind-$env-tests: $$(addprefix $$(output_dir)/, $$(addsuffix .out, $suites)) \t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED):(.*)/i )' $$^ /dev/null | perl -pe 'BEGIN { $$$$failed = 0 } $$$$failed = 1 if(/FAILED/i); END { exit($$$$failed) }' run-$kind-$env-tests-debug: $$(addprefix $$(output_dir)/, $$(addsuffix .vpd, $suites)) @@ -89,9 +88,10 @@ run-$kind-tests-fst: $$(addprefix $$(output_dir)/, $$(addsuffix .fst, $targets)) run-$kind-tests-fast: $$(addprefix $$(output_dir)/, $$(addsuffix .run, $targets)) \t@echo; perl -ne 'print " [$$$$1] $$$$ARGV \\t$$$$2\\n" if( /\\*{3}(.{8})\\*{3}(.*)/ || /ASSERTION (FAILED):(.*)/i )' $$^ /dev/null | perl -pe 'BEGIN { $$$$failed = 0 } $$$$failed = 1 if(/FAILED/i); END { exit($$$$failed) }' """ - } else { "\n" } - } + } else { "\n" } + } + def generateMakeFrag: String = { suites.values.toSeq.groupBy(_.kind).map { case (kind, s) => gen(kind, s) }.mkString("\n") } @@ -99,7 +99,7 @@ run-$kind-tests-fast: $$(addprefix $$(output_dir)/, $$(addsuffix .run, $targets) object DefaultTestSuites { val rv32uiNames = LinkedHashSet( - "simple", "add", "addi", "and", "andi", "auipc", "beq", "bge", "bgeu", "blt", "bltu", "bne", "fence_i", + "simple", "add", "addi", "and", "andi", "auipc", "beq", "bge", "bgeu", "blt", "bltu", "bne", "fence_i", "jal", "jalr", "lb", "lbu", "lh", "lhu", "lui", "lw", "or", "ori", "sb", "sh", "sw", "sll", "slli", "slt", "slti", "sra", "srai", "srl", "srli", "sub", "xor", "xori") val rv32ui = new AssemblyTestSuite("rv32ui", rv32uiNames)(_) diff --git a/src/main/scala/unittest/Generator.scala b/src/main/scala/unittest/Generator.scala index 1fd96632f4..01ef16886e 100644 --- a/src/main/scala/unittest/Generator.scala +++ b/src/main/scala/unittest/Generator.scala @@ -2,9 +2,7 @@ package freechips.rocketchip.unittest -object Generator extends freechips.rocketchip.util.GeneratorApp { - generateFirrtl - generateAnno - generateTestSuiteMakefrags // TODO: Needed only for legacy make targets - generateArtefacts -} +import firrtl.options.StageMain +import freechips.rocketchip.system.RocketChipStage + +object Generator extends StageMain(new RocketChipStage) diff --git a/src/main/scala/util/GeneratorUtils.scala b/src/main/scala/util/GeneratorUtils.scala index 4578906ffb..8d00dd8506 100644 --- a/src/main/scala/util/GeneratorUtils.scala +++ b/src/main/scala/util/GeneratorUtils.scala @@ -2,36 +2,14 @@ package freechips.rocketchip.util -import Chisel._ -import chisel3.RawModule -import chisel3.internal.firrtl.Circuit -// TODO: better job of Makefrag generation for non-RocketChip testing platforms import java.io.{File, FileWriter} -import firrtl.annotations.JsonProtocol -import freechips.rocketchip.config._ -import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.system.{DefaultTestSuites, TestGeneration} +import Chisel.throwException +import chipsalliance.rocketchip.config.{Config, Parameters} +import chisel3.internal.firrtl.Circuit -/** Representation of the information this Generator needs to collect from external sources. */ -case class ParsedInputNames( - targetDir: String, - topModuleProject: String, - topModuleClass: String, - configProject: String, - configs: String, - outputBaseName: Option[String]) { - val configClasses: Seq[String] = configs.split('_') - def prepend(prefix: String, suffix: String) = - if (prefix == "" || prefix == "_root_") suffix else (prefix + "." + suffix) - val fullConfigClasses: Seq[String] = configClasses.map(x => prepend(configProject, x)) - val fullTopModuleClass: String = prepend(topModuleProject, topModuleClass) -} +trait HasRocketChipStageUtils { -/** Common utilities we supply to all Generators. In particular, supplies the - * canonical ways of building various JVM elaboration-time structures. - */ -trait HasGeneratorUtilities { def getConfig(fullConfigClassNames: Seq[String]): Config = { new Config(fullConfigClassNames.foldRight(Parameters.empty) { case (currentName, config) => val currentConfig = try { @@ -44,22 +22,6 @@ trait HasGeneratorUtilities { }) } - def getParameters(names: Seq[String]): Parameters = getParameters(getConfig(names)) - - def getParameters(config: Config): Parameters = config.toInstance - - def elaborate(fullTopModuleClassName: String, params: Parameters): Circuit = { - val top = () => - Class.forName(fullTopModuleClassName) - .getConstructor(classOf[Parameters]) - .newInstance(params) match { - case m: RawModule => m - case l: LazyModule => LazyModule(l).module - } - - Driver.elaborate(top) - } - def enumerateROMs(circuit: Circuit): String = { val res = new StringBuilder val configs = @@ -74,73 +36,6 @@ trait HasGeneratorUtilities { } res.toString } -} - -/** Standardized command line interface for Scala entry point */ -trait GeneratorApp extends App with HasGeneratorUtilities { - lazy val names: ParsedInputNames = { - require(args.size == 5 || args.size == 6, "Usage: sbt> " + - "run TargetDir TopModuleProjectName TopModuleName " + - "ConfigProjectName ConfigNameString [OutputFilesBaseName]") - val base = - ParsedInputNames( - targetDir = args(0), - topModuleProject = args(1), - topModuleClass = args(2), - configProject = args(3), - configs = args(4), - outputBaseName = None) - - if (args.size == 6) { - base.copy(outputBaseName = Some(args(5))) - } else { - base - } - } - - // Canonical ways of building various JVM elaboration-time structures - lazy val td: String = names.targetDir - lazy val config: Config = getConfig(names.fullConfigClasses) - lazy val params: Parameters = config.toInstance - lazy val circuit: Circuit = elaborate(names.fullTopModuleClass, params) - - // Exhaustive name used to interface with external build tool targets - lazy val longName: String = names.outputBaseName.getOrElse(names.configProject + "." + names.configs) - - /** Output FIRRTL, which an external compiler can turn into Verilog. */ - def generateFirrtl { - Driver.dumpFirrtl(circuit, Some(new File(td, s"$longName.fir"))) // FIRRTL - } - - def generateAnno { - val annotationFile = new File(td, s"$longName.anno.json") - val af = new FileWriter(annotationFile) - af.write(JsonProtocol.serialize(circuit.annotations.map(_.toFirrtl))) - af.close() - } - - /** Output software test Makefrags, which provide targets for integration testing. */ - def generateTestSuiteMakefrags { - addTestSuites - writeOutputFile(td, s"$longName.d", TestGeneration.generateMakefrag) // Subsystem-specific test suites - } - - def addTestSuites { - TestGeneration.addSuite(DefaultTestSuites.groundtest64("p")) - TestGeneration.addSuite(DefaultTestSuites.emptyBmarks) - TestGeneration.addSuite(DefaultTestSuites.singleRegression) - } - - def generateROMs { - writeOutputFile(td, s"$longName.rom.conf", enumerateROMs(circuit)) - } - - /** Output files created as a side-effect of elaboration */ - def generateArtefacts { - ElaborationArtefacts.files.foreach { case (extension, contents) => - writeOutputFile(td, s"$longName.$extension", contents ()) - } - } def writeOutputFile(targetDir: String, fname: String, contents: String): File = { val f = new File(targetDir, fname) @@ -149,6 +44,7 @@ trait GeneratorApp extends App with HasGeneratorUtilities { fw.close f } + } object ElaborationArtefacts { diff --git a/vsim/Makefrag b/vsim/Makefrag index f28f192a21..278384769a 100644 --- a/vsim/Makefrag +++ b/vsim/Makefrag @@ -64,14 +64,14 @@ VCS_OPTS = -notice -line +lint=all,noVCDE,noONGS,noUI -error=PCWM-L -timescale=1 # Build the simulator #-------------------------------------------------------------------- -simv = $(sim_dir)/simv-$(PROJECT)-$(CONFIG) +simv = $(sim_dir)/simv-$(PROJECT)-$(CONFIG_STR) $(simv) : $(sim_vsrcs) $(sim_csrcs) cd $(sim_dir) && \ rm -rf csrc && \ $(VCS) $(VCS_OPTS) -o $(simv) \ -debug_pp \ -simv_debug = $(sim_dir)/simv-$(PROJECT)-$(CONFIG)-debug +simv_debug = $(sim_dir)/simv-$(PROJECT)-$(CONFIG_STR)-debug $(simv_debug) : $(sim_vsrcs) $(sim_csrcs) cd $(sim_dir) && \ rm -rf csrc && \ diff --git a/vsim/Makefrag-verilog b/vsim/Makefrag-verilog index 9b0afa159a..13e35373d3 100644 --- a/vsim/Makefrag-verilog +++ b/vsim/Makefrag-verilog @@ -10,7 +10,7 @@ verilog = $(generated_dir)/$(long_name).v $(generated_dir)/%.fir $(generated_dir)/%.d: $(FIRRTL_JAR) $(chisel_srcs) $(bootrom_img) mkdir -p $(dir $@) - cd $(base_dir) && $(SBT) "runMain $(PROJECT).Generator $(generated_dir) $(PROJECT) $(MODEL) $(CFG_PROJECT) $(CONFIG)" + cd $(base_dir) && $(SBT) "runMain $(PROJECT).Generator -td $(generated_dir) -T $(PROJECT).$(MODEL) -C $(CONFIG)" $(generated_dir)/%.v $(generated_dir)/%.conf: $(generated_dir)/%.fir $(FIRRTL_JAR) mkdir -p $(dir $@)