Skip to content

Commit

Permalink
Merge branch 'main' into queue-benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
polybeandip committed Sep 3, 2024
2 parents bd630e2 + dfb40da commit 9defd7d
Show file tree
Hide file tree
Showing 42 changed files with 4,308 additions and 4,145 deletions.
26 changes: 26 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ members = [
"fud2/fud-core",
"tools/data-conversion",
"tools/btor2/btor2i",
"tools/cider-data-converter",
"tools/calyx-pass-explorer",
"tools/cider-data-converter",
"tools/component_cells",
"tools/component_groups",
"tools/yxi",
"tools/calyx-writer",
]
Expand Down
9 changes: 9 additions & 0 deletions calyx-ir/src/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,15 @@ impl<T> Assignment<T> {
}
self.guard.for_each(&mut |port| f(&port).map(Guard::port))
}

/// Iterate through all ports contained within the assignment.
pub fn iter_ports(&self) -> impl Iterator<Item = RRC<Port>> {
self.guard
.all_ports()
.into_iter()
.chain(std::iter::once(Rc::clone(&self.dst)))
.chain(std::iter::once(Rc::clone(&self.src)))
}
}

impl From<Assignment<Nothing>> for Assignment<StaticTiming> {
Expand Down
148 changes: 106 additions & 42 deletions calyx-opt/src/passes/compile_invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use calyx_utils::{CalyxResult, Error};
use ir::{Assignment, RRC, WRC};
use itertools::Itertools;
use linked_hash_map::LinkedHashMap;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;

use super::dump_ports;
Expand Down Expand Up @@ -128,17 +128,21 @@ impl Named for CompileInvoke {
impl CompileInvoke {
/// Given `ref_cells` of an invoke, returns `(inputs, outputs)` where
/// inputs are the corresponding inputs to the `invoke` and
/// outputs are the corresponding outputs to the `invoke`.
/// outputs are the corresponding outputs to the `invoke` that are used
/// in the component the ref_cell is.
/// (i.e. If a component only reads from a register,
/// only assignments for `reg.out` will be returned.)
///
/// Since this pass eliminates all ref cells in post order, we expect that
/// invoked component already had all of its ref cells removed.

fn ref_cells_to_ports<T>(
fn ref_cells_to_ports_assignments<T>(
&mut self,
inv_cell: RRC<ir::Cell>,
ref_cells: impl Iterator<Item = (ir::Id, ir::RRC<ir::Cell>)>,
invoked_comp: Option<&ir::Component>, //i.e. in invoke reader[]()(); this is `reader`
) -> Vec<ir::Assignment<T>> {
let inv_comp = inv_cell.borrow().type_name().unwrap();
let inv_comp_id = inv_cell.borrow().type_name().unwrap();
let mut assigns = Vec::new();
for (ref_cell_name, concrete_cell) in ref_cells {
log::debug!(
Expand All @@ -147,39 +151,86 @@ impl CompileInvoke {
concrete_cell.borrow().ports.len()
);

// Mapping from canonical names of the ports of the ref cell to the
// new port defined on the signature of the component. This has name of ref cell, not arg cell
let Some(comp_ports) = self.port_names.get(&inv_comp) else {
unreachable!("component `{}` invoked but not already visited by the pass", inv_comp)
// comp_ports is mapping from canonical names of the ports of the ref cell to the
// new port defined on the signature of the higher level component.
// i.e. ref_reg.in -> ref_reg_in
// These have name of ref cell, not the cell passed in as an arugment
let Some(comp_ports) = self.port_names.get(&inv_comp_id) else {
unreachable!("component `{}` invoked but not already visited by the pass", inv_comp_id)
};

// tracks ports used in assigments of the invoked component
let mut used_ports: HashSet<ir::Id> = HashSet::new();
if let Some(invoked_comp) = invoked_comp {
invoked_comp.iter_assignments(|a| {
for port in a.iter_ports() {
used_ports.insert(port.borrow().name);
}
});
invoked_comp.iter_static_assignments(|a| {
for port in a.iter_ports() {
used_ports.insert(port.borrow().name);
}
});
// If the `invoked_comp` passed to the function is `None`,
// then the component being invoked is a primitive.
} else {
unreachable!("Primitives should not have ref cells passed into them at invocation. However ref cells were found at the invocation of {}.",
inv_comp_id
);
}

//contains the newly added ports that result from ref cells removal/dump_ports
let new_comp_ports = comp_ports
.values()
.map(|p| p.borrow().name)
.collect::<HashSet<_>>();

let to_assign: HashSet<&ir::Id> =
new_comp_ports.intersection(&used_ports).collect();

// We expect each canonical port in `comp_ports` to exactly match with a port in
//`concrete_cell` based on well-formedness subtype checks.
for canon in comp_ports.keys() {
// `canon` is `ref_reg.in`, for example.
for (ref_cell_canon, new_sig_port) in comp_ports.iter() {
//only interested in ports attached to the ref cell
if canon.cell != ref_cell_name {
if ref_cell_canon.cell != ref_cell_name {
continue;
}

// For example, if we have a reader component that only reads from a ref_reg,
// we will not have `ref_reg.in = ...` in the invoke* group because the
// reader component does not access `ref_reg.in`.
if !to_assign.contains(&new_sig_port.borrow().name) {
continue;
}

// The given port of the actual, concrete cell passed in
let concrete_port =
Self::get_concrete_port(concrete_cell.clone(), &canon.port);
let concrete_port = Self::get_concrete_port(
concrete_cell.clone(),
&ref_cell_canon.port,
);

if concrete_port.borrow().has_attribute(ir::BoolAttr::Clk)
|| concrete_port.borrow().has_attribute(ir::BoolAttr::Reset)
{
continue;
}

let Some(comp_port) = comp_ports.get(canon) else {
let Some(comp_port) = comp_ports.get(ref_cell_canon) else {
unreachable!("port `{}` not found in the signature of {}. Known ports are: {}",
canon,
inv_comp,
ref_cell_canon,
inv_comp_id,
comp_ports.keys().map(|c| c.port.as_ref()).collect_vec().join(", ")
)
};
// Get the port on the new cell with the same name as ref_port
let ref_port = inv_cell.borrow().get(comp_port.borrow().name);
log::debug!("Port `{}` -> `{}`", canon, ref_port.borrow().name);
log::debug!(
"Port `{}` -> `{}`",
ref_cell_canon,
ref_port.borrow().name
);

let old_port = concrete_port.borrow().canonical();
// If the port has been removed already, get the new port from the component's signature
Expand All @@ -195,33 +246,28 @@ impl CompileInvoke {
Rc::clone(&concrete_port)
};

//Create assignments from dst to src
let dst: RRC<ir::Port>;
let src: RRC<ir::Port>;
match concrete_port.borrow().direction {
ir::Direction::Output => {
log::debug!(
"constructing: {} = {}",
ref_port.borrow().canonical(),
arg_port.borrow().canonical()
);
assigns.push(ir::Assignment::new(
ref_port.clone(),
arg_port,
));
dst = ref_port.clone();
src = arg_port;
}
ir::Direction::Input => {
log::debug!(
"constructing: {} = {}",
arg_port.borrow().canonical(),
ref_port.borrow().canonical(),
);
assigns.push(ir::Assignment::new(
arg_port,
ref_port.clone(),
));
dst = arg_port;
src = ref_port.clone();
}
_ => {
unreachable!("Cell should have inout ports");
}
};
log::debug!(
"constructing: {} = {}",
dst.borrow().canonical(),
src.borrow().canonical(),
);
assigns.push(ir::Assignment::new(dst, src));
}
}
assigns
Expand Down Expand Up @@ -272,12 +318,12 @@ impl Visitor for CompileInvoke {
for cell in comp.cells.iter() {
let mut new_ports: Vec<RRC<ir::Port>> = Vec::new();
if let Some(name) = cell.borrow().type_name() {
if let Some(vec) = self.port_names.get_ports(&name) {
if let Some(ports) = self.port_names.get_ports(&name) {
log::debug!(
"Updating ports of cell `{}' (type `{name}')",
cell.borrow().name()
);
for p in vec.iter() {
for p in ports.iter() {
let new_port = ir::rrc(ir::Port {
name: p.borrow().name,
width: p.borrow().width,
Expand Down Expand Up @@ -311,14 +357,24 @@ impl Visitor for CompileInvoke {
s: &mut ir::Invoke,
comp: &mut ir::Component,
ctx: &LibrarySignatures,
_comps: &[ir::Component],
comps: &[ir::Component],
) -> VisResult {
let mut builder = ir::Builder::new(comp, ctx);
let invoke_group = builder.add_group("invoke");

//get iterator of comps of ref_cells used in the invoke
let invoked_comp: Option<&ir::Component> = comps
.iter()
.find(|&c| s.comp.borrow().prototype.get_name().unwrap() == c.name);

// Assigns representing the ref cell connections
invoke_group.borrow_mut().assignments.extend(
self.ref_cells_to_ports(Rc::clone(&s.comp), s.ref_cells.drain(..)),
// ), //the clone here is questionable? but lets things type check? Maybe change ref_cells_to_ports to expect a reference?
self.ref_cells_to_ports_assignments(
Rc::clone(&s.comp),
s.ref_cells.drain(..),
invoked_comp,
),
//the clone here is questionable? but lets things type check? Maybe change ref_cells_to_ports to expect a reference?
);

// comp.go = 1'd1;
Expand Down Expand Up @@ -356,7 +412,6 @@ impl Visitor for CompileInvoke {
cell,
);
invoke_group.borrow_mut().assignments.extend(assigns);

// Add assignments from the attached combinational group
if let Some(cgr) = &s.comb_group {
let cg = &*cgr.borrow();
Expand Down Expand Up @@ -390,13 +445,22 @@ impl Visitor for CompileInvoke {
s: &mut ir::StaticInvoke,
comp: &mut ir::Component,
ctx: &LibrarySignatures,
_comps: &[ir::Component],
comps: &[ir::Component],
) -> VisResult {
let mut builder = ir::Builder::new(comp, ctx);
let invoke_group = builder.add_static_group("static_invoke", s.latency);

//If the component is not a primitive, pass along the component to `ref_cells_to_ports``
let invoked_comp: Option<&ir::Component> = comps
.iter()
.find(|&c| s.comp.borrow().prototype.get_name().unwrap() == c.name);

invoke_group.borrow_mut().assignments.extend(
self.ref_cells_to_ports(Rc::clone(&s.comp), s.ref_cells.drain(..)),
self.ref_cells_to_ports_assignments(
Rc::clone(&s.comp),
s.ref_cells.drain(..),
invoked_comp,
),
);

// comp.go = 1'd1;
Expand Down
36 changes: 19 additions & 17 deletions calyx-opt/src/passes/top_down_compile_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,23 +1215,17 @@ impl Named for TopDownCompileControl {
}

/// Helper function to emit profiling information when the control consists of a single group.
fn emit_single_enable(
fn extract_single_enable(
con: &mut ir::Control,
component: Id,
json_out_file: &OutputFile,
) {
) -> Option<SingleEnableInfo> {
if let ir::Control::Enable(enable) = con {
let mut profiling_info_set: HashSet<ProfilingInfo> = HashSet::new();
profiling_info_set.insert(ProfilingInfo::SingleEnable(
SingleEnableInfo {
component,
group: enable.group.borrow().name(),
},
));
let _ = serde_json::to_writer_pretty(
json_out_file.get_write(),
&profiling_info_set,
);
return Some(SingleEnableInfo {
component,
group: enable.group.borrow().name(),
});
} else {
None
}
}

Expand All @@ -1244,8 +1238,11 @@ impl Visitor for TopDownCompileControl {
) -> VisResult {
let mut con = comp.control.borrow_mut();
if matches!(*con, ir::Control::Empty(..) | ir::Control::Enable(..)) {
if let Some(json_out_file) = &self.dump_fsm_json {
emit_single_enable(&mut con, comp.name, json_out_file);
if let Some(enable_info) =
extract_single_enable(&mut con, comp.name)
{
self.fsm_groups
.insert(ProfilingInfo::SingleEnable(enable_info));
}
return Ok(Action::Stop);
}
Expand Down Expand Up @@ -1459,12 +1456,17 @@ impl Visitor for TopDownCompileControl {
let comp_group =
sch.realize_schedule(self.dump_fsm, &mut self.fsm_groups, fsm_impl);

Ok(Action::change(ir::Control::enable(comp_group)))
}

/// If requested, emit FSM json after all components are processed
fn finish_context(&mut self, _ctx: &mut calyx_ir::Context) -> VisResult {
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)))
Ok(Action::Continue)
}
}
4 changes: 2 additions & 2 deletions fud/fud/stages/verilator/stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def json_to_dat(tmp_dir: SourceType.Directory, json_path: SourceType.Path):
testbench_sv,
"--binary",
"--top-module",
"TOP", # The wrapper module name from `tb.sv`.
"toplevel", # The wrapper module name from `tb.sv`.
"--Mdir",
"{tmpdir_name}",
"-fno-inline",
Expand All @@ -192,7 +192,7 @@ def simulate(tmpdir: SourceType.Directory) -> SourceType.Stream:
cycle_limit = config["stages", self.name, "cycle_limit"]
return shell(
[
f"{tmpdir.name}/VTOP",
f"{tmpdir.name}/Vtoplevel",
f"+DATA={tmpdir.name}",
f"+CYCLE_LIMIT={str(cycle_limit)}",
f"+OUT={tmpdir.name}/output.vcd",
Expand Down
Loading

0 comments on commit 9defd7d

Please sign in to comment.