Skip to content

Commit

Permalink
[Profiling] Modifications to TDCC and scripts for first pass profiling (
Browse files Browse the repository at this point in the history
#2040)

* add new option

* Barebones hack version created... Now I want to write to a file

* Fixing clippy errors

* Small scripts for first pass VCD parsing

* very hacky way to only get group names

* Bare bones python script to output group to number of cycles

* Fix clippy errors

* Output JSON to file

* remove unused import

* Remove hack for obtaining group to states

* Small wrapper script to run TDCC, simulation, and output cycle counts

* Fix clippy errors

* Small changes to scripts

* Get Enables to directly produce FSMInfo

* First pass code for collecting FSMInfos across multiple TDCC groups

* Fix clippy errors

* remove debugging prints

* Clean things up before making PR

* Clean up more comments

* Allow passing Calyx arguments from command line

* Update fud2 tests to have empty args variable

* Replace script to get no-opt vcd with fud2 command

* README for scripts in tools/vcd-parsing

* Documentation comments for new structs

* Use Id instead of String for JSON structs
  • Loading branch information
ayakayorihiro authored May 27, 2024
1 parent 0916c48 commit b789140
Show file tree
Hide file tree
Showing 17 changed files with 197 additions and 11 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ results.xml
!cider-dap/calyxDebug/tsconfig.json

# btor2i ignore
tools/btor2/btor2i/build/
tools/btor2/btor2i/build/

# vcd-parsing tmp files ignore
tools/vcd-parsing/tmp/
tools/vcd-parsing/logs
85 changes: 75 additions & 10 deletions calyx-opt/src/passes/top_down_compile_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use crate::traversal::{
Action, ConstructVisitor, Named, ParseVal, PassOpt, VisResult, Visitor,
};
use calyx_ir::{self as ir, GetAttributes, LibrarySignatures, Printer, RRC};
use calyx_ir::{build_assignments, guard, structure};
use calyx_utils::CalyxResult;
use calyx_ir::{build_assignments, guard, structure, Id};
use calyx_utils::Error;
use calyx_utils::{CalyxResult, OutputFile};
use ir::Nothing;
use itertools::Itertools;
use petgraph::graph::DiGraph;
use std::collections::HashMap;
use serde::Serialize;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::rc::Rc;

Expand Down Expand Up @@ -210,6 +211,8 @@ fn compute_unique_ids(con: &mut ir::Control, cur_state: u64) -> u64 {

/// Represents the dyanmic execution schedule of a control program.
struct Schedule<'b, 'a: 'b> {
/// A mapping from groups to corresponding FSM state ids
pub groups_to_states: HashSet<FSMStateInfo>,
/// Assigments that should be enabled in a given state.
pub enables: HashMap<u64, Vec<ir::Assignment<Nothing>>>,
/// Transition from one state to another when the guard is true.
Expand All @@ -219,9 +222,35 @@ struct Schedule<'b, 'a: 'b> {
pub builder: &'b mut ir::Builder<'a>,
}

/// Information to be serialized for a single FSM
#[derive(PartialEq, Eq, Hash, Clone, Serialize)]
struct FSMInfo {
#[serde(serialize_with = "id_serialize_passthrough")]
pub component: Id,
#[serde(serialize_with = "id_serialize_passthrough")]
pub group: Id,
pub states: Vec<FSMStateInfo>,
}

/// Mapping of FSM state ids to corresponding group names
#[derive(PartialEq, Eq, Hash, Clone, Serialize)]
struct FSMStateInfo {
id: u64,
#[serde(serialize_with = "id_serialize_passthrough")]
group: Id,
}

fn id_serialize_passthrough<S>(id: &Id, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
id.to_string().serialize(ser)
}

impl<'b, 'a> From<&'b mut ir::Builder<'a>> for Schedule<'b, 'a> {
fn from(builder: &'b mut ir::Builder<'a>) -> Self {
Schedule {
groups_to_states: HashSet::new(),
enables: HashMap::new(),
transitions: Vec::new(),
builder,
Expand Down Expand Up @@ -279,7 +308,11 @@ impl<'b, 'a> Schedule<'b, 'a> {

/// Implement a given [Schedule] and return the name of the [ir::Group] that
/// implements it.
fn realize_schedule(self, dump_fsm: bool) -> RRC<ir::Group> {
fn realize_schedule(
self,
dump_fsm: bool,
fsm_groups: &mut HashSet<FSMInfo>,
) -> RRC<ir::Group> {
self.validate();

let group = self.builder.add_group("tdcc");
Expand All @@ -291,6 +324,13 @@ impl<'b, 'a> Schedule<'b, 'a> {
));
}

// Keep track of groups to FSM state id information for dumping to json
fsm_groups.insert(FSMInfo {
component: self.builder.component.name,
group: group.borrow().name(),
states: self.groups_to_states.iter().cloned().collect_vec(),
});

let final_state = self.last_state();
let fsm_size = get_bit_width_from(
final_state + 1, /* represent 0..final_state */
Expand Down Expand Up @@ -397,6 +437,7 @@ impl Schedule<'_, '_> {
match con {
// See explanation of FSM states generated in [ir::TopDownCompileControl].
ir::Control::Enable(ir::Enable { group, attributes }) => {

let cur_state = attributes.get(NODE_ID).unwrap_or_else(|| panic!("Group `{}` does not have node_id information", group.borrow().name()));
// If there is exactly one previous transition state with a `true`
// guard, then merge this state into previous state.
Expand All @@ -408,6 +449,9 @@ impl Schedule<'_, '_> {
(cur_state, preds)
};

// Add group to mapping for emitting group JSON info
self.groups_to_states.insert(FSMStateInfo { id: cur_state, group: group.borrow().name() });

let not_done = !guard!(group["done"]);
let signal_on = self.builder.add_constant(1, 1);

Expand Down Expand Up @@ -770,8 +814,12 @@ impl Schedule<'_, '_> {
pub struct TopDownCompileControl {
/// Print out the FSM representation to STDOUT
dump_fsm: bool,
/// Output a JSON FSM representation to file if specified
dump_fsm_json: Option<OutputFile>,
/// Enable early transitions
early_transitions: bool,
/// Bookkeeping for FSM ids for groups across all FSMs in the program
fsm_groups: HashSet<FSMInfo>,
}

impl ConstructVisitor for TopDownCompileControl {
Expand All @@ -783,7 +831,9 @@ impl ConstructVisitor for TopDownCompileControl {

Ok(TopDownCompileControl {
dump_fsm: opts[&"dump-fsm"].bool(),
dump_fsm_json: opts[&"dump-fsm-json"].not_null_outstream(),
early_transitions: opts[&"early-transitions"].bool(),
fsm_groups: HashSet::new(),
})
}

Expand All @@ -809,6 +859,12 @@ impl Named for TopDownCompileControl {
ParseVal::Bool(false),
PassOpt::parse_bool,
),
PassOpt::new(
"dump-fsm-json",
"Write the state machine implementing the schedule to a JSON file",
ParseVal::OutStream(OutputFile::Null),
PassOpt::parse_outstream,
),
PassOpt::new(
"early-transitions",
"Experimental: Enable early transitions for group enables",
Expand Down Expand Up @@ -855,7 +911,8 @@ impl Visitor for TopDownCompileControl {
let mut sch = Schedule::from(&mut builder);
sch.calculate_states_seq(s, self.early_transitions)?;
// Compile schedule and return the group.
let seq_group = sch.realize_schedule(self.dump_fsm);
let seq_group =
sch.realize_schedule(self.dump_fsm, &mut self.fsm_groups);

// Add NODE_ID to compiled group.
let mut en = ir::Control::enable(seq_group);
Expand All @@ -881,7 +938,8 @@ impl Visitor for TopDownCompileControl {

// Compile schedule and return the group.
sch.calculate_states_if(i, self.early_transitions)?;
let if_group = sch.realize_schedule(self.dump_fsm);
let if_group =
sch.realize_schedule(self.dump_fsm, &mut self.fsm_groups);

// Add NODE_ID to compiled group.
let mut en = ir::Control::enable(if_group);
Expand All @@ -907,7 +965,8 @@ impl Visitor for TopDownCompileControl {
sch.calculate_states_while(w, self.early_transitions)?;

// Compile schedule and return the group.
let if_group = sch.realize_schedule(self.dump_fsm);
let if_group =
sch.realize_schedule(self.dump_fsm, &mut self.fsm_groups);

// Add NODE_ID to compiled group.
let mut en = ir::Control::enable(if_group);
Expand Down Expand Up @@ -949,7 +1008,7 @@ impl Visitor for TopDownCompileControl {
_ => {
let mut sch = Schedule::from(&mut builder);
sch.calculate_states(con, self.early_transitions)?;
sch.realize_schedule(self.dump_fsm)
sch.realize_schedule(self.dump_fsm, &mut self.fsm_groups)
}
};

Expand Down Expand Up @@ -1020,8 +1079,14 @@ impl Visitor for TopDownCompileControl {
let mut sch = Schedule::from(&mut builder);
// Add assignments for the final states
sch.calculate_states(&control.borrow(), self.early_transitions)?;
let comp_group = sch.realize_schedule(self.dump_fsm);

let comp_group =
sch.realize_schedule(self.dump_fsm, &mut self.fsm_groups);
if let Some(json_out_file) = &self.dump_fsm_json {
let _ = serde_json::to_writer_pretty(
json_out_file.get_write(),
&self.fsm_groups,
);
}
Ok(Action::change(ir::Control::enable(comp_group)))
}
}
2 changes: 2 additions & 0 deletions calyx-utils/src/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// use serde::{Serialize, Serializer};

pub type GSym = symbol_table::GlobalSymbol;

/// Represents an identifier in a Calyx program
Expand Down
1 change: 1 addition & 0 deletions fud2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ fn setup_calyx(
"calyx.exe",
"$calyx-base/target/debug/calyx",
)?;
e.config_var_or("args", "calyx.args", "")?;
e.rule(
"calyx",
"$calyx-exe -l $calyx-base -b $backend $args $in > $out",
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_debug.snap
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ cycle-limit = 500000000
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_firrtl_verilog.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_icarus_dat.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_icarus_vcd.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_interp_dat.snap
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ cycle-limit = 500000000
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_verilator_dat.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_verilator_vcd.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_verilog.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_xrt-trace_vcd.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
1 change: 1 addition & 0 deletions fud2/tests/snapshots/tests__emit@calyx_xrt_dat.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rule get-rsrc
# Calyx compiler
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
Expand Down
13 changes: 13 additions & 0 deletions tools/vcd-parsing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Profiling Scripts

This directory contains scripts for a first pass at profiling cycle counts in Calyx programs. It contains:

- `get-profile-counts-info.sh`: A wrapper script that produces a cycle counts estimate given a Calyx program
- `parse-vcd.py`: A helper script that reads in a VCD file and a JSON FSM file to generate cycle count estimates

### Usage

- To run the profiling pipeline, you can run `get-profile-counts-info.sh` providing the Calyx file and the Memory data. ex) From the Calyx root directory
```
bash tools/vcd-parsing/get-profile-counts-info.sh examples/tutorial/language-tutorial-compute.futil examples/tutorial/data.json
```
44 changes: 44 additions & 0 deletions tools/vcd-parsing/get-profile-counts-info.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Wrapper script for running TDCC, running simulation, and obtaining cycle counts information

if [ $# -ne 2 ]; then
echo "USAGE: bash $0 INPUT_FILE SIM_DATA_JSON"
exit
fi

SCRIPT_DIR=$( cd $( dirname $0 ) && pwd )
SCRIPT_NAME=$( echo "$0" | rev | cut -d/ -f1 | rev )
CALYX_DIR=$( dirname $( dirname ${SCRIPT_DIR} ) )
TMP_DIR=${SCRIPT_DIR}/tmp
TMP_VERILOG=${TMP_DIR}/no-opt-verilog.sv
FSM_JSON=${TMP_DIR}/fsm.json
VCD_FILE=${TMP_DIR}/trace-info.vcd
LOGS_DIR=${SCRIPT_DIR}/logs
mkdir -p ${TMP_DIR} ${LOGS_DIR}

rm -f ${TMP_VERILOG} ${FSM_JSON}

INPUT_FILE=$1
SIM_DATA_JSON=$2

# 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} -p no-opt -x tdcc:dump-fsm-json="${FSM_JSON}"
set +o xtrace
) &> ${LOGS_DIR}/gol-tdcc

# Run simuation to get VCD
echo "[${SCRIPT_NAME}] Obtaining VCD file via simulation"
(
set -o xtrace
fud2 ${INPUT_FILE} -o ${VCD_FILE} -s calyx.args='-p no-opt' -s sim.data=${SIM_DATA_JSON}
set +o xtrace
) &> ${LOGS_DIR}/gol-vcd

# Run script to get cycle level counts
echo "[${SCRIPT_NAME}] Using FSM info and VCD file to obtain cycle level counts"
(
python3 ${SCRIPT_DIR}/parse-vcd.py ${VCD_FILE} ${FSM_JSON}
) # &> ${LOGS_DIR}/gol-process
Loading

0 comments on commit b789140

Please sign in to comment.