diff --git a/sim/midas b/sim/midas index 0c7fb627..ed0f6a8d 160000 --- a/sim/midas +++ b/sim/midas @@ -1 +1 @@ -Subproject commit 0c7fb627d9782c0ac41a09af17afd14e3b777634 +Subproject commit ed0f6a8d5ef45ddc64cf501d10dcd2cd9e85a144 diff --git a/sim/src/main/scala/firesim/Generator.scala b/sim/src/main/scala/firesim/Generator.scala index 6ffacc56..d40fd001 100755 --- a/sim/src/main/scala/firesim/Generator.scala +++ b/sim/src/main/scala/firesim/Generator.scala @@ -74,10 +74,14 @@ trait HasFireSimGeneratorUtilities extends HasGeneratorUtilities with HasTestSui lazy val targetParams = getParameters(names.fullConfigClasses) lazy val target = getGenerator(names, targetParams) lazy val testDir = new File(names.targetDir) - val customPasses = Seq( + val targetTransforms = Seq( firesim.passes.AsyncResetRegPass, firesim.passes.PlusArgReaderPass ) + lazy val hostTransforms = Seq( + new firesim.passes.ILATopWiringTransform(testDir) + ) + // While this is called the HostConfig, it does also include configurations // that control what models are instantiated lazy val hostParams = getParameters( @@ -98,8 +102,9 @@ trait HasFireSimGeneratorUtilities extends HasGeneratorUtilities with HasTestSui generatorArgs.midasFlowKind match { case "midas" | "strober" => - midas.MidasCompiler(chirrtl, annos, portList, testDir, None, customPasses)(hostParams alterPartial { - case midas.EnableSnapshot => generatorArgs.midasFlowKind == "strober" }) + midas.MidasCompiler( + chirrtl, annos, portList, testDir, None, targetTransforms, hostTransforms + )(hostParams alterPartial {case midas.EnableSnapshot => generatorArgs.midasFlowKind == "strober" }) // Need replay } } diff --git a/sim/src/main/scala/midasexamples/GCD.scala b/sim/src/main/scala/midasexamples/GCD.scala index 89939a36..7ca125ed 100644 --- a/sim/src/main/scala/midasexamples/GCD.scala +++ b/sim/src/main/scala/midasexamples/GCD.scala @@ -2,6 +2,8 @@ package firesim.midasexamples +import midas.passes.FpgaDebugAnnotation + import chisel3._ import chisel3.util.unless @@ -20,4 +22,7 @@ class GCD extends Module { when (io.e) { x := io.a; y := io.b } io.z := x io.v := y === 0.U + + chisel3.experimental.annotate(FpgaDebugAnnotation(x)) + chisel3.experimental.annotate(FpgaDebugAnnotation(y)) } diff --git a/sim/src/main/scala/midasexamples/Generator.scala b/sim/src/main/scala/midasexamples/Generator.scala index ace86307..85b6c5e0 100644 --- a/sim/src/main/scala/midasexamples/Generator.scala +++ b/sim/src/main/scala/midasexamples/Generator.scala @@ -1,9 +1,10 @@ //See LICENSE for license details. + package firesim.midasexamples -import java.io.File -import freechips.rocketchip.config.Config import midas._ +import freechips.rocketchip.config.Config +import java.io.File trait GeneratorUtils { def targetName: String @@ -23,9 +24,13 @@ trait GeneratorUtils { case midas.F1 => new Config(new firesim.firesim.WithDefaultMemModel ++ new midas.F1Config) }).toInstance - def compile() { MidasCompiler(dut, genDir)(midasParams) } + lazy val hostTransforms = Seq( + new firesim.passes.ILATopWiringTransform(genDir) + ) + + def compile() { MidasCompiler(dut, genDir, hostTransforms = hostTransforms)(midasParams) } def compileWithSnaptshotting() { - MidasCompiler(dut, genDir)( + MidasCompiler(dut, genDir, hostTransforms = hostTransforms)( midasParams alterPartial { case midas.EnableSnapshot => true }) } def compileWithReplay() { @@ -33,7 +38,6 @@ trait GeneratorUtils { } } - object Generator extends App with GeneratorUtils { lazy val targetName = args(1) lazy val genDir = new File(args(2)) diff --git a/sim/src/main/scala/passes/AsyncResetReg.scala b/sim/src/main/scala/passes/AsyncResetReg.scala index 218d0106..48364dcd 100644 --- a/sim/src/main/scala/passes/AsyncResetReg.scala +++ b/sim/src/main/scala/passes/AsyncResetReg.scala @@ -1,3 +1,5 @@ +// See LICENSE for license details. + package firesim package passes @@ -16,7 +18,7 @@ class AsyncResetReg extends chisel3.experimental.MultiIOModule { // Transform RocketChip's async reset to sync reset object AsyncResetRegPass extends firrtl.passes.Pass { - override def name = "[midas top] AsyncResetReg Pass" + override def name = "[FireSim] Replace AsyncReset Pass" override def inputForm = MidForm override def outputForm = MidForm diff --git a/sim/src/main/scala/passes/ILATopWiring.scala b/sim/src/main/scala/passes/ILATopWiring.scala new file mode 100644 index 00000000..f1f3e7f6 --- /dev/null +++ b/sim/src/main/scala/passes/ILATopWiring.scala @@ -0,0 +1,137 @@ +// See LICENSE for license details. + +package firesim.passes + +import midas.passes.FirrtlFpgaDebugAnnotation + +import firrtl._ +import firrtl.ir._ +import firrtl.passes._ +import firrtl.annotations._ + +import java.io._ +import scala.io.Source +import collection.mutable + +import firrtl.transforms.TopWiring._ + +/** Add ports punch out annotated ports out to the toplevel of the circuit. + This also has an option to pass a function as a parmeter to generate custom output files as a result of the additional ports + * @note This *does* work for deduped modules + */ +class ILATopWiringTransform(dir: File = new File("/tmp/")) extends Transform { + def inputForm: CircuitForm = LowForm + def outputForm: CircuitForm = LowForm + override def name = "[FireSim] ILA Top Wiring Transform" + + type InstPath = Seq[String] + + def ILAWiringOutputFiles(dir: String, mapping: Seq[((ComponentName, Type, Boolean, InstPath, String), Int)], state: CircuitState): CircuitState = { + + //val targetFile: Option[String] = state.annotations.collectFirst { case ILADebugFileAnnotation(fn) => fn } + + //output vivado tcl file + val tclOutputFile = new PrintWriter(new File(dir, "firesim_ila_insert_vivado.tcl" )) + //output verilog 'include' file with ports + val portsOutputFile = new PrintWriter(new File(dir, "firesim_ila_insert_ports.v" )) + //output verilog 'include' file with wires + val wiresOutputFile = new PrintWriter(new File(dir, "firesim_ila_insert_wires.v" )) + //output verilog 'include' file with ila instantiation + val ilaInstOutputFile = new PrintWriter(new File(dir, "firesim_ila_insert_inst.v" )) + + //vivado tcl prologue + tclOutputFile.append(s"create_project managed_ip_project $$CL_DIR/ip/firesim_ila_ip/managed_ip_project -part xcvu9p-flgb2104-2-i -ip\n") + tclOutputFile.append(s"set_property simulator_language Verilog [current_project]\n") + tclOutputFile.append(s"set_property target_simulator XSim [current_project]\n") + tclOutputFile.append(s"create_ip -name ila -vendor xilinx.com -library ip -version 6.2 -module_name ila_firesim_0 -dir $$CL_DIR/ip/firesim_ila_ip\n") + tclOutputFile.append(s"set_property -dict [list ") + + + if (mapping.nonEmpty) { + //verilg wires `include file prologeu + wiresOutputFile.append(s"//Automatically generated wire declarations for ILA connections\n") + + //verilog ila instantiation `include file prologue + ilaInstOutputFile.append(s"//Automatically generated ILA instantiation\n") + ilaInstOutputFile.append(s"ila_firesim_0 CL_FIRESIM_DEBUG_WIRING_TRANSFORM (\n") + ilaInstOutputFile.append(s" .clk (firesim_internal_clock)") + } + + //body + mapping map { case ((cname, tpe, _, path, prefix), index) => + //val probewidth = tpe.asInstanceOf[GroundType].width.asInstanceOf[IntWidth].width + val probewidth = tpe match { case GroundType(IntWidth(w)) => w } + val probewidth1 = probewidth - 1 + val probetriggers = 3 + val probenum = index + val probeportname = prefix + path.mkString("_") + + //vivado tcl + tclOutputFile.append(s"CONFIG.C_PROBE$probenum" ++ s"_WIDTH {$probewidth} ") + tclOutputFile.append(s"CONFIG.C_PROBE$probenum" ++ s"_MU_CNT {$probetriggers} ") + + //verilog ports + portsOutputFile.append(s" .$probeportname($probeportname), \n") + + //verilog wires + wiresOutputFile.append(s"wire [$probewidth1:0] $probeportname; \n") + + //verilog ila instantiation + ilaInstOutputFile.append(s",\n .probe$probenum ($probeportname)") + } + + if (mapping.isEmpty) { + //vivado tcl + tclOutputFile.append(s"CONFIG.C_PROBE0" ++ s"_WIDTH {1} ") + tclOutputFile.append(s"CONFIG.C_PROBE0" ++ s"_MU_CNT {3} ") + } + + + //vivado tcl epilogue + val numprobes = if (mapping.size > 0) {mapping.size} else {1} + val probetriggers = 3 + tclOutputFile.append(s"CONFIG.C_NUM_OF_PROBES {$numprobes} ") + tclOutputFile.append(s"CONFIG.C_TRIGOUT_EN {false} ") + tclOutputFile.append(s"CONFIG.C_EN_STRG_QUAL {1} ") + tclOutputFile.append(s"CONFIG.C_ADV_TRIGGER {true} ") + tclOutputFile.append(s"CONFIG.C_TRIGIN_EN {false} ") + tclOutputFile.append(s"CONFIG.ALL_PROBE_SAME_MU_CNT {$probetriggers} ") + tclOutputFile.append(s"] [get_ips ila_firesim_0]\n") + tclOutputFile.append(s"generate_target {instantiation_template} [get_files $$CL_DIR/ip/firesim_ila_ip/ila_firesim_0/ila_firesim_0.xci]\n") + tclOutputFile.append(s"generate_target all [get_files $$CL_DIR/ip/firesim_ila_ip/ila_firesim_0/ila_firesim_0.xci]\n") + tclOutputFile.append(s"export_ip_user_files -of_objects [get_files $$CL_DIR/ip/firesim_ila_ip/ila_0/ila_firesim_0.xci] -no_script -sync -force -quiet\n") + tclOutputFile.append(s"create_ip_run [get_files -of_objects [get_fileset sources_1] $$CL_DIR/ip/firesim_ila_ip/ila_firesim_0/ila_firesim_0.xci]\n") + tclOutputFile.append(s"launch_runs -jobs 8 ila_firesim_0_synth_1\n") + tclOutputFile.append(s"wait_on_run ila_firesim_0_synth_1\n") + tclOutputFile.append(s"export_simulation -of_objects [get_files $$CL_DIR/ip/firesim_ila_ip/ila_firesim_0/ila_firesim_0.xci] -directory $$CL_DIR/ip/firesim_ila_ip/ip_user_files/sim_scripts -ip_user_files_dir $$CL_DIR/ip/firesim_ila_ip/ip_user_files -ipstatic_source_dir $$CL_DIR/ip/firesim_ila_ip/ip_user_files/ipstatic -lib_map_path [list {modelsim=$$CL_DIR/ip/firesim_ila_ip/managed_ip_project/managed_ip_project.cache/compile_simlib/modelsim} {questa=$$CL_DIR/ip/firesim_ila_ip/managed_ip_project/managed_ip_project.cache/compile_simlib/questa} {ies=$$CL_DIR/ip/firesim_ila_ip/managed_ip_project/managed_ip_project.cache/compile_simlib/ies} {vcs=$$CL_DIR/ip/firesim_ila_ip/managed_ip_project/managed_ip_project.cache/compile_simlib/vcs} {riviera=$$CL_DIR/ip/firesim_ila_ip/managed_ip_project/managed_ip_project.cache/compile_simlib/riviera}] -use_ip_compiled_libs -force -quiet\n") + + if (mapping.nonEmpty) { + //verilog ila instantiation `include epilogue + ilaInstOutputFile.append(s"\n);\n") + } + + tclOutputFile.close() + portsOutputFile.close() + wiresOutputFile.close() + ilaInstOutputFile.close() + + state + } + + def execute(state: CircuitState): CircuitState = { + val ilaannos = state.annotations.collect { + case a @ (_: FirrtlFpgaDebugAnnotation) => a + } + + //Take debug annotation and make them into TopWiring annotations + val targetannos = ilaannos match { + case p => p.map { case FirrtlFpgaDebugAnnotation(target) => TopWiringAnnotation(target, s"ila_") } + } + + //dirname should be some aws-fpga synthesis directory + val topwiringannos = targetannos ++ Seq(TopWiringOutputFilesAnnotation(dir.getPath(), ILAWiringOutputFiles)) + + def topwiringtransform = new TopWiringTransform + topwiringtransform.execute(state.copy(annotations = state.annotations ++ topwiringannos)) + } +} diff --git a/sim/src/main/scala/passes/PlusArgReader.scala b/sim/src/main/scala/passes/PlusArgReader.scala index a5c21151..1910d04a 100644 --- a/sim/src/main/scala/passes/PlusArgReader.scala +++ b/sim/src/main/scala/passes/PlusArgReader.scala @@ -1,3 +1,5 @@ +// See LICENSE for license details. + package firesim package passes @@ -7,7 +9,7 @@ import firrtl.Utils.zero // Remove RocketChip's plusarg_reader object PlusArgReaderPass extends firrtl.passes.Pass { - override def name = "[midas top] PlusArgReader Pass" + override def name = "[FireSim] PlusArgReader Pass" override def inputForm = MidForm override def outputForm = MidForm