Merge pull request #197 from firesim/print-synthesis

Enable Endpointed Print synthesis
This commit is contained in:
David Biancolin 2019-01-15 14:32:38 -05:00 committed by GitHub
commit 5be11e3861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 293 additions and 24 deletions

View File

@ -10,31 +10,32 @@
# own images.
[fireboom-singlecore-nic-ddr3-llc4mb]
agfi=agfi-05c7620b62b901690
agfi=agfi-00857d0de5aa1d7b7
deploytripletoverride=None
customruntimeconfig=None
[fireboom-singlecore-no-nic-ddr3-llc4mb]
agfi=agfi-07f3c2762e4349327
agfi=agfi-0659b86373011fe04
deploytripletoverride=None
customruntimeconfig=None
[firesim-quadcore-nic-ddr3-llc4mb]
agfi=agfi-0c078780eadb4203b
agfi=agfi-0b929d88d9f66c9f5
deploytripletoverride=None
customruntimeconfig=None
[firesim-quadcore-no-nic-ddr3-llc4mb]
agfi=agfi-0e9945a181069cc67
agfi=agfi-0fb7f0fe1602f9cd9
deploytripletoverride=None
customruntimeconfig=None
[firesim-singlecore-no-nic-lbp]
agfi=agfi-05ca0d174e131e124
agfi=agfi-047644ef3f61ae041
deploytripletoverride=None
customruntimeconfig=None
[firesim-supernode-quadcore-nic-ddr3-llc4mb]
agfi=agfi-012e0df3c4b90375d
[firesim-supernode-singlecore-nic-lbp]
agfi=agfi-0498cf07932fd2e99
deploytripletoverride=None
customruntimeconfig=None

View File

@ -12,3 +12,4 @@ This section describes methods of debugging the target design and the simulation
Debugging-Hardware-Using-ILA.rst
TracerV.rst
DESSERT.rst
printf-synthesis.rst

View File

@ -0,0 +1,66 @@
Printf Synthesis
===================
MIDAS can synthesize printfs present in FIRRTL (implemented as ``printf``
statements) that would otherwise be lost in the FPGA synthesis flow. Rocket and
BOOM have printfs of their commit logs and other useful transaction
streams.
::
C0: 409 [1] pc=[008000004c] W[r10=0000000000000000][1] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402573] csrr a0, mhartid
C0: 410 [0] pc=[008000004c] W[r 0=0000000000000000][0] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402573] csrr a0, mhartid
C0: 411 [0] pc=[008000004c] W[r 0=0000000000000000][0] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402573] csrr a0, mhartid
C0: 412 [1] pc=[0080000050] W[r 0=0000000000000000][0] R[r10=0000000000000000] R[r 0=0000000000000000] inst=[00051063] bnez a0, pc + 0
C0: 413 [1] pc=[0080000054] W[r 5=0000000080000054][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000297] auipc t0, 0x0
C0: 414 [1] pc=[0080000058] W[r 5=0000000080000064][1] R[r 5=0000000080000054] R[r16=0000000000000003] inst=[01028293] addi t0, t0, 16
C0: 415 [1] pc=[008000005c] W[r 0=0000000000010000][1] R[r 5=0000000080000064] R[r 5=0000000080000064] inst=[30529073] csrw mtvec, t0
Synthesizing these printfs lets you capture the same logs on a running FireSim instance.
Enabling Printf Synthesis
----------------------------
To synthesize a printf, in your Chisel source you need to annotate the specific
printfs you'd like to capture. Presently, due to a limitation in Chisel and
FIRRTL's annotation system, you need to annotate the arguments to the printf, not the printf itself,
like so:
::
printf(midas.targetutils.SynthesizePrintf("x%d p%d 0x%x\n", rf_waddr, rf_waddr, rf_wdata))
Be judicious, as synthesizing many, frequently active printfs, will slow down your simulator.
Once your printfs have been annotated, to enable printf synthesis add the ``WithPrintfSynthesis`` Config to your
PLATFORM_CONFIG in SimConfigs.scala. During compilation, MIDAS will print the
number of printfs it's synthesized. In the target's generated header
(``<DESIGN>-const.h``), you'll find metadata for each of the printfs MIDAS synthesized.
This is passed as argument to the constructor of the ``synthesized_prints_t``
endpoint driver, which will be automatically instantiated in FireSim driver.
Runtime Arguments
-----------------
**+printfile**
Specifies the file into which the synthesized printf log should written.
**+print-start**
Specifies the target-cycle at which the printf trace should be captured in the
simulator. Since capturing high-bandwidth printf traces will slow down
simulation, this allows the user to reach the region-of-interest at full simulation speed.
**+print-end**
Specifies the target cycle at which to stop pulling the synthesized print
trace from the simulator.
**+print-human-readable**
By default, a captured printf trace will be written to file as a raw,
unformatted binary, as properly formatting the printf further slows the
simulator. Setting this will properly format the print data before writing
it to file.
Related Publications
--------------------
Printf synthesis was first presented in our FPL2018 paper, `DESSERT
<https://people.eecs.berkeley.edu/~biancolin/papers/dessert-fpl18.pdf>`_.

