From b6fd5132abce68f9fe6c1aa6584d10cd28144e97 Mon Sep 17 00:00:00 2001 From: Ayaka Yorihiro <36107281+ayakayorihiro@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:42:22 -0400 Subject: [PATCH] [Profiler] fud2 support and processing instrumentation-produced cells (#2314) *Note: This PR is still a draft because I have some questions about the changes I made here that I wanted to ask about. Once those concerns are resolved I will turn this into a full PR.* ## This PR contains: - `fud2/scripts/profiler.rhai`: fud2 support for the profiler. I added a new state `flamegraph` (the `svg` file containing the flame graph) and a new operation called `profiler`, which takes a Calyx file and produces the profiled flame graph. - Updates to existing profiling scripts to read instrumented cell signals and allow toggling between profiling optimized and non-optimized Calyx programs. - Updated fud2 tests to reflect new state and operation. ## Usage First, clone https://github.com/brendangregg/FlameGraph and edit the `fud2` configuration file to specify the location of `flamegraph.pl`: ex) ``` [flamegraph] script = "/home/ayaka/projects/FlameGraph/flamegraph.pl" ``` To obtain a flame graph from a Calyx program, specify the output as a `svg` file. ex) ``` fud2 tests/correctness/while.futil -o while.svg -s sim.data=tests/correctness/while.futil ``` will produce the below flame graph. ![image](https://github.com/user-attachments/assets/b257ecb6-9f45-49b7-9555-f68e3212403e) To obtain a flame graph for the non-compiler-optimized version of the Calyx program, add `-s passes=no-opt`: ex) ``` fud2 tests/correctness/while.futil -o flame.svg -s sim.data=tests/correctness/while.futil.data -s passes=no-opt ``` --- .../src/passes/profiler_instrumentation.rs | 6 +- fud2/scripts/profiler.rhai | 86 +++++++++++ fud2/tests/snapshots/tests__list_ops.snap | 6 + fud2/tests/snapshots/tests__list_states.snap | 5 + .../snapshots/tests__test@plan_profiler.snap | 70 +++++++++ tools/component_cells/src/main.rs | 5 +- tools/profiler/create-visuals.py | 9 +- tools/profiler/get-profile-counts-info.sh | 135 ------------------ tools/profiler/parse-vcd.py | 55 +++---- 9 files changed, 203 insertions(+), 174 deletions(-) create mode 100644 fud2/scripts/profiler.rhai create mode 100644 fud2/tests/snapshots/tests__test@plan_profiler.snap delete mode 100644 tools/profiler/get-profile-counts-info.sh diff --git a/calyx-opt/src/passes/profiler_instrumentation.rs b/calyx-opt/src/passes/profiler_instrumentation.rs index 829861ec91..631cdb3eb3 100644 --- a/calyx-opt/src/passes/profiler_instrumentation.rs +++ b/calyx-opt/src/passes/profiler_instrumentation.rs @@ -44,18 +44,22 @@ impl Visitor for ProfilerInstrumentation { .iter() .map(|group| group.borrow().name()) .collect::>(); + let comp_name = comp.name; // for each group, construct a instrumentation cell and instrumentation assignment let mut asgn_and_cell = Vec::with_capacity(group_names.len()); { let mut builder = ir::Builder::new(comp, sigs); let one = builder.add_constant(1, 1); for group_name in group_names.into_iter() { - let name = format!("{}_probe", group_name); + // store group and component name (differentiate between groups of the same name under different components) + let name = format!("{}__{}_probe", group_name, comp_name); let inst_cell = builder.add_primitive(name, "std_wire", &[1]); let asgn: [ir::Assignment; 1] = build_assignments!( builder; inst_cell["in"] = ? one["out"]; ); + // the probes should be @control because they should have value 0 whenever the corresponding group is not active. + inst_cell.borrow_mut().add_attribute(BoolAttr::Control, 1); inst_cell.borrow_mut().add_attribute(BoolAttr::Protected, 1); asgn_and_cell.push((asgn[0].clone(), inst_cell)); } diff --git a/fud2/scripts/profiler.rhai b/fud2/scripts/profiler.rhai new file mode 100644 index 0000000000..7fe78e7dee --- /dev/null +++ b/fud2/scripts/profiler.rhai @@ -0,0 +1,86 @@ +import "calyx" as c; +import "verilator" as v; +import "rtl_sim" as sim; + +export const instrumented_verilog = state("verilog-instrumented", ["sv"]); +export const instrumented_sim = state("sim-instrumented", ["exe"]); +export const instrumented_vcd = state("vcd-instrumented", ["vcd"]); +export const flamegraph = state("flamegraph", ["svg"]); + +fn profiling_setup(e) { + e.var_("cells", "cells.json"); + e.var_("tdcc-json", "fsm.json"); // might not be necessary if we get rid of fsms? + + // series of passes after instrumentation? + e.config_var_or("passes", "profiler.compilation-passes", "all"); // set passes="no-opt" to run without optimizations + + // rules for preprocessing + + e.config_var_or("component_cells", "component_cells", "$calyx-base/target/debug/component_cells"); + e.rule("component-cells", "$component_cells -l $calyx-base $in > $out"); + + // rules for postprocessing + + // script to process vcd and attribute active cycles to every group/cell + e.var_("parse-vcd-script", "$calyx-base/tools/profiler/parse-vcd.py"); + e.rule("parse-vcd", "python3 $parse-vcd-script $in $tdcc-json $cells summary.csv $out"); + + // script to produce trace and visuals + e.var_("create-visuals-script", "$calyx-base/tools/profiler/create-visuals.py"); + e.rule("create-visuals", "python3 $create-visuals-script $in $cells timeline.json fsm-timeline.json $out fsm-flame.folded frequency.folded components.folded fsm-components.folded"); + + e.config_var("flamegraph-script", "flamegraph.script"); + e.rule("produce-flame-graph", "$flamegraph-script $in > $out"); + + // Standalone Verilog testbench. copied from testbench + e.rsrc("tb.sv"); + +} + +fn calyx_to_flamegraph(e, input, output) { + // instrument calyx and produce verilog + let instrumented_verilog = "instrumented.sv"; + e.build_cmd(["$cells"], "component-cells", [input], []); + e.build_cmd([instrumented_verilog], "calyx", [input], []); + e.arg("backend", "verilog"); + e.arg("args", " -p static-inline -p compile-static -p compile-repeat -p par-to-seq -p compile-invoke -p profiler-instrumentation -p $passes -x tdcc:dump-fsm-json=fsm.json"); + + let instrumented_sim = "instrumented.exe"; + // verilog --> sim; adapted from verilator::verilator_build() + let verilator_out_dir = "verilator-out"; + let sim_bin = `${verilator_out_dir}/Vtoplevel`; + e.build_cmd( + [sim_bin], + "verilator-compile-standalone-tb", + [instrumented_verilog], + ["tb.sv"], + ); + e.arg("out-dir", verilator_out_dir); + e.build("cp", sim_bin, instrumented_sim); + + let instrumented_vcd = "instrumented.vcd"; + // sim --> vcd; adapted from rtl_sim + e.build_cmd( + ["sim.log", instrumented_vcd], + "sim-run", + [instrumented_sim, "$datadir"], + [], + ); + e.arg("bin", instrumented_sim); + e.arg("args", `+NOTRACE=0 +OUT=${instrumented_vcd}`); + + // vcd --> flamegraph + let elems_profiled_json = "elems-profiled.json"; + let flamegraph_folded = "flamegraph.folded"; + e.build_cmd([elems_profiled_json], "parse-vcd", [instrumented_vcd], []); + e.build_cmd([flamegraph_folded], "create-visuals", [elems_profiled_json], ["$cells"]); + e.build_cmd([output], "produce-flame-graph", [flamegraph_folded], []); +} + +op( + "profiler", + [c::calyx_setup, profiling_setup, v::verilator_setup, sim::sim_setup], + c::calyx_state, + flamegraph, + |e, input, output| calyx_to_flamegraph(e, input, output) +); diff --git a/fud2/tests/snapshots/tests__list_ops.snap b/fud2/tests/snapshots/tests__list_ops.snap index ed1652224d..76578bfbe1 100644 --- a/fud2/tests/snapshots/tests__list_ops.snap +++ b/fud2/tests/snapshots/tests__list_ops.snap @@ -1,5 +1,6 @@ --- source: fud2/tests/tests.rs +snapshot_kind: text --- [ ( @@ -97,6 +98,11 @@ source: fud2/tests/tests.rs "calyx", "primitive-uses-json", ), + ( + "profiler", + "calyx", + "flamegraph", + ), ( "simulate", "sim", diff --git a/fud2/tests/snapshots/tests__list_states.snap b/fud2/tests/snapshots/tests__list_states.snap index 8872d6c337..fbc730d110 100644 --- a/fud2/tests/snapshots/tests__list_states.snap +++ b/fud2/tests/snapshots/tests__list_states.snap @@ -1,5 +1,6 @@ --- source: fud2/tests/tests.rs +snapshot_kind: text --- [ "calyx", @@ -10,11 +11,15 @@ source: fud2/tests/tests.rs "dat", "firrtl", "firrtl-with-primitives", + "flamegraph", "mrxl", "primitive-uses-json", "sim", + "sim-instrumented", "vcd", + "vcd-instrumented", "verilog", + "verilog-instrumented", "verilog-noverify", "verilog-refmem", "verilog-refmem-noverify", diff --git a/fud2/tests/snapshots/tests__test@plan_profiler.snap b/fud2/tests/snapshots/tests__test@plan_profiler.snap new file mode 100644 index 0000000000..7b42740530 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_profiler.snap @@ -0,0 +1,70 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: profiler" +snapshot_kind: text +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +calyx-base = /test/calyx +calyx-exe = $calyx-base/target/debug/calyx +args = +rule calyx + command = $calyx-exe -l $calyx-base -b $backend $args $in > $out +rule calyx-pass + command = $calyx-exe -l $calyx-base -p $pass $args $in > $out +flags = -p none +rule calyx-with-flags + command = $calyx-exe -l $calyx-base $flags $args $in > $out + +cells = cells.json +tdcc-json = fsm.json +passes = all +component_cells = $calyx-base/target/debug/component_cells +rule component-cells + command = $component_cells -l $calyx-base $in > $out +parse-vcd-script = $calyx-base/tools/profiler/parse-vcd.py +rule parse-vcd + command = python3 $parse-vcd-script $in $tdcc-json $cells summary.csv $out +create-visuals-script = $calyx-base/tools/profiler/create-visuals.py +rule create-visuals + command = python3 $create-visuals-script $in $cells timeline.json fsm-timeline.json $out fsm-flame.folded frequency.folded components.folded fsm-components.folded + +verilator = verilator +cycle-limit = 500000000 +rule verilator-compile-standalone-tb + command = $verilator $in tb.sv --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir +rule verilator-compile-custom-tb + command = $verilator $in tb.sv memories.sv --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir +rule cp + command = cp $in $out + +python = python3 +build json-dat.py: get-rsrc +rule hex-data + command = $python json-dat.py --from-json $in $out +rule json-data + command = $python json-dat.py --to-json $out $in +sim_data = /test/data.json +datadir = sim_data +build $datadir: hex-data $sim_data | json-dat.py +rule sim-run + command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out +cycle-limit = 500000000 + +build $cells: component-cells /input.ext +build instrumented.sv: calyx /input.ext + backend = verilog + args = -p static-inline -p compile-static -p compile-repeat -p par-to-seq -p compile-invoke -p profiler-instrumentation -p $passes -x tdcc:dump-fsm-json=fsm.json +build verilator-out/Vtoplevel: verilator-compile-standalone-tb instrumented.sv | tb.sv + out-dir = verilator-out +build instrumented.exe: cp verilator-out/Vtoplevel +build sim.log instrumented.vcd: sim-run instrumented.exe $datadir + bin = instrumented.exe + args = +NOTRACE=0 +OUT=instrumented.vcd +build elems-profiled.json: parse-vcd instrumented.vcd +build flamegraph.folded: create-visuals elems-profiled.json | $cells +build /output.ext: produce-flame-graph flamegraph.folded + +default /output.ext diff --git a/tools/component_cells/src/main.rs b/tools/component_cells/src/main.rs index 8b1be9eb40..67d2f89fe9 100644 --- a/tools/component_cells/src/main.rs +++ b/tools/component_cells/src/main.rs @@ -6,6 +6,8 @@ use serde::Serialize; use std::path::{Path, PathBuf}; use std::{collections::HashSet, io}; +/// Tool to obtain list of names and original component names for all non-primitive cells in each component. + #[derive(FromArgs)] /// Path for library and path for file to read from struct Args { @@ -26,9 +28,6 @@ fn read_path(path: &str) -> Result { Ok(Path::new(path).into()) } -#[derive(Default)] -pub struct ComponentCellsBackend; - fn main() -> CalyxResult<()> { let p: Args = argh::from_env(); diff --git a/tools/profiler/create-visuals.py b/tools/profiler/create-visuals.py index 50061d4daa..1c279380d4 100644 --- a/tools/profiler/create-visuals.py +++ b/tools/profiler/create-visuals.py @@ -41,7 +41,13 @@ def get_active_group(self): elif len(self.active_groups) == 1: return self.active_groups[0] else: - raise Exception(f'Component {self.component} is parallel! Active groups: {self.active_groups}') + # concatenate all parallel active groups + sorted_active_groups = list(sorted(self.active_groups)) + acc = sorted_active_groups[0] + for group in sorted_active_groups[1:]: + suffix = group.split(".")[-1] + acc += "/" + suffix + return acc """ Returns the identifier of this stack: either the full name of the active group, or the full name of the cell if no groups are active. @@ -266,7 +272,6 @@ def create_timeline_stacks(trace, main_component): cell_to_stackframe_info["TOP.toplevel"] = (2, 1) for i in trace: - print(trace[i]) active_this_cycle = set() # Start from the bottom up. Parent is the previous stack! parent = "MAIN" diff --git a/tools/profiler/get-profile-counts-info.sh b/tools/profiler/get-profile-counts-info.sh deleted file mode 100644 index 06c3bc39b3..0000000000 --- a/tools/profiler/get-profile-counts-info.sh +++ /dev/null @@ -1,135 +0,0 @@ -# Wrapper script for running TDCC, running simulation, obtaining cycle counts information, and producing flame graphs to visualize - -if [ $# -lt 2 ]; then - echo "USAGE: bash $0 INPUT_FILE SIM_DATA_JSON [OUT_CSV]" - exit -fi - -SCRIPT_DIR=$( cd $( dirname $0 ) && pwd ) -SCRIPT_NAME=$( echo "$0" | rev | cut -d/ -f1 | rev ) -CALYX_DIR=$( dirname $( dirname ${SCRIPT_DIR} ) ) - -INPUT_FILE=$1 -SIM_DATA_JSON=$2 -name=$( echo "${INPUT_FILE}" | rev | cut -d/ -f1 | rev | cut -d. -f1 ) -DATA_DIR=${SCRIPT_DIR}/data/${name} -TMP_DIR=${DATA_DIR}/generated-data -if [ $# -ge 3 ]; then - OUT_CSV=$3 -else - OUT_CSV=${TMP_DIR}/summary.csv -fi - -FLAMEGRAPH_DIR=${SCRIPT_DIR}/fg-tmp - -if [ ! -d ${FLAMEGRAPH_DIR} ]; then - ( - cd ${SCRIPT_DIR} - git clone git@github.com:brendangregg/FlameGraph.git fg-tmp - ) -fi - -TMP_VERILOG=${TMP_DIR}/no-opt-verilog.sv -FSM_JSON=${TMP_DIR}/fsm.json -CELLS_JSON=${TMP_DIR}/cells.json -OUT_JSON=${TMP_DIR}/dump.json -TIMELINE_VIEW_JSON=${TMP_DIR}/timeline.json -FSM_TIMELINE_VIEW_JSON=${TMP_DIR}/fsm-timeline.json -FLAME_GRAPH_FOLDED=${TMP_DIR}/flame.folded -FSM_FLAME_GRAPH_FOLDED=${TMP_DIR}/fsm-flame.folded -FREQUENCY_FLAME_GRAPH_FOLDED=${TMP_DIR}/frequency-flame.folded -COMPONENTS_FOLDED=${TMP_DIR}/components.folded -FSM_COMPONENTS_FOLDED=${TMP_DIR}/fsm-components.folded -VCD_FILE=${TMP_DIR}/trace.vcd -LOGS_DIR=${DATA_DIR}/logs -if [ -d ${DATA_DIR} ]; then - rm -rf ${DATA_DIR} # clean out directory for run each time -fi -mkdir -p ${TMP_DIR} ${LOGS_DIR} -rm -f ${TMP_DIR}/* ${LOGS_DIR}/* # remove data from last run - -CALYX_ARGS=" -p static-inline -p compile-static -p compile-repeat -p par-to-seq -p no-opt " - - -# Run TDCC to get the FSM info -echo "[${SCRIPT_NAME}] Obtaining FSM info from TDCC" -( - cd ${CALYX_DIR} - set -o xtrace - cargo run -- ${INPUT_FILE} ${CALYX_ARGS} -x tdcc:dump-fsm-json="${FSM_JSON}" - set +o xtrace -) &> ${LOGS_DIR}/gol-tdcc - -if [ ! -f ${FSM_JSON} ]; then - echo "[${SCRIPT_NAME}] Failed to generate ${FSM_JSON}! Exiting" - exit 1 -fi - -# Run component-cells backend to get cell information -echo "[${SCRIPT_NAME}] Obtaining cell information from component-cells backend" -( - cd ${CALYX_DIR} - set -o xtrace - cargo run --manifest-path tools/component_cells/Cargo.toml ${INPUT_FILE} -o ${CELLS_JSON} -) &> ${LOGS_DIR}/gol-cells - -if [ ! -f ${CELLS_JSON} ]; then - echo "[${SCRIPT_NAME}] Failed to generate ${CELLS_JSON}! Exiting" - exit 1 -fi - -# Run simuation to get VCD -echo "[${SCRIPT_NAME}] Obtaining VCD file via simulation" -( - set -o xtrace - fud2 ${INPUT_FILE} -o ${VCD_FILE} --through verilator -s calyx.args="${CALYX_ARGS}" -s sim.data=${SIM_DATA_JSON} - set +o xtrace -) &> ${LOGS_DIR}/gol-vcd - -if [ ! -f ${VCD_FILE} ]; then - echo "[${SCRIPT_NAME}] Failed to generate ${VCD_FILE}! Exiting" - exit 1 -fi - -# Run script to get cycle level counts -echo "[${SCRIPT_NAME}] Using FSM info and VCD file to obtain cycle level counts" -( - set -o xtrace - python3 ${SCRIPT_DIR}/parse-vcd.py ${VCD_FILE} ${FSM_JSON} ${CELLS_JSON} ${OUT_CSV} ${OUT_JSON} - set +o xtrace -) &> ${LOGS_DIR}/gol-process - -if [ "$4" == "-d" ]; then - cat ${LOGS_DIR}/gol-process | grep -v Writing # exclude lines that show paths -else - tail -3 ${LOGS_DIR}/gol-process | head -2 # last line is the set +o xtrace, which we don't need to show -fi - -echo "[${SCRIPT_NAME}] Writing visualization files" -( - set -o xtrace - python3 ${SCRIPT_DIR}/create-visuals.py ${OUT_JSON} ${CELLS_JSON} ${TIMELINE_VIEW_JSON} ${FSM_TIMELINE_VIEW_JSON} ${FLAME_GRAPH_FOLDED} ${FSM_FLAME_GRAPH_FOLDED} ${FREQUENCY_FLAME_GRAPH_FOLDED} ${COMPONENTS_FOLDED} ${FSM_COMPONENTS_FOLDED} - set +o xtrace -) &> ${LOGS_DIR}/gol-visuals - -echo "[${SCRIPT_NAME}] Creating flame graph svg" -( - set -o xtrace - for opt in "" "--inverted" "--reverse"; do - if [ "${opt}" == "" ]; then - filename=flame - else - filename=flame"${opt:1}" - fi - ${FLAMEGRAPH_DIR}/flamegraph.pl ${opt} --countname="cycles" ${FLAME_GRAPH_FOLDED} > ${TMP_DIR}/${filename}.svg - echo - ${FLAMEGRAPH_DIR}/flamegraph.pl ${opt} --countname="cycles" ${FSM_FLAME_GRAPH_FOLDED} > ${TMP_DIR}/fsm-${filename}.svg - echo - ${FLAMEGRAPH_DIR}/flamegraph.pl ${opt} --countname="times active" ${FREQUENCY_FLAME_GRAPH_FOLDED} > ${TMP_DIR}/frequency-${filename}.svg - echo - ${FLAMEGRAPH_DIR}/flamegraph.pl ${opt} --countname="times active" ${COMPONENTS_FOLDED} > ${TMP_DIR}/components-${filename}.svg - echo - ${FLAMEGRAPH_DIR}/flamegraph.pl ${opt} --countname="times active" ${FSM_COMPONENTS_FOLDED} > ${TMP_DIR}/fsm-components-${filename}.svg - done - set +o xtrace -) &> ${LOGS_DIR}/gol-flamegraph diff --git a/tools/profiler/parse-vcd.py b/tools/profiler/parse-vcd.py index ce2dc236d0..8c2d79d7a2 100644 --- a/tools/profiler/parse-vcd.py +++ b/tools/profiler/parse-vcd.py @@ -11,7 +11,7 @@ class ProfilingInfo: def __init__(self, name, component, fsm_name=None, fsm_values=None, tdcc_group_name=None, is_cell=False): self.name = name self.fsm_name = fsm_name - self.fsm_values = fsm_values + self.fsm_values = list(sorted(fsm_values)) if fsm_values is not None else None self.total_cycles = 0 self.closed_segments = [] # Segments will be (start_time, end_time) self.current_segment = None @@ -95,25 +95,20 @@ def end_current_segment(self, curr_clock_cycle): class VCDConverter(vcdvcd.StreamParserCallbacks): - def __init__(self, fsms, single_enable_names, tdcc_groups, fsm_group_maps, main_component, cells): + def __init__(self, fsms, tdcc_groups, fsm_group_maps, main_component, cells): super().__init__() self.main_component = main_component self.fsms = fsms - self.single_enable_names = single_enable_names.keys() + self.single_enable_names = set() # Recording the first cycle when the TDCC group became active self.tdcc_group_active_cycle = {tdcc_group_name : -1 for tdcc_group_name in tdcc_groups} # Map from a TDCC group to all FSMs that depend on it. maybe a 1:1 mapping self.tdcc_group_to_dep_fsms = tdcc_groups # Group name --> ProfilingInfo object self.profiling_info = {} - self.signal_to_curr_value = {fsm : -1 for fsm in fsms} for group in fsm_group_maps: # Differentiate FSM versions from ground truth versions self.profiling_info[f"{group}FSM"] = ProfilingInfo(group, fsm_group_maps[group]["component"], fsm_group_maps[group]["fsm"], fsm_group_maps[group]["ids"], fsm_group_maps[group]["tdcc-group-name"]) - for single_enable_group in single_enable_names: - self.profiling_info[single_enable_group] = ProfilingInfo(single_enable_group, single_enable_names[single_enable_group]) - self.signal_to_curr_value[f"{single_enable_group}_go"] = -1 - self.signal_to_curr_value[f"{single_enable_group}_done"] = -1 self.cells = set(cells.keys()) for cell in cells: self.profiling_info[cell] = ProfilingInfo(cell, cells[cell], is_cell=True) @@ -127,9 +122,6 @@ def enddefinitions(self, vcd, signals, cur_sig_vals): names = [remove_size_from_name(e[0]) for e in refs] signal_id_dict = {sid : [] for sid in vcd.references_to_ids.values()} # one id can map to multiple signal names since wires are connected - # main_go_name = f"{self.main_component}.go" - # signal_id_dict[vcd.references_to_ids[main_go_name]] = [main_go_name] - clock_name = f"{self.main_component}.clk" if clock_name not in names: print("Can't find the clock? Exiting...") @@ -154,11 +146,12 @@ def enddefinitions(self, vcd, signals, cur_sig_vals): for fsm in self.fsms: if name.startswith(f"{fsm}.out["): signal_id_dict[sid].append(name) - for single_enable_group in self.single_enable_names: - if name.startswith(f"{single_enable_group}_go.out["): - signal_id_dict[sid].append(name) - if name.startswith(f"{single_enable_group}_done.out["): - signal_id_dict[sid].append(name) + if "_probe_out" in name: # instrumentation probes are "___probe" + group_component_split = name.split("_probe_out")[0].split("__") + group_name = group_component_split[0] + self.single_enable_names.add(group_name) + self.profiling_info[group_name] = ProfilingInfo(group_name, group_component_split[1]) + signal_id_dict[sid].append(name) # don't need to check for signal ids that don't pertain to signals we're interested in self.signal_id_to_names = {k:v for k,v in signal_id_dict.items() if len(v) > 0} @@ -247,11 +240,11 @@ def postprocess(self): if signal_name.endswith(".done") and value == 1: # cells have .go and .done cell = signal_name.split(".done")[0] self.profiling_info[cell].end_current_segment(clock_cycles) - if "_go" in signal_name and value == 1: - group = "_".join(signal_name.split("_")[0:-1]) + if "_probe_out" in signal_name and value == 1: # instrumented group started being active + group = signal_name.split("_probe_out")[0].split("__")[0] self.profiling_info[group].start_new_segment(clock_cycles) - elif "_done" in signal_name and value == 1: - group = "_".join(signal_name.split("_")[0:-1]) + elif "_probe_out" in signal_name and value == 0: # instrumented group stopped being active + group = signal_name.split("_probe_out")[0].split("__")[0] self.profiling_info[group].end_current_segment(clock_cycles) elif "fsm" in signal_name: fsm = ".".join(signal_name.split(".")[0:-1]) @@ -302,9 +295,8 @@ def read_component_cell_names_json(json_file): return full_main_component, components_to_cells # Reads json generated by TDCC (via dump-fsm-json option) to produce initial group information -def remap_tdcc_json(json_file, components_to_cells): - profiling_infos = json.load(open(json_file)) - group_names = {} # all groups (to record ground truth). Maps to the group's component (needed for stacks) +def remap_tdcc_json(tdcc_json_file, components_to_cells): + profiling_infos = json.load(open(tdcc_json_file)) cells_to_components = {} # go and done info are needed for cells. cell --> component name tdcc_groups = {} # TDCC-generated groups that manage control flow using FSMs. maps to all fsms that map to the tdcc group fsm_group_maps = {} # fsm-managed groups info (fsm register, TDCC group that manages fsm, id of group within fsm) @@ -325,18 +317,13 @@ def remap_tdcc_json(json_file, components_to_cells): if tdcc_group not in tdcc_groups: # Keep track of the TDCC group to figure out when first group starts tdcc_groups[tdcc_group] = set() tdcc_groups[tdcc_group].add(fsm_name) - group_names[group_name] = fsm["component"] else: fsm_group_maps[group_name]["ids"].append(state["id"]) - else: - component = profiling_info["SingleEnable"]["component"] - for cell in components_to_cells[component]: # get all possibilities of cells - group_names[cell + "." + profiling_info["SingleEnable"]["group"]] = component for component in components_to_cells: for cell in components_to_cells[component]: cells_to_components[cell] = component - return fsms, group_names, tdcc_groups, fsm_group_maps, cells_to_components + return fsms, tdcc_groups, fsm_group_maps, cells_to_components def output_result(out_csv, dump_out_json, converter): print(f"Total clock cycles: {converter.clock_cycles}") @@ -372,10 +359,10 @@ def output_result(out_csv, dump_out_json, converter): writer.writeheader() writer.writerows(csv_acc) -def main(vcd_filename, groups_json_file, cells_json_file, out_csv, dump_out_json): +def main(vcd_filename, tdcc_json_file, cells_json_file, out_csv, dump_out_json): main_component, components_to_cells = read_component_cell_names_json(cells_json_file) - fsms, group_names, tdcc_group_names, fsm_group_maps, cells = remap_tdcc_json(groups_json_file, components_to_cells) - converter = VCDConverter(fsms, group_names, tdcc_group_names, fsm_group_maps, main_component, cells) + fsms, tdcc_group_names, fsm_group_maps, cells = remap_tdcc_json(tdcc_json_file, components_to_cells) + converter = VCDConverter(fsms, tdcc_group_names, fsm_group_maps, main_component, cells) vcdvcd.VCDVCD(vcd_filename, callbacks=converter, store_tvs=False) converter.postprocess() output_result(out_csv, dump_out_json, converter) @@ -398,6 +385,8 @@ def main(vcd_filename, groups_json_file, cells_json_file, out_csv, dump_out_json ] print(f"Usage: {sys.argv[0]} {' '.join(args_desc)}") print("TDCC_JSON: Run Calyx with `tdcc:dump-fsm-json` option") - print("CELLS_JSON: Run Calyx with `component-cells` backend") + print("CELLS_JSON: Run the `component_cells` tool") + print("GROUPS_JSON: Run the `component_groups` tool") print("If SUMMARY_OUT_CSV is STDOUT, then summary CSV will be printed to stdout") + print("DUMP_OUT_JSON: output json file for group-specific") sys.exit(-1)