Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Static FSM Compilation #2215

Merged
merged 106 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
42d8e85
it's a start
calebmkim May 30, 2024
5a0efac
some progress
calebmkim Jun 4, 2024
17cb04a
more progress
calebmkim Jun 4, 2024
a928089
really messy progress
calebmkim Jun 6, 2024
c8656be
some small changes
calebmkim Jun 6, 2024
936c5d2
bad idea but maybe use this code later
calebmkim Jun 13, 2024
a7e70fe
a start
calebmkim Jun 13, 2024
ca44408
really basic works
calebmkim Jun 14, 2024
d60ae36
move staticstruct from passes->analysis
calebmkim Jun 14, 2024
bc29c45
rename
calebmkim Jun 14, 2024
4710e7d
some progress lceaning
calebmkim Jun 14, 2024
380621c
progress on par
calebmkim Jun 15, 2024
a2408f2
par tree'
calebmkim Jun 17, 2024
f782910
fixed edge case-- offload at state 0
calebmkim Jun 17, 2024
008aab7
progress
calebmkim Jun 17, 2024
013b3c2
compiles
calebmkim Jun 17, 2024
84f75ec
querying should work
calebmkim Jun 18, 2024
8fcd046
i think it's done just lots of bugs
calebmkim Jun 18, 2024
1a1957c
things look kinda correct-ish
calebmkim Jun 18, 2024
eef84d1
change z.futil
calebmkim Jun 19, 2024
5f51bf0
working for semi-complicated design
calebmkim Jun 19, 2024
af45ae2
added tests
calebmkim Jun 19, 2024
db5e807
remove debug macro
calebmkim Jun 19, 2024
c1d73bf
hacky fix for bug
calebmkim Jun 19, 2024
3ddc3e0
delte junk
calebmkim Jun 19, 2024
802754c
minor bug
calebmkim Jun 19, 2024
727fd82
delete junk
calebmkim Jun 19, 2024
10022de
small changes
calebmkim Jun 20, 2024
c5a06fc
static interface
calebmkim Jun 20, 2024
020d4b6
systolic fix
calebmkim Jun 20, 2024
2d6410c
refactor count_to_n, instantiate_fsm
calebmkim Jun 21, 2024
aa7c67b
lots of changes, hopefully more efficient
calebmkim Jun 21, 2024
ab6d719
stupid bug
calebmkim Jun 21, 2024
3443c98
fixed stupid bug hopefully
calebmkim Jun 21, 2024
e6c4eda
first coloring try
calebmkim Jun 24, 2024
72c8801
coloring probably bugs though
calebmkim Jun 24, 2024
0109bf6
very small changes
calebmkim Jun 29, 2024
50cb465
sharing fsms passes some tests now
calebmkim Jun 29, 2024
15847f3
insert qor conflicts
calebmkim Jun 29, 2024
2214112
comment out QoR
calebmkim Jun 29, 2024
87db361
ohe
calebmkim Jul 8, 2024
8d730b6
fix ohe bug
calebmkim Jul 8, 2024
b6f40a4
use ohe heuristic
calebmkim Jul 8, 2024
9333d21
use heuristic
calebmkim Jul 8, 2024
832e29a
first pass at opportunistically merging par threads
calebmkim Jul 9, 2024
70b7c6d
fixed obvious bugs, still prob more
calebmkim Jul 9, 2024
c4d6288
remove dbg stmt
calebmkim Jul 9, 2024
dc44635
fix stoopid bug
calebmkim Jul 9, 2024
2fb8cbc
5 hours of debugging for a dumb typo
calebmkim Jul 10, 2024
905ee07
remove warning
calebmkim Jul 10, 2024
4e1ff9f
reduce redundancy
calebmkim Jul 11, 2024
a540e48
Merge branch 'static-repeat-compilation' of https://github.com/calyxi…
calebmkim Jul 11, 2024
a38529a
remove dbg stmt
calebmkim Jul 11, 2024
1b9595a
slightly better guard
calebmkim Jul 14, 2024
42a1e22
rewrite tests
calebmkim Jul 15, 2024
ef4ee7b
documentation
calebmkim Jul 15, 2024
748031e
cleaner code
calebmkim Jul 15, 2024
f590f8a
lots of documentation
calebmkim Jul 15, 2024
bd85537
modify tests
calebmkim Jul 15, 2024
4a29f2a
rename to more intuitive
calebmkim Jul 16, 2024
cfb8628
added complete documentation for
calebmkim Jul 16, 2024
08fa8b1
docs
calebmkim Jul 16, 2024
f0fa096
simplify query stuff
calebmkim Jul 16, 2024
5b3b057
done
calebmkim Jul 16, 2024
5543df8
fix error
calebmkim Jul 16, 2024
ba41adf
add cli
calebmkim Jul 17, 2024
2287ca6
some more stuff
calebmkim Jul 17, 2024
e3e27ca
another try
calebmkim Jul 17, 2024
4ca2fd7
back to og
calebmkim Jul 17, 2024
329de88
check if this works
calebmkim Jul 17, 2024
3ad0bb9
cleanup
calebmkim Jul 17, 2024
7f6790f
added some more docs
calebmkim Jul 17, 2024
36b95e4
rename analyses
calebmkim Jul 18, 2024
769ab7a
trim code
calebmkim Jul 18, 2024
19c7bfe
static tree cleaning
calebmkim Jul 18, 2024
9fdfb4f
inlining par is still a bit confusing
calebmkim Jul 18, 2024
f1505b6
default -> true
calebmkim Jul 18, 2024
1dcd1f6
add documentation
calebmkim Jul 19, 2024
eae0ebf
typo
calebmkim Jul 19, 2024
35a1fea
nicer readability
calebmkim Jul 19, 2024
1ac71d9
rewrite tests
calebmkim Jul 19, 2024
5b812a2
small documentaion change
calebmkim Jul 19, 2024
f8da9e5
rewrite tests
calebmkim Jul 19, 2024
4ac0089
clippy
calebmkim Jul 19, 2024
69cefa1
another clippy error
calebmkim Jul 19, 2024
d8683b3
more clippy errors
calebmkim Jul 19, 2024
3fed7b9
reset group_rewrites
calebmkim Jul 19, 2024
ed1dc3f
Merge branch 'main' into static-repeat-compilation
calebmkim Jul 19, 2024
92bd69d
fix mergeing issue
calebmkim Jul 19, 2024
ebab0fc
yxi tests
calebmkim Jul 19, 2024
2e0053a
unconditionally increase sgroup latency
calebmkim Jul 23, 2024
aa4bb90
unconditionally make threads same length
calebmkim Jul 23, 2024
081f79b
different par strat
calebmkim Jul 25, 2024
869f7d2
greedy sharing option for fsms
calebmkim Jul 26, 2024
992855c
Merge branch 'main' into static-repeat-compilation
calebmkim Aug 5, 2024
ddb73c2
documentation
calebmkim Aug 7, 2024
f599f70
xxx note
calebmkim Aug 7, 2024
7eae0f4
lots of correctness tests
calebmkim Aug 7, 2024
d16e78f
complicated query tests
calebmkim Aug 7, 2024
5ae780b
fix test
calebmkim Aug 7, 2024
08dcb23
Merge branch 'main' into static-repeat-compilation
calebmkim Aug 7, 2024
f6d36c8
documentation
calebmkim Aug 8, 2024
77cfcd1
32 bit to avoid overflow
calebmkim Aug 8, 2024
9044d27
Merge branch 'main' into static-repeat-compilation
calebmkim Sep 28, 2024
16709fb
rewrite test + add a tiny bit of documentation
calebmkim Sep 28, 2024
191802a
comments
calebmkim Sep 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions calyx-frontend/src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ pub enum BoolAttr {
#[strum(serialize = "promoted")]
/// denotes a static component or control promoted from dynamic
Promoted,
#[strum(serialize = "par")]
/// Denotes a group that was generated from a `staticpar` during static
/// inlining.
ParCtrl,
}
impl From<BoolAttr> for Attribute {
fn from(attr: BoolAttr) -> Self {
Expand Down
11 changes: 11 additions & 0 deletions calyx-opt/src/analysis/graph_coloring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,17 @@ where
.collect()
}

// Reverses a coloring by mapping color C -> vec of nodes colored C.
pub fn reverse_coloring(coloring: &HashMap<T, T>) -> HashMap<T, Vec<T>> {
let mut rev_coloring: HashMap<T, Vec<T>> = HashMap::new();
for (node, color) in coloring {
rev_coloring
.entry(color.clone())
.or_default()
.push(node.clone());
}
rev_coloring
}
pub fn welsh_powell_coloring(&self) -> HashMap<T, T> {
let mut coloring: HashMap<T, T> = HashMap::new();

Expand Down
6 changes: 4 additions & 2 deletions calyx-opt/src/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ pub mod reaching_defns;
mod read_write_set;
mod schedule_conflicts;
mod share_set;
mod static_fsm;
mod static_par_timing;
mod static_schedule;
mod static_tree;
mod variable_detection;

pub use compaction_analysis::CompactionAnalysis;
Expand All @@ -42,6 +43,7 @@ pub use promotion_analysis::PromotionAnalysis;
pub use read_write_set::{AssignmentAnalysis, ReadWriteSet};
pub use schedule_conflicts::ScheduleConflicts;
pub use share_set::ShareSet;
pub use static_fsm::{FSMEncoding, StaticFSM};
pub use static_par_timing::StaticParTiming;
pub use static_schedule::{StaticFSM, StaticSchedule};
pub use static_tree::{Node, ParNodes, SingleNode, StateType};
pub use variable_detection::VariableDetection;
250 changes: 250 additions & 0 deletions calyx-opt/src/analysis/static_fsm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use crate::passes::math_utilities::get_bit_width_from;
use calyx_ir::{self as ir};
use calyx_ir::{build_assignments, Nothing};
use calyx_ir::{guard, structure};
use std::collections::HashMap;
use std::rc::Rc;

#[derive(Debug, Clone, Copy, Default)]
// Define an FSMEncoding Enum
pub enum FSMEncoding {
#[default]
Binary,
OneHot,
}

#[derive(Debug)]
/// Represents a static FSM (i.e., the actual register in hardware that counts)
pub struct StaticFSM {
fsm_cell: ir::RRC<ir::Cell>,
encoding: FSMEncoding,
// The fsm's bitwidth (this redundant information bc we have `cell`)
// but makes it easier if we easily have access to this.
bitwidth: u64,
// Mapping of queries from (u64, u64) -> Port
queries: HashMap<(u64, u64), ir::RRC<ir::Port>>,
}
impl StaticFSM {
// Builds a static_fsm from: num_states and encoding type.
pub fn from_basic_info(
num_states: u64,
encoding: FSMEncoding,
builder: &mut ir::Builder,
) -> Self {
// Determine number of bits needed in the register.
let fsm_size = match encoding {
/* represent 0..latency */
FSMEncoding::Binary => get_bit_width_from(num_states + 1),
FSMEncoding::OneHot => num_states,
};
// OHE needs an initial value of 1.
let register = match encoding {
FSMEncoding::Binary => {
builder.add_primitive("fsm", "std_reg", &[fsm_size])
}
FSMEncoding::OneHot => {
builder.add_primitive("fsm", "init_one_reg", &[fsm_size])
}
};

StaticFSM {
encoding,
fsm_cell: register,
bitwidth: fsm_size,
queries: HashMap::new(),
}
}

// Builds an incrementer, and returns the assignments and incrementer cell itself.
// assignments are:
// adder.left = fsm.out; adder.right = 1;
// cell is:
// adder
pub fn build_incrementer(
&self,
builder: &mut ir::Builder,
) -> (Vec<ir::Assignment<Nothing>>, ir::RRC<ir::Cell>) {
let fsm_cell = Rc::clone(&self.fsm_cell);
// For OHE, the "adder" can just be a shifter.
// For OHE the first_state = 1 rather than 0.
// Final state is encoded differently for OHE vs. Binary
let adder = match self.encoding {
FSMEncoding::Binary => {
builder.add_primitive("adder", "std_add", &[self.bitwidth])
}
FSMEncoding::OneHot => {
builder.add_primitive("lsh", "std_lsh", &[self.bitwidth])
}
};
let const_one = builder.add_constant(1, self.bitwidth);
let incr_assigns = build_assignments!(
builder;
// increments the fsm
adder["left"] = ? fsm_cell["out"];
adder["right"] = ? const_one["out"];
)
.to_vec();
(incr_assigns, adder)
}

// Returns the assignments that conditionally increment the fsm,
// based on guard.
// The assignments are:
// fsm.in = guard ? adder.out;
// fsm.write_en = guard ? 1'd1;
pub fn conditional_increment(
&self,
guard: ir::Guard<Nothing>,
adder: ir::RRC<ir::Cell>,
builder: &mut ir::Builder,
) -> Vec<ir::Assignment<Nothing>> {
let fsm_cell = Rc::clone(&self.fsm_cell);
let signal_on = builder.add_constant(1, 1);
let my_assigns = build_assignments!(
builder;
// increments the fsm
fsm_cell["in"] = guard ? adder["out"];
fsm_cell["write_en"] = guard ? signal_on["out"];
);
my_assigns.to_vec()
}

// Returns the assignments that conditionally resets the fsm to 0,
// but only if guard is true.
// The assignments are:
// fsm.in = guard ? 0;
// fsm.write_en = guard ? 1'd1;
pub fn conditional_reset(
&self,
guard: ir::Guard<Nothing>,
builder: &mut ir::Builder,
) -> Vec<ir::Assignment<Nothing>> {
let fsm_cell = Rc::clone(&self.fsm_cell);
let signal_on = builder.add_constant(1, 1);
let const_0 = match self.encoding {
FSMEncoding::Binary => builder.add_constant(0, self.bitwidth),
FSMEncoding::OneHot => builder.add_constant(1, self.bitwidth),
};
let assigns = build_assignments!(
builder;
fsm_cell["in"] = guard ? const_0["out"];
fsm_cell["write_en"] = guard ? signal_on["out"];
);
assigns.to_vec()
}

// Returns a guard that takes a (beg, end) `query`, and returns the equivalent
// guard to `beg <= fsm.out < end`.
pub fn query_between(
&mut self,
builder: &mut ir::Builder,
query: (u64, u64),
) -> Box<ir::Guard<Nothing>> {
let (beg, end) = query;
// Querying OHE is easy, since we already have `self.get_one_hot_query()`
let fsm_cell = Rc::clone(&self.fsm_cell);
if matches!(self.encoding, FSMEncoding::OneHot) {
let g = self.get_one_hot_query(fsm_cell, (beg, end), builder);
return Box::new(g);
}

if beg + 1 == end {
// if beg + 1 == end then we only need to check if fsm == beg
let interval_const = builder.add_constant(beg, self.bitwidth);
let g = guard!(fsm_cell["out"] == interval_const["out"]);
Box::new(g)
} else if beg == 0 {
// if beg == 0, then we only need to check if fsm < end
let end_const = builder.add_constant(end, self.bitwidth);
let lt: ir::Guard<Nothing> =
guard!(fsm_cell["out"] < end_const["out"]);
Box::new(lt)
} else {
// otherwise, check if fsm >= beg & fsm < end
let beg_const = builder.add_constant(beg, self.bitwidth);
let end_const = builder.add_constant(end, self.bitwidth);
let beg_guard: ir::Guard<Nothing> =
guard!(fsm_cell["out"] >= beg_const["out"]);
let end_guard: ir::Guard<Nothing> =
guard!(fsm_cell["out"] < end_const["out"]);
Box::new(ir::Guard::And(Box::new(beg_guard), Box::new(end_guard)))
}
}

// Given a one-hot query, it will return a guard corresponding to that query.
// If it has already built the query (i.e., added the wires/continuous assigments),
// it just uses the same port.
// Otherwise it will build the query.
fn get_one_hot_query(
&mut self,
fsm_cell: ir::RRC<ir::Cell>,
(lb, ub): (u64, u64),
builder: &mut ir::Builder,
) -> ir::Guard<Nothing> {
match self.queries.get(&(lb, ub)) {
None => {
let port = Self::build_one_hot_query(
Rc::clone(&fsm_cell),
self.bitwidth,
(lb, ub),
builder,
);
self.queries.insert((lb, ub), Rc::clone(&port));
ir::Guard::port(port)
}
Some(port) => ir::Guard::port(Rc::clone(port)),
}
}

// Given a (lb, ub) query, and an fsm (and for convenience, a bitwidth),
// Returns a `port`: port is a `wire.out`, where `wire` holds
// whether or not the query is true, i.e., whether the FSM really is
// between [lb, ub).
fn build_one_hot_query(
fsm_cell: ir::RRC<ir::Cell>,
fsm_bitwidth: u64,
(lb, ub): (u64, u64),
builder: &mut ir::Builder,
) -> ir::RRC<ir::Port> {
// The wire that holds the query
let formatted_name = format!("bw_{}_{}", lb, ub);
let wire: ir::RRC<ir::Cell> =
builder.add_primitive(formatted_name, "std_wire", &[1]);
let wire_out = wire.borrow().get("out");

// Continuous assignments to check the FSM
let assigns = {
let in_width = fsm_bitwidth;
// Since 00...00 is the initial state, we need to check lb-1.
let start_index = lb;
// Since verilog slices are inclusive.
let end_index = ub - 1;
let out_width = ub - lb; // == (end_index - start_index + 1)
structure!(builder;
let slicer = prim std_bit_slice(in_width, start_index, end_index, out_width);
let const_slice_0 = constant(0, out_width);
let signal_on = constant(1,1);
);
let slicer_neq_0 = guard!(slicer["out"] != const_slice_0["out"]);
// Extend the continuous assignmments to include this particular query for FSM state;
let my_assigns = build_assignments!(builder;
slicer["in"] = ? fsm_cell["out"];
wire["in"] = slicer_neq_0 ? signal_on["out"];
);
my_assigns.to_vec()
};
builder.add_continuous_assignments(assigns);
wire_out
}

// Return a unique id (i.e., get_unique_id for each FSM in the same component
// will be different).
pub fn get_unique_id(&self) -> ir::Id {
self.fsm_cell.borrow().name()
}

// Return the bitwidth of an FSM object
pub fn get_bitwidth(&self) -> u64 {
self.bitwidth
}
}
Loading
Loading