@ -1 +1 @@
Subproject commit 50d4a55873741d01f9381f6fa56aa6ced2a4bb5b
Subproject commit 71a361090c98b89455deb5fd90eba8f08b3b34e8

View File

@ -23,12 +23,13 @@ SBT_FLAGS ?= -J-Xmx16G -J-Xss8M -J-XX:MaxPermSize=256M -J-XX:MaxMetaspaceSize=51
firesim_base_dir := $(abspath .)
# Manage the FIRRTL dependency manually
FIRRTL_JAR ?= $(firesim_base_dir)/lib/firrtl.jar
FIRRTL_SUBMODULE_DIR ?= $(firesim_base_dir)/firrtl
FIRRTL_JAR ?= $(FIRRTL_SUBMODULE_DIR)/utils/bin/firrtl.jar
$(FIRRTL_JAR): $(shell find $(FIRRTL_SUBMODULE_DIR)/src/main/scala -iname "*.scala")
$(MAKE) -C $(FIRRTL_SUBMODULE_DIR) SBT="$(SBT) $(SBT_FLAGS)" root_dir=$(FIRRTL_SUBMODULE_DIR) build-scala
mkdir -p $(@D)
cp -p $(FIRRTL_SUBMODULE_DIR)/utils/bin/firrtl.jar $(FIRRTL_JAR)
touch $(FIRRTL_JAR)
mkdir -p $(firesim_base_dir)/lib
cp -p $(FIRRTL_JAR) $(firesim_base_dir)/lib/
firrtl: $(FIRRTL_JAR)
.PHONY: firrtl

@ -1 +1 @@
Subproject commit ba12915e9b93685107c503b3f91b96d491c48459
Subproject commit 380c233b43c2de53b0ee15a39e9364d438066b9f

@ -1 +1 @@
Subproject commit d9251aea8bc7c4f65ea46013e319d41cbdeb1944
Subproject commit 12991c628ce506cf462e5c5c24688bd99c5b2b74

View File

