From 5e8ad382852e2d259845e8b6f41ecd8540c0d8be Mon Sep 17 00:00:00 2001 From: calebmkim <55243755+calebmkim@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:27:03 -0400 Subject: [PATCH] Reduce Register Usage for Separate Static Islands (#1632) * first draft of single fsm compilation * it works * fixed nested repeat bug * added documentation * added test cases * removed unnecessary doc * bug fix (needed more conflicts) * small documentation changes * fixed test and removed debug stmt * modified test case to catch bug * removed unnecessary files * code cleaning * renamed function --- calyx-ir/src/component.rs | 13 + calyx-opt/src/analysis/graph_coloring.rs | 10 +- calyx-opt/src/default_passes.rs | 1 + calyx-opt/src/passes/cell_share.rs | 8 +- calyx-opt/src/passes/compile_static.rs | 405 ++++++++++++++++-- examples/futil/dot-product.expect | 111 +++-- examples/futil/simple.expect | 30 +- examples/futil/vectorized-add.expect | 101 +++-- runt.toml | 4 +- .../static-islands/par-static-islands.expect | 5 + .../static-islands/par-static-islands.futil | 47 ++ .../par-static-islands.futil.data | 12 + .../seq-mem-dot-product.expect | 0 .../seq-mem-dot-product.futil | 0 .../seq-mem-dot-product.futil.data | 0 ...roup_go.expect => rewrite-group-go.expect} | 2 +- ..._group_go.futil => rewrite-group-go.futil} | 0 .../rewrite-static-while-nested.expect | 58 ++- .../rewrite-static-while.expect | 54 ++- .../compile-static/separate-fsms.expect | 113 +++++ .../passes/compile-static/separate-fsms.futil | 45 ++ 21 files changed, 802 insertions(+), 217 deletions(-) create mode 100644 tests/correctness/static-islands/par-static-islands.expect create mode 100644 tests/correctness/static-islands/par-static-islands.futil create mode 100644 tests/correctness/static-islands/par-static-islands.futil.data rename tests/correctness/{repeat => static-islands}/seq-mem-dot-product.expect (100%) rename tests/correctness/{repeat => static-islands}/seq-mem-dot-product.futil (100%) rename tests/correctness/{repeat => static-islands}/seq-mem-dot-product.futil.data (100%) rename tests/passes/compile-static/{rewrite_group_go.expect => rewrite-group-go.expect} (100%) rename tests/passes/compile-static/{rewrite_group_go.futil => rewrite-group-go.futil} (100%) create mode 100644 tests/passes/compile-static/separate-fsms.expect create mode 100644 tests/passes/compile-static/separate-fsms.futil diff --git a/calyx-ir/src/component.rs b/calyx-ir/src/component.rs index d14d609886..165489cbbe 100644 --- a/calyx-ir/src/component.rs +++ b/calyx-ir/src/component.rs @@ -207,6 +207,19 @@ impl Component { self.cells.find(name) } + /// Return a reference to the cell with `name` if present. + pub fn find_guaranteed_cell(&self, name: S) -> RRC + where + S: Into + std::fmt::Debug + Copy, + { + self.cells.find(name).unwrap_or_else(|| { + unreachable!( + "called find_certain_cell on {:?} but it wasn't found", + name + ) + }) + } + /// Construct a non-conflicting name using the Component's namegenerator. pub fn generate_name(&mut self, prefix: S) -> Id where diff --git a/calyx-opt/src/analysis/graph_coloring.rs b/calyx-opt/src/analysis/graph_coloring.rs index 58842dbe2e..dcea4f6f24 100644 --- a/calyx-opt/src/analysis/graph_coloring.rs +++ b/calyx-opt/src/analysis/graph_coloring.rs @@ -74,7 +74,13 @@ where /// Given an `ordering` of `T`s, find a mapping from nodes to `T`s such /// that no node has a neighbor with the same `T`. - pub fn color_greedy(&mut self, bound: Option) -> HashMap { + /// `keep_self_color` indicates whether to keep the mapping of the node to + /// itself in the returned HashMap (since nodes are "colors") + pub fn color_greedy( + &mut self, + bound: Option, + keep_self_color: bool, + ) -> HashMap { let mut all_colors: BTreeMap = BTreeMap::new(); let mut coloring: HashMap = HashMap::new(); let always_share = bound.is_none(); @@ -159,7 +165,7 @@ where coloring .into_iter() .map(|(n1, n2)| (rev_map[&n1].clone(), rev_map[&n2].clone())) - .filter(|(a, b)| a != b) + .filter(|(a, b)| (a != b) || keep_self_color) .collect() } diff --git a/calyx-opt/src/default_passes.rs b/calyx-opt/src/default_passes.rs index 59e19d1c22..0190e9f83b 100644 --- a/calyx-opt/src/default_passes.rs +++ b/calyx-opt/src/default_passes.rs @@ -106,6 +106,7 @@ impl PassManager { [ StaticInliner, MergeAssign, // Static inliner generates lots of assigns + DeadGroupRemoval, // Static inliner generates lots of dead groups SimplifyStaticGuards, CompileStatic, TopDownStaticTiming, diff --git a/calyx-opt/src/passes/cell_share.rs b/calyx-opt/src/passes/cell_share.rs index d5e5ae4966..46ac420f41 100644 --- a/calyx-opt/src/passes/cell_share.rs +++ b/calyx-opt/src/passes/cell_share.rs @@ -442,9 +442,9 @@ impl Visitor for CellShare { let parent_b = par_thread_map.get(live_b).unwrap(); if live_a != live_b && parent_a == parent_b { - // we have to check par_timing_map - // to see whether liveness overlaps - // for dynamic pars, liveness_overlaps() returns + // We have to check par_timing_map + // to see whether liveness overlaps. + // For dynamic pars, liveness_overlaps() returns // true no matter what. if self.par_timing_map.liveness_overlaps( parent_a, live_a, live_b, a, b, @@ -497,7 +497,7 @@ impl Visitor for CellShare { if graph.has_nodes() { coloring.extend( graph - .color_greedy(*bound) + .color_greedy(*bound, false) .iter() .map(|(a, b)| (*a, comp.find_cell(*b).unwrap())), ); diff --git a/calyx-opt/src/passes/compile_static.rs b/calyx-opt/src/passes/compile_static.rs index 0cd8735308..a103c6d39e 100644 --- a/calyx-opt/src/passes/compile_static.rs +++ b/calyx-opt/src/passes/compile_static.rs @@ -1,11 +1,12 @@ use super::math_utilities::get_bit_width_from; +use crate::analysis::GraphColoring; use crate::traversal::{Action, Named, VisResult, Visitor}; use calyx_ir as ir; use calyx_ir::{guard, structure, GetAttributes}; use calyx_utils::Error; use ir::{build_assignments, Nothing, StaticTiming, RRC}; use itertools::Itertools; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ops::Not; use std::rc::Rc; @@ -16,6 +17,8 @@ pub struct CompileStatic { reset_early_map: HashMap, /// maps group that has an FSM that resets early to its dynamic "wrapper" group name. wrapper_map: HashMap, + /// maps fsm names to their corresponding signal_reg + signal_reg_map: HashMap, /// maps reset_early_group names to (fsm name, fsm_width) fsm_info_map: HashMap, /// rewrites `static_group[go]` to `dynamic_group[go]` @@ -103,6 +106,39 @@ fn make_assign_dyn( } } +// Given a list of `static_groups`, find the group named `name`. +// If there is no such group, then there is an unreachable! error. +fn find_static_group( + name: &ir::Id, + static_groups: &[ir::RRC], +) -> ir::RRC { + Rc::clone( + static_groups + .iter() + .find(|static_group| static_group.borrow().name() == name) + .unwrap_or_else(|| { + unreachable!("couldn't find static group {name}") + }), + ) +} + +// Given an input static_group `sgroup`, finds the names of all of the groups +// that it triggers through their go hole. +// E.g., if `sgroup` has assignments that write to `sgroup1[go]` and `sgroup2[go]` +// then return `{sgroup1, sgroup2}` +// NOTE: assumes that static groups will only write the go holes of other static +// groups, and never dynamic groups +fn get_go_writes(sgroup: &ir::RRC) -> HashSet { + let mut uses = HashSet::new(); + for asgn in &sgroup.borrow().assignments { + let dst = asgn.dst.borrow(); + if dst.is_hole() && dst.name == "go" { + uses.insert(dst.get_parent_name()); + } + } + uses +} + impl CompileStatic { // returns an "early reset" group based on the information given // in the arguments. @@ -115,12 +151,17 @@ impl CompileStatic { sgroup_name: ir::Id, latency: u64, attributes: ir::Attributes, + fsm: ir::RRC, builder: &mut ir::Builder, ) -> ir::RRC { - let fsm_size = - get_bit_width_from(latency + 1 /* represent 0..latency */); + let fsm_name = fsm.borrow().name(); + let fsm_size = fsm + .borrow() + .find("out") + .unwrap_or_else(|| unreachable!("no `out` port on {fsm_name}")) + .borrow() + .width; structure!( builder; - let fsm = prim std_reg(fsm_size); // done hole will be undefined bc of early reset let ud = prim undef(1); let signal_on = constant(1,1); @@ -172,7 +213,9 @@ impl CompileStatic { fsm_name: &ir::Id, fsm_width: u64, group_name: &ir::Id, + signal_reg: ir::RRC, builder: &mut ir::Builder, + add_continuous_assigns: bool, ) -> ir::RRC { // get the groups/fsm necessary to build the wrapper group let early_reset_group = builder @@ -194,7 +237,6 @@ impl CompileStatic { }); structure!( builder; - let signal_reg = prim std_reg(1); let state_zero = constant(0, fsm_width); let signal_on = constant(1, 1); let signal_off = constant(0, 1); @@ -226,15 +268,17 @@ impl CompileStatic { // group[done] = fsm.out == 0 & signal_reg.out ? 1'd1 g["done"] = first_state_and_signal ? signal_on["out"]; ); - // continuous assignments to reset signal_reg back to 0 when the wrapper is done - let continuous_assigns = build_assignments!( - builder; - // when (fsm == 0 & signal_reg is high), which is the done condition of the wrapper, - // reset the signal_reg back to low - signal_reg["write_en"] = first_state_and_signal ? signal_on["out"]; - signal_reg["in"] = first_state_and_signal ? signal_off["out"]; - ); - builder.add_continuous_assignments(continuous_assigns.to_vec()); + if add_continuous_assigns { + // continuous assignments to reset signal_reg back to 0 when the wrapper is done + let continuous_assigns = build_assignments!( + builder; + // when (fsm == 0 & signal_reg is high), which is the done condition of the wrapper, + // reset the signal_reg back to low + signal_reg["write_en"] = first_state_and_signal ? signal_on["out"]; + signal_reg["in"] = first_state_and_signal ? signal_off["out"]; + ); + builder.add_continuous_assignments(continuous_assigns.to_vec()); + } g.borrow_mut().assignments = group_assigns.to_vec(); g.borrow_mut().attributes = early_reset_group.borrow().attributes.clone(); @@ -321,6 +365,257 @@ impl CompileStatic { wrapper_group.borrow_mut().assignments.extend(assignments); wrapper_group } + + // Gets all of the triggered static groups within `c`, and adds it to `cur_names`. + // Relies on sgroup_uses_map to take into account groups that are triggered through + // their `go` hole. + fn get_used_sgroups( + c: &ir::Control, + cur_names: &mut HashSet, + sgroup_uses_map: &HashMap>, + ) { + match c { + ir::Control::Empty(_) + | ir::Control::Enable(_) + | ir::Control::Invoke(_) => (), + ir::Control::Static(sc) => { + let ir::StaticControl::Enable(s) = sc else { + unreachable!("Non-Enable Static Control should have been compiled away. Run {} to do this", crate::passes::StaticInliner::name()); + }; + let group_name = s.group.borrow().name(); + if let Some(sgroup_uses) = sgroup_uses_map.get(&group_name) { + cur_names.extend(sgroup_uses); + } + cur_names.insert(group_name); + } + ir::Control::Par(ir::Par { stmts, .. }) + | ir::Control::Seq(ir::Seq { stmts, .. }) => { + for stmt in stmts { + Self::get_used_sgroups(stmt, cur_names, sgroup_uses_map); + } + } + ir::Control::Repeat(ir::Repeat { body, .. }) + | ir::Control::While(ir::While { body, .. }) => { + Self::get_used_sgroups(body, cur_names, sgroup_uses_map); + } + ir::Control::If(if_stmt) => { + Self::get_used_sgroups( + &if_stmt.tbranch, + cur_names, + sgroup_uses_map, + ); + Self::get_used_sgroups( + &if_stmt.fbranch, + cur_names, + sgroup_uses_map, + ); + } + } + } + + /// Given control `c`, adds conflicts to `conflict_graph` between all + /// static groups that are executed in separate threads of the same par block. + /// `sgroup_uses_map` maps: + /// static group names -> all of the static groups that it triggers the go ports + /// of (even recursively). + /// Example: group A {B[go] = 1;} group B {C[go] = 1} group C{} + /// Would map: A -> {B,C} and B -> {C} + fn add_par_conflicts( + c: &ir::Control, + sgroup_uses_map: &HashMap>, + conflict_graph: &mut GraphColoring, + ) { + match c { + ir::Control::Empty(_) + | ir::Control::Enable(_) + | ir::Control::Invoke(_) + | ir::Control::Static(_) => (), + ir::Control::Seq(seq) => { + for stmt in &seq.stmts { + Self::add_par_conflicts( + stmt, + sgroup_uses_map, + conflict_graph, + ); + } + } + ir::Control::Repeat(ir::Repeat { body, .. }) + | ir::Control::While(ir::While { body, .. }) => { + Self::add_par_conflicts(body, sgroup_uses_map, conflict_graph) + } + ir::Control::If(if_stmt) => { + Self::add_par_conflicts( + &if_stmt.tbranch, + sgroup_uses_map, + conflict_graph, + ); + Self::add_par_conflicts( + &if_stmt.fbranch, + sgroup_uses_map, + conflict_graph, + ); + } + ir::Control::Par(par) => { + // sgroup_conflict_vec is a vec of HashSets. + // Each entry of the vec corresponds to a par thread, and holds + // all of the groups executed in that thread. + let mut sgroup_conflict_vec = Vec::new(); + for stmt in &par.stmts { + let mut used_sgroups = HashSet::new(); + Self::get_used_sgroups( + stmt, + &mut used_sgroups, + sgroup_uses_map, + ); + sgroup_conflict_vec.push(used_sgroups); + } + for (thread1_sgroups, thread2_sgroups) in + sgroup_conflict_vec.iter().tuple_combinations() + { + for sgroup1 in thread1_sgroups { + for sgroup2 in thread2_sgroups { + conflict_graph.insert_conflict(sgroup1, sgroup2); + } + } + } + // Necessary to add conflicts between nested pars + for stmt in &par.stmts { + Self::add_par_conflicts( + stmt, + sgroup_uses_map, + conflict_graph, + ); + } + } + } + } + + /// Given an `sgroup_uses_map`, which maps: + /// static group names -> all of the static groups that it triggers the go ports + /// of (even recursively). + /// Example: group A {B[go] = 1;} group B {C[go] = 1} group C{} + /// Would map: A -> {B,C} and B -> {C} + /// Adds conflicts between any groups triggered at the same time based on + /// `go` port triggering. + fn add_go_port_conflicts( + sgroup_uses_map: &HashMap>, + conflict_graph: &mut GraphColoring, + ) { + for (sgroup, sgroup_uses) in sgroup_uses_map { + for sgroup_use in sgroup_uses { + conflict_graph.insert_conflict(sgroup_use, sgroup); + } + // If multiple groups are triggered by the same group, then + // we conservatively add a conflict between such groups + for (sgroup_use1, sgroup_use2) in + sgroup_uses.iter().tuple_combinations() + { + conflict_graph.insert_conflict(sgroup_use1, sgroup_use2); + } + } + } + + // Given a "coloring" of static group names -> their "colors", + // instantiate one fsm per color and return a hashmap that maps + // fsm names -> groups that it handles + fn build_fsm_mapping( + coloring: HashMap, + static_groups: &[ir::RRC], + builder: &mut ir::Builder, + ) -> HashMap> { + // "reverse" the coloring to map colors -> static group_names + let mut color_to_groups: HashMap> = + HashMap::new(); + for (group, color) in coloring { + color_to_groups.entry(color).or_default().insert(group); + } + // Need deterministic ordering for testing. + let mut vec_color_to_groups: Vec<(ir::Id, HashSet)> = + color_to_groups.into_iter().collect(); + vec_color_to_groups + .sort_by(|(color1, _), (color2, _)| color1.cmp(color2)); + vec_color_to_groups.into_iter().map(|(color, group_names)| { + // For each color, build an FSM that has the number of bits required + // for the largest latency in `group_names` + let max_latency = group_names + .iter() + .map(|g| { + find_static_group(g, static_groups).borrow() + .latency + }) + .max().unwrap_or_else(|| unreachable!("group {color} had no corresponding groups in its coloring map") + ); + let fsm_size = get_bit_width_from( + max_latency + 1, /* represent 0..latency */ + ); + structure!( builder; + let fsm = prim std_reg(fsm_size); + ); + let fsm_name = fsm.borrow().name(); + (fsm_name, group_names) + }).collect() + } + + // helper to `build_sgroup_uses_map` + // `parent_group` is the group that we are "currently" analyzing + // `full_group_ancestry` is the "ancestry of the group we are analyzing" + // Example: group A {B[go] = 1;} group B {C[go] = 1} group C{}, and `parent_group` + // is B, then ancestry would be B and A. + // `cur_mapping` is the current_mapping for `sgroup_uses_map` + // `group_names` is a vec of group_names. Once we analyze a group, we should + // remove it from group_names + // `sgroups` is a vec of static groups. + fn update_sgroup_uses_map( + parent_group: &ir::Id, + full_group_ancestry: &mut HashSet, + cur_mapping: &mut HashMap>, + group_names: &mut HashSet, + sgroups: &Vec>, + ) { + let group_uses = + get_go_writes(&find_static_group(parent_group, sgroups)); + for group_use in group_uses { + for ancestor in full_group_ancestry.iter() { + cur_mapping.entry(*ancestor).or_default().insert(group_use); + } + full_group_ancestry.insert(group_use); + Self::update_sgroup_uses_map( + &group_use, + full_group_ancestry, + cur_mapping, + group_names, + sgroups, + ); + full_group_ancestry.remove(&group_use); + } + group_names.remove(parent_group); + } + + /// Builds an `sgroup_uses_map`, which maps: + /// static group names -> all of the static groups that it triggers the go ports + /// of (even recursively). + /// Example: group A {B[go] = 1;} group B {C[go] = 1} group C{} + /// Would map: A -> {B,C} and B -> {C} + fn build_sgroup_uses_map( + sgroups: &Vec>, + ) -> HashMap> { + let mut names: HashSet = sgroups + .iter() + .map(|sgroup| sgroup.borrow().name()) + .collect(); + let mut cur_mapping = HashMap::new(); + while !names.is_empty() { + let random_group = *names.iter().next().unwrap(); + Self::update_sgroup_uses_map( + &random_group, + &mut HashSet::from([random_group]), + &mut cur_mapping, + &mut names, + sgroups, + ) + } + cur_mapping + } } impl Visitor for CompileStatic { @@ -332,7 +627,32 @@ impl Visitor for CompileStatic { ) -> VisResult { let sgroups: Vec> = comp.get_static_groups_mut().drain().collect(); + // `sgroup_uses_map` builds a mapping of static groups -> groups that + // it (even indirectly) triggers the `go` port of. + let sgroup_uses_map = Self::build_sgroup_uses_map(&sgroups); + // Build conflict graph and get coloring. + let mut conflict_graph: GraphColoring = + GraphColoring::from(sgroups.iter().map(|g| g.borrow().name())); + Self::add_par_conflicts( + &comp.control.borrow(), + &sgroup_uses_map, + &mut conflict_graph, + ); + Self::add_go_port_conflicts(&sgroup_uses_map, &mut conflict_graph); + let coloring = conflict_graph.color_greedy(None, true); let mut builder = ir::Builder::new(comp, sigs); + // build Mappings of fsm names -> set of groups that it can handle. + let fsm_mappings = + Self::build_fsm_mapping(coloring, &sgroups, &mut builder); + let mut groups_to_fsms = HashMap::new(); + // "Reverses" fsm_mappings to map group names -> fsm cells + for (fsm_name, group_names) in fsm_mappings { + let fsm = builder.component.find_guaranteed_cell(fsm_name); + for group_name in group_names { + groups_to_fsms.insert(group_name, Rc::clone(&fsm)); + } + } + // create "early reset" dynamic groups that never reach set their done hole for sgroup in sgroups.iter() { let mut sgroup_ref = sgroup.borrow_mut(); @@ -345,6 +665,9 @@ impl Visitor for CompileStatic { sgroup_name, sgroup_latency, sgroup_attributes, + Rc::clone(groups_to_fsms.get(&sgroup_name).unwrap_or_else( + || unreachable!("{sgroup_name} has no corresponding fsm"), + )), &mut builder, ); // map the static group name -> early reset group name @@ -362,7 +685,7 @@ impl Visitor for CompileStatic { } // rewrite static_group[go] to early_reset_group[go] - // don't have to worrry about writing static_group[done] b/c static + // don't have to worry about writing static_group[done] b/c static // groups don't have done holes. comp.for_each_assignment(|assign| { assign.for_each_port(|port| { @@ -398,7 +721,7 @@ impl Visitor for CompileStatic { let early_reset_name = self.reset_early_map.get(&sgroup_name).unwrap_or_else(|| { unreachable!( - "group {} early reset wrapper has not been created", + "group {} early reset has not been created", sgroup_name ) }); @@ -409,12 +732,47 @@ impl Visitor for CompileStatic { // create the builder/cells that we need to create wrapper group let mut builder = ir::Builder::new(comp, sigs); let (fsm_name, fsm_width )= self.fsm_info_map.get(early_reset_name).unwrap_or_else(|| unreachable!("group {} has no correspondoing fsm in self.fsm_map", early_reset_name)); - let wrapper = Self::build_wrapper_group( - fsm_name, - *fsm_width, - early_reset_name, - &mut builder, - ); + // If we've already made a wrapper for a group that uses the same + // FSM, we can reuse the signal_reg. Otherwise, we must + // instantiate a new signal_reg. + let wrapper = match self.signal_reg_map.get(fsm_name) { + None => { + // Need to build the signal_reg and the continuous + // assignment that resets the signal_reg + structure!( builder; + let signal_reg = prim std_reg(1); + ); + self.signal_reg_map + .insert(*fsm_name, signal_reg.borrow().name()); + Self::build_wrapper_group( + fsm_name, + *fsm_width, + early_reset_name, + signal_reg, + &mut builder, + true, + ) + } + Some(reg_name) => { + // Already_built the signal_reg. + // We don't need to add continuous assignments + // that resets signal_reg. + let signal_reg = builder + .component + .find_cell(*reg_name) + .unwrap_or_else(|| { + unreachable!("signal reg {reg_name} found") + }); + Self::build_wrapper_group( + fsm_name, + *fsm_width, + early_reset_name, + signal_reg, + &mut builder, + false, + ) + } + }; self.wrapper_map .insert(*early_reset_name, wrapper.borrow().name()); wrapper @@ -444,7 +802,6 @@ impl Visitor for CompileStatic { /// } /// ... /// } - /// control { /// while l.out { /// A; @@ -479,7 +836,7 @@ impl Visitor for CompileStatic { let mut builder = ir::Builder::new(comp, sigs); let reset_group_name = self.get_reset_group_name(sc); - // get fsm for reset_group + // Get fsm for reset_group let (fsm, fsm_width) = self.fsm_info_map.get(reset_group_name).unwrap_or_else(|| unreachable!("group {} has no correspondoing fsm in self.fsm_map", reset_group_name)); let wrapper_group = self.build_wrapper_group_while( fsm, diff --git a/examples/futil/dot-product.expect b/examples/futil/dot-product.expect index e3cd96fdb2..b90294a1e5 100644 --- a/examples/futil/dot-product.expect +++ b/examples/futil/dot-product.expect @@ -17,14 +17,13 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { @data mult_pipe0 = std_mult_pipe(32); @external @data v0 = std_mem_d1(32, 1, 1); @generated comb_reg = std_reg(1); - @generated fsm = std_reg(1); + @generated fsm = std_reg(4); @generated ud = undef(1); - @generated adder = std_add(1); - @generated fsm7 = std_reg(4); - @generated ud7 = undef(1); - @generated adder7 = std_add(4); + @generated adder = std_add(4); + @generated ud0 = undef(1); + @generated adder0 = std_add(4); @generated signal_reg = std_reg(1); - @generated fsm9 = std_reg(2); + @generated fsm0 = std_reg(2); @generated invoke0_go = std_wire(1); @generated invoke0_done = std_wire(1); @generated early_reset_cond00_go = std_wire(1); @@ -39,86 +38,82 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { @generated tdcc_done = std_wire(1); } wires { - i0.write_en = invoke0_go.out | fsm7.out == 4'd7 & early_reset_static_seq_go.out ? 1'd1; + i0.write_en = invoke0_go.out | fsm.out == 4'd7 & early_reset_static_seq_go.out ? 1'd1; i0.clk = clk; i0.reset = reset; - i0.in = fsm7.out == 4'd7 & early_reset_static_seq_go.out ? add1.out; + i0.in = fsm.out == 4'd7 & early_reset_static_seq_go.out ? add1.out; i0.in = invoke0_go.out ? const0.out; early_reset_cond00_go.in = wrapper_early_reset_cond00_go.out ? 1'd1; - add1.left = fsm7.out == 4'd7 & early_reset_static_seq_go.out ? i0.out; - add1.right = fsm7.out == 4'd7 & early_reset_static_seq_go.out ? const3.out; + add1.left = fsm.out == 4'd7 & early_reset_static_seq_go.out ? i0.out; + add1.right = fsm.out == 4'd7 & early_reset_static_seq_go.out ? const3.out; done = tdcc_done.out ? 1'd1; - fsm.write_en = early_reset_cond00_go.out ? 1'd1; + fsm.write_en = early_reset_cond00_go.out | early_reset_static_seq_go.out ? 1'd1; fsm.clk = clk; fsm.reset = reset; - fsm.in = fsm.out != 1'd0 & early_reset_cond00_go.out ? adder.out; - fsm.in = fsm.out == 1'd0 & early_reset_cond00_go.out ? 1'd0; + fsm.in = fsm.out != 4'd0 & early_reset_cond00_go.out ? adder.out; + fsm.in = fsm.out == 4'd0 & early_reset_cond00_go.out | fsm.out == 4'd8 & early_reset_static_seq_go.out ? 4'd0; + fsm.in = fsm.out != 4'd8 & early_reset_static_seq_go.out ? adder0.out; adder.left = early_reset_cond00_go.out ? fsm.out; - adder.right = early_reset_cond00_go.out ? 1'd1; - add0.left = fsm7.out == 4'd6 & early_reset_static_seq_go.out ? v0.read_data; - add0.right = fsm7.out == 4'd6 & early_reset_static_seq_go.out ? B_read0_0.out; - v0.write_en = fsm7.out == 4'd6 & early_reset_static_seq_go.out ? 1'd1; + adder.right = early_reset_cond00_go.out ? 4'd1; + add0.left = fsm.out == 4'd6 & early_reset_static_seq_go.out ? v0.read_data; + add0.right = fsm.out == 4'd6 & early_reset_static_seq_go.out ? B_read0_0.out; + v0.write_en = fsm.out == 4'd6 & early_reset_static_seq_go.out ? 1'd1; v0.clk = clk; - v0.addr0 = fsm7.out == 4'd6 & early_reset_static_seq_go.out ? const2.out; + v0.addr0 = fsm.out == 4'd6 & early_reset_static_seq_go.out ? const2.out; v0.reset = reset; - v0.write_data = fsm7.out == 4'd6 & early_reset_static_seq_go.out ? add0.out; - comb_reg.write_en = early_reset_cond00_go.out | fsm7.out == 4'd8 & early_reset_static_seq_go.out ? 1'd1; + v0.write_data = fsm.out == 4'd6 & early_reset_static_seq_go.out ? add0.out; + comb_reg.write_en = early_reset_cond00_go.out | fsm.out == 4'd8 & early_reset_static_seq_go.out ? 1'd1; comb_reg.clk = clk; comb_reg.reset = reset; - comb_reg.in = early_reset_cond00_go.out | fsm7.out == 4'd8 & early_reset_static_seq_go.out ? le0.out; + comb_reg.in = early_reset_cond00_go.out | fsm.out == 4'd8 & early_reset_static_seq_go.out ? le0.out; early_reset_cond00_done.in = ud.out; - fsm9.write_en = fsm9.out == 2'd3 | fsm9.out == 2'd0 & invoke0_done.out & tdcc_go.out | fsm9.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out | fsm9.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 1'd1; - fsm9.clk = clk; - fsm9.reset = reset; - fsm9.in = fsm9.out == 2'd0 & invoke0_done.out & tdcc_go.out ? 2'd1; - fsm9.in = fsm9.out == 2'd3 ? 2'd0; - fsm9.in = fsm9.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 2'd3; - fsm9.in = fsm9.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out ? 2'd2; - while_wrapper_early_reset_static_seq_go.in = !while_wrapper_early_reset_static_seq_done.out & fsm9.out == 2'd2 & tdcc_go.out ? 1'd1; - fsm7.write_en = early_reset_static_seq_go.out ? 1'd1; - fsm7.clk = clk; - fsm7.reset = reset; - fsm7.in = fsm7.out == 4'd8 & early_reset_static_seq_go.out ? 4'd0; - fsm7.in = fsm7.out != 4'd8 & early_reset_static_seq_go.out ? adder7.out; - invoke0_go.in = !invoke0_done.out & fsm9.out == 2'd0 & tdcc_go.out ? 1'd1; + while_wrapper_early_reset_static_seq_go.in = !while_wrapper_early_reset_static_seq_done.out & fsm0.out == 2'd2 & tdcc_go.out ? 1'd1; + invoke0_go.in = !invoke0_done.out & fsm0.out == 2'd0 & tdcc_go.out ? 1'd1; tdcc_go.in = go; A0.clk = clk; - A0.addr0 = fsm7.out == 4'd0 & early_reset_static_seq_go.out ? i0.out; + A0.addr0 = fsm.out == 4'd0 & early_reset_static_seq_go.out ? i0.out; A0.reset = reset; + fsm0.write_en = fsm0.out == 2'd3 | fsm0.out == 2'd0 & invoke0_done.out & tdcc_go.out | fsm0.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out | fsm0.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 1'd1; + fsm0.clk = clk; + fsm0.reset = reset; + fsm0.in = fsm0.out == 2'd0 & invoke0_done.out & tdcc_go.out ? 2'd1; + fsm0.in = fsm0.out == 2'd3 ? 2'd0; + fsm0.in = fsm0.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 2'd3; + fsm0.in = fsm0.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out ? 2'd2; mult_pipe0.clk = clk; - mult_pipe0.left = fsm7.out >= 4'd1 & fsm7.out < 4'd4 & early_reset_static_seq_go.out ? A_read0_0.out; - mult_pipe0.go = fsm7.out >= 4'd1 & fsm7.out < 4'd4 & early_reset_static_seq_go.out ? 1'd1; + mult_pipe0.left = fsm.out >= 4'd1 & fsm.out < 4'd4 & early_reset_static_seq_go.out ? A_read0_0.out; + mult_pipe0.go = fsm.out >= 4'd1 & fsm.out < 4'd4 & early_reset_static_seq_go.out ? 1'd1; mult_pipe0.reset = reset; - mult_pipe0.right = fsm7.out >= 4'd1 & fsm7.out < 4'd4 & early_reset_static_seq_go.out ? B_read0_0.out; + mult_pipe0.right = fsm.out >= 4'd1 & fsm.out < 4'd4 & early_reset_static_seq_go.out ? B_read0_0.out; + adder0.left = early_reset_static_seq_go.out ? fsm.out; + adder0.right = early_reset_static_seq_go.out ? 4'd1; invoke0_done.in = i0.done; early_reset_static_seq_go.in = while_wrapper_early_reset_static_seq_go.out ? 1'd1; - le0.left = early_reset_cond00_go.out | fsm7.out == 4'd8 & early_reset_static_seq_go.out ? i0.out; - le0.right = early_reset_cond00_go.out | fsm7.out == 4'd8 & early_reset_static_seq_go.out ? const1.out; - signal_reg.write_en = fsm.out == 1'd0 & signal_reg.out | fsm.out == 1'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; + le0.left = early_reset_cond00_go.out | fsm.out == 4'd8 & early_reset_static_seq_go.out ? i0.out; + le0.right = early_reset_cond00_go.out | fsm.out == 4'd8 & early_reset_static_seq_go.out ? const1.out; + signal_reg.write_en = fsm.out == 4'd0 & signal_reg.out | fsm.out == 4'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; signal_reg.clk = clk; signal_reg.reset = reset; - signal_reg.in = fsm.out == 1'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; - signal_reg.in = fsm.out == 1'd0 & signal_reg.out ? 1'd0; + signal_reg.in = fsm.out == 4'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; + signal_reg.in = fsm.out == 4'd0 & signal_reg.out ? 1'd0; B0.clk = clk; - B0.addr0 = fsm7.out == 4'd0 & early_reset_static_seq_go.out ? i0.out; + B0.addr0 = fsm.out == 4'd0 & early_reset_static_seq_go.out ? i0.out; B0.reset = reset; - B_read0_0.write_en = (fsm7.out == 4'd0 | fsm7.out == 4'd5) & early_reset_static_seq_go.out ? 1'd1; + B_read0_0.write_en = (fsm.out == 4'd0 | fsm.out == 4'd5) & early_reset_static_seq_go.out ? 1'd1; B_read0_0.clk = clk; B_read0_0.reset = reset; - B_read0_0.in = fsm7.out == 4'd0 & early_reset_static_seq_go.out ? B0.read_data; - B_read0_0.in = fsm7.out == 4'd5 & early_reset_static_seq_go.out ? A_read0_0.out; - wrapper_early_reset_cond00_go.in = !wrapper_early_reset_cond00_done.out & fsm9.out == 2'd1 & tdcc_go.out ? 1'd1; - wrapper_early_reset_cond00_done.in = fsm.out == 1'd0 & signal_reg.out ? 1'd1; - early_reset_static_seq_done.in = ud7.out; - tdcc_done.in = fsm9.out == 2'd3 ? 1'd1; - while_wrapper_early_reset_static_seq_done.in = !comb_reg.out & fsm7.out == 4'd0 ? 1'd1; - A_read0_0.write_en = (fsm7.out == 4'd0 | fsm7.out == 4'd4) & early_reset_static_seq_go.out ? 1'd1; + B_read0_0.in = fsm.out == 4'd0 & early_reset_static_seq_go.out ? B0.read_data; + B_read0_0.in = fsm.out == 4'd5 & early_reset_static_seq_go.out ? A_read0_0.out; + wrapper_early_reset_cond00_go.in = !wrapper_early_reset_cond00_done.out & fsm0.out == 2'd1 & tdcc_go.out ? 1'd1; + wrapper_early_reset_cond00_done.in = fsm.out == 4'd0 & signal_reg.out ? 1'd1; + early_reset_static_seq_done.in = ud0.out; + tdcc_done.in = fsm0.out == 2'd3 ? 1'd1; + while_wrapper_early_reset_static_seq_done.in = !comb_reg.out & fsm.out == 4'd0 ? 1'd1; + A_read0_0.write_en = (fsm.out == 4'd0 | fsm.out == 4'd4) & early_reset_static_seq_go.out ? 1'd1; A_read0_0.clk = clk; A_read0_0.reset = reset; - A_read0_0.in = fsm7.out == 4'd0 & early_reset_static_seq_go.out ? A0.read_data; - A_read0_0.in = fsm7.out == 4'd4 & early_reset_static_seq_go.out ? mult_pipe0.out; - adder7.left = early_reset_static_seq_go.out ? fsm7.out; - adder7.right = early_reset_static_seq_go.out ? 4'd1; + A_read0_0.in = fsm.out == 4'd0 & early_reset_static_seq_go.out ? A0.read_data; + A_read0_0.in = fsm.out == 4'd4 & early_reset_static_seq_go.out ? mult_pipe0.out; } control {} } diff --git a/examples/futil/simple.expect b/examples/futil/simple.expect index 316dd1b3f4..4f61bc431b 100644 --- a/examples/futil/simple.expect +++ b/examples/futil/simple.expect @@ -2,9 +2,9 @@ import "primitives/core.futil"; import "primitives/binary_operators.futil"; static<5> component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { cells { - @generated fsm2 = std_reg(3); - @generated ud2 = undef(1); - @generated adder2 = std_add(3); + @generated fsm = std_reg(3); + @generated ud = undef(1); + @generated adder = std_add(3); @generated signal_reg = std_reg(1); @generated early_reset_static_seq_go = std_wire(1); @generated early_reset_static_seq_done = std_wire(1); @@ -13,21 +13,21 @@ static<5> component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done } wires { done = wrapper_early_reset_static_seq_done.out ? 1'd1; - fsm2.write_en = early_reset_static_seq_go.out ? 1'd1; - fsm2.clk = clk; - fsm2.reset = reset; - fsm2.in = fsm2.out != 3'd4 & early_reset_static_seq_go.out ? adder2.out; - fsm2.in = fsm2.out == 3'd4 & early_reset_static_seq_go.out ? 3'd0; - adder2.left = early_reset_static_seq_go.out ? fsm2.out; - adder2.right = early_reset_static_seq_go.out ? 3'd1; - wrapper_early_reset_static_seq_done.in = fsm2.out == 3'd0 & signal_reg.out ? 1'd1; + fsm.write_en = early_reset_static_seq_go.out ? 1'd1; + fsm.clk = clk; + fsm.reset = reset; + fsm.in = fsm.out != 3'd4 & early_reset_static_seq_go.out ? adder.out; + fsm.in = fsm.out == 3'd4 & early_reset_static_seq_go.out ? 3'd0; + adder.left = early_reset_static_seq_go.out ? fsm.out; + adder.right = early_reset_static_seq_go.out ? 3'd1; + wrapper_early_reset_static_seq_done.in = fsm.out == 3'd0 & signal_reg.out ? 1'd1; early_reset_static_seq_go.in = wrapper_early_reset_static_seq_go.out ? 1'd1; - signal_reg.write_en = fsm2.out == 3'd0 & signal_reg.out | fsm2.out == 3'd0 & !signal_reg.out & wrapper_early_reset_static_seq_go.out ? 1'd1; + signal_reg.write_en = fsm.out == 3'd0 & signal_reg.out | fsm.out == 3'd0 & !signal_reg.out & wrapper_early_reset_static_seq_go.out ? 1'd1; signal_reg.clk = clk; signal_reg.reset = reset; - signal_reg.in = fsm2.out == 3'd0 & !signal_reg.out & wrapper_early_reset_static_seq_go.out ? 1'd1; - signal_reg.in = fsm2.out == 3'd0 & signal_reg.out ? 1'd0; - early_reset_static_seq_done.in = ud2.out; + signal_reg.in = fsm.out == 3'd0 & !signal_reg.out & wrapper_early_reset_static_seq_go.out ? 1'd1; + signal_reg.in = fsm.out == 3'd0 & signal_reg.out ? 1'd0; + early_reset_static_seq_done.in = ud.out; wrapper_early_reset_static_seq_go.in = go; } control {} diff --git a/examples/futil/vectorized-add.expect b/examples/futil/vectorized-add.expect index b18b1ed149..005c8bc0f2 100644 --- a/examples/futil/vectorized-add.expect +++ b/examples/futil/vectorized-add.expect @@ -14,14 +14,13 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { @data i0 = std_reg(4); @control le0 = std_le(4); @generated comb_reg = std_reg(1); - @generated fsm = std_reg(1); + @generated fsm = std_reg(3); @generated ud = undef(1); - @generated adder = std_add(1); - @generated fsm4 = std_reg(3); - @generated ud4 = undef(1); - @generated adder4 = std_add(3); + @generated adder = std_add(3); + @generated ud0 = undef(1); + @generated adder0 = std_add(3); @generated signal_reg = std_reg(1); - @generated fsm6 = std_reg(2); + @generated fsm0 = std_reg(2); @generated invoke0_go = std_wire(1); @generated invoke0_done = std_wire(1); @generated early_reset_cond00_go = std_wire(1); @@ -36,79 +35,75 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { @generated tdcc_done = std_wire(1); } wires { - i0.write_en = invoke0_go.out | fsm4.out == 3'd2 & early_reset_static_seq_go.out ? 1'd1; + i0.write_en = invoke0_go.out | fsm.out == 3'd2 & early_reset_static_seq_go.out ? 1'd1; i0.clk = clk; i0.reset = reset; - i0.in = fsm4.out == 3'd2 & early_reset_static_seq_go.out ? add1.out; + i0.in = fsm.out == 3'd2 & early_reset_static_seq_go.out ? add1.out; i0.in = invoke0_go.out ? const0.out; early_reset_cond00_go.in = wrapper_early_reset_cond00_go.out ? 1'd1; - add1.left = fsm4.out == 3'd2 & early_reset_static_seq_go.out ? i0.out; - add1.right = fsm4.out == 3'd2 & early_reset_static_seq_go.out ? const2.out; + add1.left = fsm.out == 3'd2 & early_reset_static_seq_go.out ? i0.out; + add1.right = fsm.out == 3'd2 & early_reset_static_seq_go.out ? const2.out; done = tdcc_done.out ? 1'd1; - fsm.write_en = early_reset_cond00_go.out ? 1'd1; + fsm.write_en = early_reset_cond00_go.out | early_reset_static_seq_go.out ? 1'd1; fsm.clk = clk; fsm.reset = reset; - fsm.in = fsm.out != 1'd0 & early_reset_cond00_go.out ? adder.out; - fsm.in = fsm.out == 1'd0 & early_reset_cond00_go.out ? 1'd0; + fsm.in = fsm.out != 3'd0 & early_reset_cond00_go.out ? adder.out; + fsm.in = fsm.out != 3'd3 & early_reset_static_seq_go.out ? adder0.out; + fsm.in = fsm.out == 3'd0 & early_reset_cond00_go.out | fsm.out == 3'd3 & early_reset_static_seq_go.out ? 3'd0; adder.left = early_reset_cond00_go.out ? fsm.out; - adder.right = early_reset_cond00_go.out ? 1'd1; - fsm6.write_en = fsm6.out == 2'd3 | fsm6.out == 2'd0 & invoke0_done.out & tdcc_go.out | fsm6.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out | fsm6.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 1'd1; - fsm6.clk = clk; - fsm6.reset = reset; - fsm6.in = fsm6.out == 2'd0 & invoke0_done.out & tdcc_go.out ? 2'd1; - fsm6.in = fsm6.out == 2'd3 ? 2'd0; - fsm6.in = fsm6.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 2'd3; - fsm6.in = fsm6.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out ? 2'd2; - add0.left = fsm4.out == 3'd1 & early_reset_static_seq_go.out ? A_read0_0.out; - add0.right = fsm4.out == 3'd1 & early_reset_static_seq_go.out ? B_read0_0.out; - adder4.left = early_reset_static_seq_go.out ? fsm4.out; - adder4.right = early_reset_static_seq_go.out ? 3'd1; - comb_reg.write_en = early_reset_cond00_go.out | fsm4.out == 3'd3 & early_reset_static_seq_go.out ? 1'd1; + adder.right = early_reset_cond00_go.out ? 3'd1; + add0.left = fsm.out == 3'd1 & early_reset_static_seq_go.out ? A_read0_0.out; + add0.right = fsm.out == 3'd1 & early_reset_static_seq_go.out ? B_read0_0.out; + comb_reg.write_en = early_reset_cond00_go.out | fsm.out == 3'd3 & early_reset_static_seq_go.out ? 1'd1; comb_reg.clk = clk; comb_reg.reset = reset; - comb_reg.in = early_reset_cond00_go.out | fsm4.out == 3'd3 & early_reset_static_seq_go.out ? le0.out; + comb_reg.in = early_reset_cond00_go.out | fsm.out == 3'd3 & early_reset_static_seq_go.out ? le0.out; early_reset_cond00_done.in = ud.out; - fsm4.write_en = early_reset_static_seq_go.out ? 1'd1; - fsm4.clk = clk; - fsm4.reset = reset; - fsm4.in = fsm4.out != 3'd3 & early_reset_static_seq_go.out ? adder4.out; - fsm4.in = fsm4.out == 3'd3 & early_reset_static_seq_go.out ? 3'd0; - while_wrapper_early_reset_static_seq_go.in = !while_wrapper_early_reset_static_seq_done.out & fsm6.out == 2'd2 & tdcc_go.out ? 1'd1; - invoke0_go.in = !invoke0_done.out & fsm6.out == 2'd0 & tdcc_go.out ? 1'd1; + while_wrapper_early_reset_static_seq_go.in = !while_wrapper_early_reset_static_seq_done.out & fsm0.out == 2'd2 & tdcc_go.out ? 1'd1; + invoke0_go.in = !invoke0_done.out & fsm0.out == 2'd0 & tdcc_go.out ? 1'd1; tdcc_go.in = go; A0.clk = clk; - A0.addr0 = fsm4.out == 3'd0 & early_reset_static_seq_go.out ? i0.out; + A0.addr0 = fsm.out == 3'd0 & early_reset_static_seq_go.out ? i0.out; A0.reset = reset; - Sum0.write_en = fsm4.out == 3'd1 & early_reset_static_seq_go.out ? 1'd1; + Sum0.write_en = fsm.out == 3'd1 & early_reset_static_seq_go.out ? 1'd1; Sum0.clk = clk; - Sum0.addr0 = fsm4.out == 3'd1 & early_reset_static_seq_go.out ? i0.out; + Sum0.addr0 = fsm.out == 3'd1 & early_reset_static_seq_go.out ? i0.out; Sum0.reset = reset; - Sum0.write_data = fsm4.out == 3'd1 & early_reset_static_seq_go.out ? add0.out; + Sum0.write_data = fsm.out == 3'd1 & early_reset_static_seq_go.out ? add0.out; + fsm0.write_en = fsm0.out == 2'd3 | fsm0.out == 2'd0 & invoke0_done.out & tdcc_go.out | fsm0.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out | fsm0.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 1'd1; + fsm0.clk = clk; + fsm0.reset = reset; + fsm0.in = fsm0.out == 2'd0 & invoke0_done.out & tdcc_go.out ? 2'd1; + fsm0.in = fsm0.out == 2'd3 ? 2'd0; + fsm0.in = fsm0.out == 2'd2 & while_wrapper_early_reset_static_seq_done.out & tdcc_go.out ? 2'd3; + fsm0.in = fsm0.out == 2'd1 & wrapper_early_reset_cond00_done.out & tdcc_go.out ? 2'd2; + adder0.left = early_reset_static_seq_go.out ? fsm.out; + adder0.right = early_reset_static_seq_go.out ? 3'd1; invoke0_done.in = i0.done; early_reset_static_seq_go.in = while_wrapper_early_reset_static_seq_go.out ? 1'd1; - le0.left = early_reset_cond00_go.out | fsm4.out == 3'd3 & early_reset_static_seq_go.out ? i0.out; - le0.right = early_reset_cond00_go.out | fsm4.out == 3'd3 & early_reset_static_seq_go.out ? const1.out; - signal_reg.write_en = fsm.out == 1'd0 & signal_reg.out | fsm.out == 1'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; + le0.left = early_reset_cond00_go.out | fsm.out == 3'd3 & early_reset_static_seq_go.out ? i0.out; + le0.right = early_reset_cond00_go.out | fsm.out == 3'd3 & early_reset_static_seq_go.out ? const1.out; + signal_reg.write_en = fsm.out == 3'd0 & signal_reg.out | fsm.out == 3'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; signal_reg.clk = clk; signal_reg.reset = reset; - signal_reg.in = fsm.out == 1'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; - signal_reg.in = fsm.out == 1'd0 & signal_reg.out ? 1'd0; + signal_reg.in = fsm.out == 3'd0 & !signal_reg.out & wrapper_early_reset_cond00_go.out ? 1'd1; + signal_reg.in = fsm.out == 3'd0 & signal_reg.out ? 1'd0; B0.clk = clk; - B0.addr0 = fsm4.out == 3'd0 & early_reset_static_seq_go.out ? i0.out; + B0.addr0 = fsm.out == 3'd0 & early_reset_static_seq_go.out ? i0.out; B0.reset = reset; - B_read0_0.write_en = fsm4.out == 3'd0 & early_reset_static_seq_go.out ? 1'd1; + B_read0_0.write_en = fsm.out == 3'd0 & early_reset_static_seq_go.out ? 1'd1; B_read0_0.clk = clk; B_read0_0.reset = reset; - B_read0_0.in = fsm4.out == 3'd0 & early_reset_static_seq_go.out ? B0.read_data; - wrapper_early_reset_cond00_go.in = !wrapper_early_reset_cond00_done.out & fsm6.out == 2'd1 & tdcc_go.out ? 1'd1; - wrapper_early_reset_cond00_done.in = fsm.out == 1'd0 & signal_reg.out ? 1'd1; - early_reset_static_seq_done.in = ud4.out; - tdcc_done.in = fsm6.out == 2'd3 ? 1'd1; - while_wrapper_early_reset_static_seq_done.in = !comb_reg.out & fsm4.out == 3'd0 ? 1'd1; - A_read0_0.write_en = fsm4.out == 3'd0 & early_reset_static_seq_go.out ? 1'd1; + B_read0_0.in = fsm.out == 3'd0 & early_reset_static_seq_go.out ? B0.read_data; + wrapper_early_reset_cond00_go.in = !wrapper_early_reset_cond00_done.out & fsm0.out == 2'd1 & tdcc_go.out ? 1'd1; + wrapper_early_reset_cond00_done.in = fsm.out == 3'd0 & signal_reg.out ? 1'd1; + early_reset_static_seq_done.in = ud0.out; + tdcc_done.in = fsm0.out == 2'd3 ? 1'd1; + while_wrapper_early_reset_static_seq_done.in = !comb_reg.out & fsm.out == 3'd0 ? 1'd1; + A_read0_0.write_en = fsm.out == 3'd0 & early_reset_static_seq_go.out ? 1'd1; A_read0_0.clk = clk; A_read0_0.reset = reset; - A_read0_0.in = fsm4.out == 3'd0 & early_reset_static_seq_go.out ? A0.read_data; + A_read0_0.in = fsm.out == 3'd0 & early_reset_static_seq_go.out ? A0.read_data; } control {} } diff --git a/runt.toml b/runt.toml index aebc5191be..063b399a49 100644 --- a/runt.toml +++ b/runt.toml @@ -233,9 +233,9 @@ fud exec --from calyx --to jq \ """ [[tests]] -name = "dynamic repeat correctness test" +name = "correctness test of static islands without static promotion" paths = [ - "tests/correctness/repeat/*.futil", + "tests/correctness/static-islands/*.futil", ] cmd = """ fud exec --from calyx --to jq \ diff --git a/tests/correctness/static-islands/par-static-islands.expect b/tests/correctness/static-islands/par-static-islands.expect new file mode 100644 index 0000000000..5c36f52b98 --- /dev/null +++ b/tests/correctness/static-islands/par-static-islands.expect @@ -0,0 +1,5 @@ +{ + "m": [ + 150 + ] +} diff --git a/tests/correctness/static-islands/par-static-islands.futil b/tests/correctness/static-islands/par-static-islands.futil new file mode 100644 index 0000000000..90d5bdba4d --- /dev/null +++ b/tests/correctness/static-islands/par-static-islands.futil @@ -0,0 +1,47 @@ +import "primitives/core.futil"; + +component main() -> () { + cells { + @external m = std_mem_d1(32, 1, 1); + r1 = std_reg(32); + r2 = std_reg(32); + add1 = std_add(32); + add2 = std_add(32); + add = std_add(32); + } + wires { + static<1> group add_five_r1 { + add1.left = r1.out; + add1.right = 32'd5; + r1.in = add1.out; + r1.write_en = 1'd1; + } + static<1> group add_five_r2 { + add2.left = r2.out; + add2.right = 32'd5; + r2.in = add2.out; + r2.write_en = 1'd1; + } + group write_mem { + m.addr0 = 1'd0; + add.left = r1.out; + add.right = r2.out; + m.write_data = add.out; + m.write_en = 1'd1; + write_mem[done] = m.done; + } + } + control { + seq { + par { + static repeat 15 { + add_five_r1; + } + static repeat 15 { + add_five_r2; + } + } + write_mem; + } + } +} \ No newline at end of file diff --git a/tests/correctness/static-islands/par-static-islands.futil.data b/tests/correctness/static-islands/par-static-islands.futil.data new file mode 100644 index 0000000000..e2e2501837 --- /dev/null +++ b/tests/correctness/static-islands/par-static-islands.futil.data @@ -0,0 +1,12 @@ +{ + "m": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": false, + "width": 32 + } + } +} \ No newline at end of file diff --git a/tests/correctness/repeat/seq-mem-dot-product.expect b/tests/correctness/static-islands/seq-mem-dot-product.expect similarity index 100% rename from tests/correctness/repeat/seq-mem-dot-product.expect rename to tests/correctness/static-islands/seq-mem-dot-product.expect diff --git a/tests/correctness/repeat/seq-mem-dot-product.futil b/tests/correctness/static-islands/seq-mem-dot-product.futil similarity index 100% rename from tests/correctness/repeat/seq-mem-dot-product.futil rename to tests/correctness/static-islands/seq-mem-dot-product.futil diff --git a/tests/correctness/repeat/seq-mem-dot-product.futil.data b/tests/correctness/static-islands/seq-mem-dot-product.futil.data similarity index 100% rename from tests/correctness/repeat/seq-mem-dot-product.futil.data rename to tests/correctness/static-islands/seq-mem-dot-product.futil.data diff --git a/tests/passes/compile-static/rewrite_group_go.expect b/tests/passes/compile-static/rewrite-group-go.expect similarity index 100% rename from tests/passes/compile-static/rewrite_group_go.expect rename to tests/passes/compile-static/rewrite-group-go.expect index 7ed7158d96..5b44b35b56 100644 --- a/tests/passes/compile-static/rewrite_group_go.expect +++ b/tests/passes/compile-static/rewrite-group-go.expect @@ -6,9 +6,9 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { b = std_reg(2); c = std_reg(2); @generated fsm = std_reg(2); + @generated fsm0 = std_reg(3); @generated ud = undef(1); @generated adder = std_add(2); - @generated fsm0 = std_reg(3); @generated ud0 = undef(1); @generated adder0 = std_add(3); @generated signal_reg = std_reg(1); diff --git a/tests/passes/compile-static/rewrite_group_go.futil b/tests/passes/compile-static/rewrite-group-go.futil similarity index 100% rename from tests/passes/compile-static/rewrite_group_go.futil rename to tests/passes/compile-static/rewrite-group-go.futil diff --git a/tests/passes/compile-static/rewrite-static-while-nested.expect b/tests/passes/compile-static/rewrite-static-while-nested.expect index 409fa67351..8e8a8d83f7 100644 --- a/tests/passes/compile-static/rewrite-static-while-nested.expect +++ b/tests/passes/compile-static/rewrite-static-while-nested.expect @@ -8,13 +8,11 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { r = std_reg(3); l2 = std_lt(3); r_cond = std_reg(1); - @generated fsm = std_reg(1); + @generated fsm = std_reg(2); @generated ud = undef(1); - @generated adder = std_add(1); - @generated fsm0 = std_reg(1); + @generated adder = std_add(2); @generated ud0 = undef(1); - @generated adder0 = std_add(1); - @generated fsm1 = std_reg(2); + @generated adder0 = std_add(2); @generated ud1 = undef(1); @generated adder1 = std_add(2); @generated signal_reg = std_reg(1); @@ -39,47 +37,47 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { r_cond.in = l.out; r_cond.write_en = 1'd1; p.addr0 = 1'd0; - adder0.left = fsm0.out; - adder0.right = 1'd1; - fsm0.write_en = 1'd1; - fsm0.in = fsm0.out != 1'd0 ? adder0.out; - fsm0.in = fsm0.out == 1'd0 ? 1'd0; + adder0.left = fsm.out; + adder0.right = 2'd1; + fsm.write_en = 1'd1; + fsm.in = fsm.out != 2'd0 ? adder0.out; + fsm.in = fsm.out == 2'd0 ? 2'd0; early_reset_A2[done] = ud0.out; } group early_reset_static_seq { - incr.left = fsm1.out == 2'd0 ? p.read_data; - incr.right = fsm1.out == 2'd0 ? 3'd1; - p.write_data = fsm1.out == 2'd0 ? incr.out; - p.write_en = fsm1.out == 2'd0 ? 1'd1; - p.addr0 = fsm1.out == 2'd0 ? 1'd0; - l.left = fsm1.out == 2'd1 ? p.read_data; - l.right = fsm1.out == 2'd1 ? 3'd6; - r_cond.in = fsm1.out == 2'd1 ? l.out; - r_cond.write_en = fsm1.out == 2'd1 ? 1'd1; - p.addr0 = fsm1.out == 2'd1 ? 1'd0; - adder1.left = fsm1.out; + incr.left = fsm.out == 2'd0 ? p.read_data; + incr.right = fsm.out == 2'd0 ? 3'd1; + p.write_data = fsm.out == 2'd0 ? incr.out; + p.write_en = fsm.out == 2'd0 ? 1'd1; + p.addr0 = fsm.out == 2'd0 ? 1'd0; + l.left = fsm.out == 2'd1 ? p.read_data; + l.right = fsm.out == 2'd1 ? 3'd6; + r_cond.in = fsm.out == 2'd1 ? l.out; + r_cond.write_en = fsm.out == 2'd1 ? 1'd1; + p.addr0 = fsm.out == 2'd1 ? 1'd0; + adder1.left = fsm.out; adder1.right = 2'd1; - fsm1.write_en = 1'd1; - fsm1.in = fsm1.out != 2'd1 ? adder1.out; - fsm1.in = fsm1.out == 2'd1 ? 2'd0; + fsm.write_en = 1'd1; + fsm.in = fsm.out != 2'd1 ? adder1.out; + fsm.in = fsm.out == 2'd1 ? 2'd0; early_reset_static_seq[done] = ud1.out; } group wrapper_early_reset_A2 { early_reset_A2[go] = 1'd1; - signal_reg.write_en = fsm0.out == 1'd0 & !signal_reg.out ? 1'd1; - signal_reg.in = fsm0.out == 1'd0 & !signal_reg.out ? 1'd1; - wrapper_early_reset_A2[done] = fsm0.out == 1'd0 & signal_reg.out ? 1'd1; + signal_reg.write_en = fsm.out == 2'd0 & !signal_reg.out ? 1'd1; + signal_reg.in = fsm.out == 2'd0 & !signal_reg.out ? 1'd1; + wrapper_early_reset_A2[done] = fsm.out == 2'd0 & signal_reg.out ? 1'd1; } group while_wrapper_early_reset_static_seq { early_reset_static_seq[go] = 1'd1; - while_wrapper_early_reset_static_seq[done] = !r_cond.out & fsm1.out == 2'd0 ? 1'd1; + while_wrapper_early_reset_static_seq[done] = !r_cond.out & fsm.out == 2'd0 ? 1'd1; } comb group comp { l2.left = r.out; l2.right = 3'd3; } - signal_reg.write_en = fsm0.out == 1'd0 & signal_reg.out ? 1'd1; - signal_reg.in = fsm0.out == 1'd0 & signal_reg.out ? 1'd0; + signal_reg.write_en = fsm.out == 2'd0 & signal_reg.out ? 1'd1; + signal_reg.in = fsm.out == 2'd0 & signal_reg.out ? 1'd0; } control { while l2.out with comp { diff --git a/tests/passes/compile-static/rewrite-static-while.expect b/tests/passes/compile-static/rewrite-static-while.expect index 9ddba91f21..00656c54e4 100644 --- a/tests/passes/compile-static/rewrite-static-while.expect +++ b/tests/passes/compile-static/rewrite-static-while.expect @@ -6,13 +6,11 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { incr = std_add(3); l = std_lt(3); r = std_reg(1); - @generated fsm = std_reg(1); + @generated fsm = std_reg(2); @generated ud = undef(1); - @generated adder = std_add(1); - @generated fsm0 = std_reg(1); + @generated adder = std_add(2); @generated ud0 = undef(1); - @generated adder0 = std_add(1); - @generated fsm1 = std_reg(2); + @generated adder0 = std_add(2); @generated ud1 = undef(1); @generated adder1 = std_add(2); @generated signal_reg = std_reg(1); @@ -23,41 +21,41 @@ component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { l.right = 3'd6; r.in = l.out; r.write_en = 1'd1; - adder0.left = fsm0.out; - adder0.right = 1'd1; - fsm0.write_en = 1'd1; - fsm0.in = fsm0.out != 1'd0 ? adder0.out; - fsm0.in = fsm0.out == 1'd0 ? 1'd0; + adder0.left = fsm.out; + adder0.right = 2'd1; + fsm.write_en = 1'd1; + fsm.in = fsm.out != 2'd0 ? adder0.out; + fsm.in = fsm.out == 2'd0 ? 2'd0; early_reset_B[done] = ud0.out; } group early_reset_static_seq { - incr.left = fsm1.out == 2'd0 ? p.out; - incr.right = fsm1.out == 2'd0 ? 3'd1; - p.in = fsm1.out == 2'd0 ? incr.out; - p.write_en = fsm1.out == 2'd0 ? 1'd1; - l.left = fsm1.out == 2'd1 ? p.out; - l.right = fsm1.out == 2'd1 ? 3'd6; - r.in = fsm1.out == 2'd1 ? l.out; - r.write_en = fsm1.out == 2'd1 ? 1'd1; - adder1.left = fsm1.out; + incr.left = fsm.out == 2'd0 ? p.out; + incr.right = fsm.out == 2'd0 ? 3'd1; + p.in = fsm.out == 2'd0 ? incr.out; + p.write_en = fsm.out == 2'd0 ? 1'd1; + l.left = fsm.out == 2'd1 ? p.out; + l.right = fsm.out == 2'd1 ? 3'd6; + r.in = fsm.out == 2'd1 ? l.out; + r.write_en = fsm.out == 2'd1 ? 1'd1; + adder1.left = fsm.out; adder1.right = 2'd1; - fsm1.write_en = 1'd1; - fsm1.in = fsm1.out != 2'd1 ? adder1.out; - fsm1.in = fsm1.out == 2'd1 ? 2'd0; + fsm.write_en = 1'd1; + fsm.in = fsm.out != 2'd1 ? adder1.out; + fsm.in = fsm.out == 2'd1 ? 2'd0; early_reset_static_seq[done] = ud1.out; } group wrapper_early_reset_B { early_reset_B[go] = 1'd1; - signal_reg.write_en = fsm0.out == 1'd0 & !signal_reg.out ? 1'd1; - signal_reg.in = fsm0.out == 1'd0 & !signal_reg.out ? 1'd1; - wrapper_early_reset_B[done] = fsm0.out == 1'd0 & signal_reg.out ? 1'd1; + signal_reg.write_en = fsm.out == 2'd0 & !signal_reg.out ? 1'd1; + signal_reg.in = fsm.out == 2'd0 & !signal_reg.out ? 1'd1; + wrapper_early_reset_B[done] = fsm.out == 2'd0 & signal_reg.out ? 1'd1; } group while_wrapper_early_reset_static_seq { early_reset_static_seq[go] = 1'd1; - while_wrapper_early_reset_static_seq[done] = !r.out & fsm1.out == 2'd0 ? 1'd1; + while_wrapper_early_reset_static_seq[done] = !r.out & fsm.out == 2'd0 ? 1'd1; } - signal_reg.write_en = fsm0.out == 1'd0 & signal_reg.out ? 1'd1; - signal_reg.in = fsm0.out == 1'd0 & signal_reg.out ? 1'd0; + signal_reg.write_en = fsm.out == 2'd0 & signal_reg.out ? 1'd1; + signal_reg.in = fsm.out == 2'd0 & signal_reg.out ? 1'd0; } control { seq { diff --git a/tests/passes/compile-static/separate-fsms.expect b/tests/passes/compile-static/separate-fsms.expect new file mode 100644 index 0000000000..fed018914f --- /dev/null +++ b/tests/passes/compile-static/separate-fsms.expect @@ -0,0 +1,113 @@ +import "primitives/core.futil"; +import "primitives/pipelined.futil"; +component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) { + cells { + a = std_reg(2); + b = std_reg(2); + c = std_reg(2); + d = std_reg(2); + @generated fsm = std_reg(1); + @generated fsm0 = std_reg(1); + @generated fsm1 = std_reg(1); + @generated fsm2 = std_reg(1); + @generated fsm3 = std_reg(3); + @generated ud = undef(1); + @generated adder = std_add(1); + @generated ud0 = undef(1); + @generated adder0 = std_add(1); + @generated ud1 = undef(1); + @generated adder1 = std_add(1); + @generated ud2 = undef(1); + @generated adder2 = std_add(1); + @generated ud3 = undef(1); + @generated adder3 = std_add(3); + @generated signal_reg = std_reg(1); + @generated signal_reg0 = std_reg(1); + @generated signal_reg1 = std_reg(1); + } + wires { + group early_reset_B { + b.in = 2'd0; + b.write_en = fsm2.out == 1'd0 ? 1'd1; + adder.left = fsm2.out; + adder.right = 1'd1; + fsm2.write_en = 1'd1; + fsm2.in = fsm2.out != 1'd0 ? adder.out; + fsm2.in = fsm2.out == 1'd0 ? 1'd0; + early_reset_B[done] = ud.out; + } + group early_reset_C { + c.in = 2'd0; + c.write_en = fsm0.out == 1'd0 ? 1'd1; + adder0.left = fsm0.out; + adder0.right = 1'd1; + fsm0.write_en = 1'd1; + fsm0.in = fsm0.out != 1'd0 ? adder0.out; + fsm0.in = fsm0.out == 1'd0 ? 1'd0; + early_reset_C[done] = ud0.out; + } + group early_reset_A { + a.in = 2'd0; + a.write_en = fsm.out == 1'd0 ? 1'd1; + adder1.left = fsm.out; + adder1.right = 1'd1; + fsm.write_en = 1'd1; + fsm.in = fsm.out != 1'd0 ? adder1.out; + fsm.in = fsm.out == 1'd0 ? 1'd0; + early_reset_A[done] = ud1.out; + } + group early_reset_D { + d.in = 2'd0; + d.write_en = fsm1.out == 1'd0 ? 1'd1; + adder2.left = fsm1.out; + adder2.right = 1'd1; + fsm1.write_en = 1'd1; + fsm1.in = fsm1.out != 1'd0 ? adder2.out; + fsm1.in = fsm1.out == 1'd0 ? 1'd0; + early_reset_D[done] = ud2.out; + } + group early_reset_run_A_and_D { + early_reset_A[go] = fsm3.out < 3'd4 ? 1'd1; + early_reset_D[go] = fsm3.out >= 3'd4 & fsm3.out < 3'd6 ? 1'd1; + adder3.left = fsm3.out; + adder3.right = 3'd1; + fsm3.write_en = 1'd1; + fsm3.in = fsm3.out != 3'd5 ? adder3.out; + fsm3.in = fsm3.out == 3'd5 ? 3'd0; + early_reset_run_A_and_D[done] = ud3.out; + } + group wrapper_early_reset_run_A_and_D { + early_reset_run_A_and_D[go] = 1'd1; + signal_reg.write_en = fsm3.out == 3'd0 & !signal_reg.out ? 1'd1; + signal_reg.in = fsm3.out == 3'd0 & !signal_reg.out ? 1'd1; + wrapper_early_reset_run_A_and_D[done] = fsm3.out == 3'd0 & signal_reg.out ? 1'd1; + } + group wrapper_early_reset_B { + early_reset_B[go] = 1'd1; + signal_reg0.write_en = fsm2.out == 1'd0 & !signal_reg0.out ? 1'd1; + signal_reg0.in = fsm2.out == 1'd0 & !signal_reg0.out ? 1'd1; + wrapper_early_reset_B[done] = fsm2.out == 1'd0 & signal_reg0.out ? 1'd1; + } + group wrapper_early_reset_C { + early_reset_C[go] = 1'd1; + signal_reg1.write_en = fsm0.out == 1'd0 & !signal_reg1.out ? 1'd1; + signal_reg1.in = fsm0.out == 1'd0 & !signal_reg1.out ? 1'd1; + wrapper_early_reset_C[done] = fsm0.out == 1'd0 & signal_reg1.out ? 1'd1; + } + signal_reg.write_en = fsm3.out == 3'd0 & signal_reg.out ? 1'd1; + signal_reg.in = fsm3.out == 3'd0 & signal_reg.out ? 1'd0; + signal_reg0.write_en = fsm2.out == 1'd0 & signal_reg0.out ? 1'd1; + signal_reg0.in = fsm2.out == 1'd0 & signal_reg0.out ? 1'd0; + signal_reg1.write_en = fsm0.out == 1'd0 & signal_reg1.out ? 1'd1; + signal_reg1.in = fsm0.out == 1'd0 & signal_reg1.out ? 1'd0; + } + control { + par { + par { + wrapper_early_reset_run_A_and_D; + wrapper_early_reset_B; + } + wrapper_early_reset_C; + } + } +} diff --git a/tests/passes/compile-static/separate-fsms.futil b/tests/passes/compile-static/separate-fsms.futil new file mode 100644 index 0000000000..9a7dc0f00c --- /dev/null +++ b/tests/passes/compile-static/separate-fsms.futil @@ -0,0 +1,45 @@ +// -p well-formed -p compile-static -p dead-group-removal -p remove-ids + +// Test that we need separate FSMS: a) across par blocks and b) when a group +// triggers the "go" hole of another group +import "primitives/core.futil"; +import "primitives/pipelined.futil"; + +component main() -> () { + cells { + a = std_reg(2); + b = std_reg(2); + c = std_reg(2); + d = std_reg(2); + } + + wires { + static<1> group B{ + b.in = 2'd0; + b.write_en = %0 ? 1'd1; + } + static<1> group C{ + c.in = 2'd0; + c.write_en = %0 ? 1'd1; + } + static<1> group A{ + a.in = 2'd0; + a.write_en = %0 ? 1'd1; + } + static<1> group D{ + d.in = 2'd0; + d.write_en = %0 ? 1'd1; + } + static<6> group run_A_and_D{ + A[go] = %[0:4] ? 1'd1; + D[go] = %[4:6] ? 1'd1; + } + } + + control { + par { + par {run_A_and_D; B;} + C; + } + } +} \ No newline at end of file