@ -11,6 +11,7 @@
#include "endpoints/sim_mem.h"
#include "endpoints/fpga_memory_model.h"
#include "endpoints/synthesized_assertions.h"
#include "endpoints/synthesized_prints.h"
firesim_top_t::firesim_top_t(int argc, char** argv)
{
@ -333,11 +334,30 @@ uint64_t host_mem_offset = -0x80000000LL;
#endif
#endif
// add more endpoints here
// There can only be one instance of assert and print widgets as their IO is
// uniquely generated by a FIRRTL transform
#ifdef ASSERTIONWIDGET_struct_guard
#ifdef ASSERTIONWIDGET_0_PRESENT
ASSERTIONWIDGET_0_substruct_create;
endpoints.push_back(new synthesized_assertions_t(this, ASSERTIONWIDGET_0_substruct));
add_endpoint(new synthesized_assertions_t(this, ASSERTIONWIDGET_0_substruct));
#endif
#endif
#ifdef PRINTWIDGET_struct_guard
#ifdef PRINTWIDGET_0_PRESENT
PRINTWIDGET_0_substruct_create;
print_endpoint = new synthesized_prints_t(this,
args,
PRINTWIDGET_0_substruct,
PRINTWIDGET_0_print_count,
PRINTWIDGET_0_token_bytes,
PRINTWIDGET_0_idle_cycles_mask,
PRINTWIDGET_0_print_offsets,
PRINTWIDGET_0_format_strings,
PRINTWIDGET_0_argument_counts,
PRINTWIDGET_0_argument_widths,
PRINTWIDGET_0_DMA_ADDR);
add_endpoint(print_endpoint);
#endif
#endif
// Add functions you'd like to periodically invoke on a paused simulator here.
@ -348,7 +368,7 @@ uint64_t host_mem_offset = -0x80000000LL;
bool firesim_top_t::simulation_complete() {
bool is_complete = false;
for (auto e: endpoints) {
for (auto &e: endpoints) {
is_complete |= e->terminate();
}
return is_complete;
@ -362,7 +382,7 @@ uint64_t firesim_top_t::profile_models(){
}
int firesim_top_t::exit_code(){
for (auto e: endpoints) {
for (auto &e: endpoints) {
if (e->exit_code())
return e->exit_code();
}
@ -371,11 +391,11 @@ int firesim_top_t::exit_code(){
void firesim_top_t::run() {
for (auto e: fpga_models) {
for (auto &e: fpga_models) {
e->init();
}
for (auto e: endpoints) {
for (auto &e: endpoints) {
e->init();
}
@ -394,7 +414,7 @@ void firesim_top_t::run() {
run_scheduled_tasks();
step(get_largest_stepsize(), false);
while(!done() && !simulation_complete()){
for (auto e: endpoints) e->tick();
for (auto &e: endpoints) e->tick();
}
}
@ -425,5 +445,8 @@ void firesim_top_t::run() {
for (auto e: fpga_models) {
e->finish();
}
#ifdef PRINTWIDGET_0_PRESENT
print_endpoint->finish();
#endif
}

View File

@ -1,11 +1,15 @@
#ifndef __FIRESIM_TOP_H
#define __FIRESIM_TOP_H
#include <memory>
#include "simif.h"
#include "endpoints/endpoint.h"
#include "endpoints/fpga_model.h"
#include "systematic_scheduler.h"
#include "endpoints/synthesized_prints.h"
class firesim_top_t: virtual simif_t, public systematic_scheduler_t
{
public:
@ -16,15 +20,19 @@ class firesim_top_t: virtual simif_t, public systematic_scheduler_t
protected:
void add_endpoint(endpoint_t* endpoint) {
endpoints.push_back(endpoint);
endpoints.push_back(std::unique_ptr<endpoint_t>(endpoint));
}
private:
// Memory mapped endpoints bound to software models
std::vector<endpoint_t*> endpoints;
std::vector<std::unique_ptr<endpoint_t> > endpoints;
// FPGA-hosted models with programmable registers & instrumentation
std::vector<FpgaModel*> fpga_models;
#ifdef PRINTWIDGET_struct_guard
synthesized_prints_t * print_endpoint;
#endif
// profile interval: # of cycles to advance before profiling instrumentation registers in models
uint64_t profile_interval = -1;
uint64_t profile_models();

View File

@ -27,6 +27,10 @@
#include "Stack.h"
#elif defined DESIGNNAME_AssertModule
#include "AssertModule.h"
#elif defined DESIGNNAME_PrintfModule
#include "PrintfModule.h"
#elif defined DESIGNNAME_NarrowPrintfModule
#include "NarrowPrintfModule.h"
#endif
class dut_emul_t:

View File

@ -0,0 +1,22 @@
//See LICENSE for license details.
#include "PrintfModule.h"
class NarrowPrintfModule_t: public print_module_t, virtual simif_t
{
public:
NarrowPrintfModule_t(int argc, char** argv): print_module_t(argc, argv) {};
virtual void run() {
print_endpoint->init();
poke(reset, 1);
poke(enable, 0);
step(1);
poke(enable, 1);
poke(reset, 0);
step(4);
// Test idle-cycle rollover
poke(enable, 0);
step(256);
poke(enable, 1);
run_and_collect_prints(256);
};
};

View File

@ -0,0 +1,54 @@
//See LICENSE for license details.
#include <memory>
#include "simif.h"
#include "endpoints/synthesized_prints.h"
class print_module_t: virtual simif_t
{
public:
std::unique_ptr<synthesized_prints_t> print_endpoint;
print_module_t(int argc, char** argv) {
PRINTWIDGET_0_substruct_create;
std::vector<std::string> args(argv + 1, argv + argc);
print_endpoint = std::unique_ptr<synthesized_prints_t>(new synthesized_prints_t(this,
args,
PRINTWIDGET_0_substruct,
PRINTWIDGET_0_print_count,
PRINTWIDGET_0_token_bytes,
PRINTWIDGET_0_idle_cycles_mask,
PRINTWIDGET_0_print_offsets,
PRINTWIDGET_0_format_strings,
PRINTWIDGET_0_argument_counts,
PRINTWIDGET_0_argument_widths,
PRINTWIDGET_0_DMA_ADDR));
};
void run_and_collect_prints(int cycles) {
step(cycles, false);
while (!done()) {
print_endpoint->tick();
}
print_endpoint->finish();
};
};
#ifdef DESIGNNAME_PrintfModule
class PrintfModule_t: public print_module_t, virtual simif_t
{
public:
PrintfModule_t(int argc, char** argv): print_module_t(argc, argv) {};
virtual void run() {
print_endpoint->init();
poke(reset, 1);
poke(a, 0);
poke(b, 0);
step(1);
poke(reset, 0);
step(1);
poke(a, 1);
poke(b, 1);
run_and_collect_prints(256);
};
};
#endif //DESIGNNAME_PrintfModule

View File

@ -34,7 +34,7 @@ driver_dir = $(firesim_base_dir)/src/main/cc
DRIVER_H = $(shell find $(driver_dir) -name "*.h")
DRIVER_CC := $(driver_dir)/midasexamples/Driver.cc
TARGET_CXX_FLAGS := -DDESIGNDRIVERCLASS=$(DESIGN)_t -DDESIGNNAME_$(DESIGN) -I$(driver_dir) -I$(driver_dir)/midasexamples
TARGET_CXX_FLAGS := -DDESIGNDRIVERCLASS=$(DESIGN)_t -DDESIGNNAME_$(DESIGN) -I$(driver_dir) -I$(driver_dir)/midasexamples -g
TARGET_LD_FLAGS :=
##########################

View File

@ -31,6 +31,12 @@ class WithSynthAsserts extends Config((site, here, up) => {
case EndpointKey => EndpointMap(Seq(new midas.widgets.AssertBundleEndpoint)) ++ up(EndpointKey)
})
// Experimental: mixing this in will enable print synthesis
class WithPrintfSynthesis extends Config((site, here, up) => {
case midas.SynthPrints => true
case EndpointKey => EndpointMap(Seq(new midas.widgets.PrintRecordEndpoint)) ++ up(EndpointKey)
})
class WithSerialWidget extends Config((site, here, up) => {
case EndpointKey => up(EndpointKey) ++ EndpointMap(Seq(new SimSerialIO))
})

View File

@ -10,6 +10,7 @@ import junctions._
// This is incomplete and must be mixed into a complete platform config
class DefaultMIDASConfig extends Config(new Config((site, here, up) => {
case SynthAsserts => true
case SynthPrints => true
}) ++ new Config(new firesim.firesim.WithDefaultMemModel))
class PointerChaserConfig extends Config((site, here, up) => {

View File

@ -18,7 +18,7 @@ trait GeneratorUtils {
Class.forName(s"firesim.midasexamples.${targetName}")
.getConstructors.head
.newInstance()
.asInstanceOf[chisel3.Module]
.asInstanceOf[chisel3.experimental.RawModule]
}
def midasParams = (platform match {
case midas.F1 => new Config(new DefaultMIDASConfig ++ new midas.F1Config)

View File

@ -0,0 +1,53 @@
//See LICENSE for license details.
package firesim.midasexamples
import chisel3._
import chisel3.util.LFSR16
import chisel3.experimental.MultiIOModule
import midas.targetutils.SynthesizePrintf
class PrintfModule extends MultiIOModule {
val a = IO(Input(Bool()))
val b = IO(Input(Bool()))
val cycle = RegInit(0.U(16.W))
when(a) { cycle := cycle + 1.U }
// Printf format strings must be prefixed with "SYNTHESIZED_PRINT CYCLE: %d"
// so they can be pulled out of RTL simulators log and sorted within a cycle
// As the printf order will be different betwen RTL simulator and synthesized stream
printf(SynthesizePrintf("SYNTHESIZED_PRINT CYCLE: %d\n", cycle))
val wideArgument = VecInit(Seq.fill(33)(WireInit(cycle))).asUInt
printf(SynthesizePrintf("SYNTHESIZED_PRINT CYCLE: %d wideArgument: %x\n", cycle, wideArgument)) // argument width > DMA width
val childInst = Module(new PrintfModuleChild)
childInst.c := a
childInst.cycle := cycle
printf(SynthesizePrintf("thi$!sn+taS/\neName", "SYNTHESIZED_PRINT CYCLE: %d constantArgument: %x\n", cycle, 1.U(8.W)))
}
class PrintfModuleChild extends MultiIOModule {
val c = IO(Input(Bool()))
val cycle = IO(Input(UInt(16.W)))
val lfsr = chisel3.util.LFSR16(c)
printf(SynthesizePrintf("SYNTHESIZED_PRINT CYCLE: %d LFSR: %x\n", cycle, lfsr))
//when (lsfr(0)) {
// printf(SynthesizePrintf(p"SYNTHESIZED_PRINT CYCLE: ${cycle} LFSR is odd"))
//}
}
class NarrowPrintfModule extends MultiIOModule {
val enable = IO(Input(Bool()))
val cycle = RegInit(0.U(12.W))
cycle := cycle + 1.U
when(LFSR16()(0) & LFSR16()(0) & enable) {
printf(SynthesizePrintf("SYNTHESIZED_PRINT CYCLE: %d\n", cycle(5,0)))
}
}

View File

@ -3,6 +3,7 @@ package firesim.midasexamples
import java.io.File
import scala.sys.process.{stringSeqToProcess, ProcessLogger}
import scala.io.Source
abstract class TutorialSuite(
val targetName: String, // See GeneratorUtils
@ -69,6 +70,26 @@ abstract class TutorialSuite(
ignore should s"pass in ${testEnv}" in { }
}
}
// Checks that the synthesized print log in ${genDir}/${synthPrintLog} matches the
// printfs from the RTL simulator
def diffSynthesizedPrints(synthPrintLog: String) {
behavior of "synthesized print log"
it should "match the logs produced by the verilated design" in {
def printLines(filename: File): Seq[String] = {
val lines = Source.fromFile(filename).getLines.toList
lines.filter(_.startsWith("SYNTHESIZED_PRINT")).sorted
}
val verilatedOutput = printLines(new File(outDir, s"/${targetName}.verilator.out"))
val synthPrintOutput = printLines(new File(genDir, s"/${synthPrintLog}"))
assert(verilatedOutput.size == synthPrintOutput.size && verilatedOutput.nonEmpty)
for ( (vPrint, sPrint) <- verilatedOutput.zip(synthPrintOutput) ) {
assert(vPrint == sPrint)
}
}
}
clean
mkdirs
compile
@ -90,3 +111,11 @@ class StackF1Test extends TutorialSuite("Stack", midas.F1, 8)
class RiscF1Test extends TutorialSuite("Risc", midas.F1, 64)
class RiscSRAMF1Test extends TutorialSuite("RiscSRAM", midas.F1, 64)
class AssertModuleF1Test extends TutorialSuite("AssertModule", midas.F1)
class PrintfModuleF1Test extends TutorialSuite("PrintfModule", midas.F1,
simulationArgs = Seq("+print-human-readable", "+printfile=synthprinttest.out")) {
diffSynthesizedPrints("synthprinttest.out")
}
class NarrowPrintfModuleF1Test extends TutorialSuite("NarrowPrintfModule", midas.F1,
simulationArgs = Seq("+print-human-readable", "+printfile=synthprinttest.out")) {
diffSynthesizedPrints("synthprinttest.out")
}