From a1d99c20678202435e848a015c06f8646a40e476 Mon Sep 17 00:00:00 2001 From: Griffin Berlstein Date: Fri, 7 Jun 2024 11:13:04 -0400 Subject: [PATCH] [Cider 2] First Pass Multi-component and Invokes (#2119) * I broke everything by being stupid so here's one big commit oops * Remove unused `is_done` method from `ProgramCounter` * invoke works now maybe? * a silly silly test case --- interp/cider2-tests/multi-comp/my_add.expect | 11 + interp/cider2-tests/multi-comp/my_add.futil | 64 ++++ .../multi-comp/my_add_invoke.expect | 11 + .../multi-comp/my_add_invoke.futil | 62 +++ .../multi-comp/my_add_invoke_ref.expect | 11 + .../multi-comp/my_add_invoke_ref.futil | 63 ++++ interp/cider2-tests/runt.toml | 9 + interp/src/flatten/flat_ir/base.rs | 44 +++ interp/src/flatten/flat_ir/component.rs | 11 + .../src/flatten/flat_ir/control/structures.rs | 50 ++- .../src/flatten/flat_ir/control/translator.rs | 67 +++- .../src/flatten/structures/environment/env.rs | 352 +++++++++++++++--- .../structures/environment/program_counter.rs | 26 +- interp/src/flatten/structures/printer.rs | 6 +- 14 files changed, 687 insertions(+), 100 deletions(-) create mode 100644 interp/cider2-tests/multi-comp/my_add.expect create mode 100644 interp/cider2-tests/multi-comp/my_add.futil create mode 100644 interp/cider2-tests/multi-comp/my_add_invoke.expect create mode 100644 interp/cider2-tests/multi-comp/my_add_invoke.futil create mode 100644 interp/cider2-tests/multi-comp/my_add_invoke_ref.expect create mode 100644 interp/cider2-tests/multi-comp/my_add_invoke_ref.futil diff --git a/interp/cider2-tests/multi-comp/my_add.expect b/interp/cider2-tests/multi-comp/my_add.expect new file mode 100644 index 0000000000..2d37b36f81 --- /dev/null +++ b/interp/cider2-tests/multi-comp/my_add.expect @@ -0,0 +1,11 @@ +{ + "left_reg": [ + 5 + ], + "result": [ + 15 + ], + "right_reg": [ + 10 + ] +} diff --git a/interp/cider2-tests/multi-comp/my_add.futil b/interp/cider2-tests/multi-comp/my_add.futil new file mode 100644 index 0000000000..f78b709722 --- /dev/null +++ b/interp/cider2-tests/multi-comp/my_add.futil @@ -0,0 +1,64 @@ +import "primitives/core.futil"; +import "primitives/binary_operators.futil"; + +component my_add(left: 32, right: 32) -> (out: 32) { + cells { + result = std_reg(32); + add = std_add(32); + } + wires { + group do_add { + add.left = left; + add.right = right; + result.in = add.out; + result.write_en = 1'd1; + do_add[done] = result.done; + } + + out = result.out; + } + + control { + do_add; + } +} + +component main() -> () { + cells { + left_reg = std_reg(32); + right_reg = std_reg(32); + my_add = my_add(); + result = std_reg(32); + } + + wires { + group init_left { + left_reg.in = 32'd5; + left_reg.write_en = 1'd1; + init_left[done] = left_reg.done; + } + + group init_right { + right_reg.in = 32'd10; + right_reg.write_en = 1'd1; + init_right[done] = right_reg.done; + } + + group do_add { + my_add.go = 1'd1; + my_add.left = left_reg.out; + my_add.right = right_reg.out; + result.in = my_add.out; + result.write_en = my_add.done; + do_add[done] = result.done; + } + } + + control { + seq { + init_left; + init_right; + do_add; + } + } +} diff --git a/interp/cider2-tests/multi-comp/my_add_invoke.expect b/interp/cider2-tests/multi-comp/my_add_invoke.expect new file mode 100644 index 0000000000..2d37b36f81 --- /dev/null +++ b/interp/cider2-tests/multi-comp/my_add_invoke.expect @@ -0,0 +1,11 @@ +{ + "left_reg": [ + 5 + ], + "result": [ + 15 + ], + "right_reg": [ + 10 + ] +} diff --git a/interp/cider2-tests/multi-comp/my_add_invoke.futil b/interp/cider2-tests/multi-comp/my_add_invoke.futil new file mode 100644 index 0000000000..ad0fc257ca --- /dev/null +++ b/interp/cider2-tests/multi-comp/my_add_invoke.futil @@ -0,0 +1,62 @@ +import "primitives/core.futil"; +import "primitives/binary_operators.futil"; + +component my_add(left: 32, right: 32) -> (out: 32) { + cells { + result = std_reg(32); + add = std_add(32); + } + wires { + group do_add { + add.left = left; + add.right = right; + result.in = add.out; + result.write_en = 1'd1; + do_add[done] = result.done; + } + + out = result.out; + } + + control { + do_add; + } +} + +component main() -> () { + cells { + left_reg = std_reg(32); + right_reg = std_reg(32); + my_add = my_add(); + result = std_reg(32); + } + + wires { + group init_left { + left_reg.in = 32'd5; + left_reg.write_en = 1'd1; + init_left[done] = left_reg.done; + } + + group init_right { + right_reg.in = 32'd10; + right_reg.write_en = 1'd1; + init_right[done] = right_reg.done; + } + + group store_result { + result.in = my_add.out; + result.write_en = 1'd1; + store_result[done] = result.done; + } + } + + control { + seq { + init_left; + init_right; + invoke my_add(left=left_reg.out, right=right_reg.out)(); + store_result; + } + } +} diff --git a/interp/cider2-tests/multi-comp/my_add_invoke_ref.expect b/interp/cider2-tests/multi-comp/my_add_invoke_ref.expect new file mode 100644 index 0000000000..2d37b36f81 --- /dev/null +++ b/interp/cider2-tests/multi-comp/my_add_invoke_ref.expect @@ -0,0 +1,11 @@ +{ + "left_reg": [ + 5 + ], + "result": [ + 15 + ], + "right_reg": [ + 10 + ] +} diff --git a/interp/cider2-tests/multi-comp/my_add_invoke_ref.futil b/interp/cider2-tests/multi-comp/my_add_invoke_ref.futil new file mode 100644 index 0000000000..680eaee938 --- /dev/null +++ b/interp/cider2-tests/multi-comp/my_add_invoke_ref.futil @@ -0,0 +1,63 @@ +import "primitives/core.futil"; +import "primitives/binary_operators.futil"; + +component my_add(left: 32, right: 32) -> (out: 32) { + cells { + result = std_reg(32); + ref add = std_add(32); + } + wires { + group do_add { + add.left = left; + add.right = right; + result.in = add.out; + result.write_en = 1'd1; + do_add[done] = result.done; + } + + out = result.out; + } + + control { + do_add; + } +} + +component main() -> () { + cells { + left_reg = std_reg(32); + right_reg = std_reg(32); + my_add = my_add(); + result = std_reg(32); + inner_add = std_add(32); + } + + wires { + group init_left { + left_reg.in = 32'd5; + left_reg.write_en = 1'd1; + init_left[done] = left_reg.done; + } + + group init_right { + right_reg.in = 32'd10; + right_reg.write_en = 1'd1; + init_right[done] = right_reg.done; + } + + group store_result { + result.in = my_add.out; + result.write_en = 1'd1; + store_result[done] = result.done; + } + } + + control { + seq { + init_left; + init_right; + invoke my_add[add=inner_add](left=left_reg.out, right=right_reg.out)(); + store_result; + } + } +} diff --git a/interp/cider2-tests/runt.toml b/interp/cider2-tests/runt.toml index 8e0989a4cc..b97d42b20f 100644 --- a/interp/cider2-tests/runt.toml +++ b/interp/cider2-tests/runt.toml @@ -10,6 +10,14 @@ cmd = """ timeout = 10 expect_dir = "unit" +[[tests]] +name = "multi-comp" +paths = ["multi-comp/*.futil"] +cmd = """ + ../../target/debug/cider {} -l ../../ flat --dump-registers | ../../target/debug/cider-data-converter --to json | jq --sort-keys +""" +timeout = 10 + # [[tests]] # name = "errors" # paths = ["tests/errors/*.futil"] @@ -61,6 +69,7 @@ paths = ["../tests/control/invoke/*.futil"] cmd = """ fud2 {} --from calyx --to dat --through interp-flat -s sim.data={}.data | jq --sort-keys """ +timeout = 10 # [[tests]] # name = "invoke comp" diff --git a/interp/src/flatten/flat_ir/base.rs b/interp/src/flatten/flat_ir/base.rs index 275dfb2ab6..29dbae0df4 100644 --- a/interp/src/flatten/flat_ir/base.rs +++ b/interp/src/flatten/flat_ir/base.rs @@ -250,6 +250,50 @@ impl From for CellRef { } } +#[derive(Debug)] +pub enum GlobalCellRef { + Cell(GlobalCellIdx), + Ref(GlobalRefCellIdx), +} + +impl From for GlobalCellRef { + fn from(v: GlobalRefCellIdx) -> Self { + Self::Ref(v) + } +} + +impl From for GlobalCellRef { + fn from(v: GlobalCellIdx) -> Self { + Self::Cell(v) + } +} + +impl GlobalCellRef { + pub fn from_local(local: CellRef, base_info: &BaseIndices) -> Self { + match local { + CellRef::Local(l) => (base_info + l).into(), + CellRef::Ref(r) => (base_info + r).into(), + } + } +} + +pub enum CellDefinitionRef { + Local(CellDefinitionIdx), + Ref(RefCellDefinitionIdx), +} + +impl From for CellDefinitionRef { + fn from(v: RefCellDefinitionIdx) -> Self { + Self::Ref(v) + } +} + +impl From for CellDefinitionRef { + fn from(v: CellDefinitionIdx) -> Self { + Self::Local(v) + } +} + /// A global index for assignments in the IR #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct AssignmentIdx(u32); diff --git a/interp/src/flatten/flat_ir/component.rs b/interp/src/flatten/flat_ir/component.rs index 4e703d4d8f..4211d286b2 100644 --- a/interp/src/flatten/flat_ir/component.rs +++ b/interp/src/flatten/flat_ir/component.rs @@ -63,6 +63,10 @@ pub struct ComponentCore { pub continuous_assignments: IndexRange, /// True iff component is combinational pub is_comb: bool, + /// The go port for this component + pub go: LocalPortOffset, + /// The done port for this component + pub done: LocalPortOffset, } #[derive(Debug, Clone)] @@ -193,6 +197,13 @@ impl AuxillaryComponentInfo { self.cell_offset_map.skip(cell); self.ref_cell_offset_map.skip(ref_cell); } + + pub fn get_cell_info_idx(&self, cell: CellRef) -> CellDefinitionRef { + match cell { + CellRef::Local(l) => self.cell_offset_map[l].into(), + CellRef::Ref(r) => self.ref_cell_offset_map[r].into(), + } + } } pub struct IdxSkipSizes { diff --git a/interp/src/flatten/flat_ir/control/structures.rs b/interp/src/flatten/flat_ir/control/structures.rs index 714fbfb599..2b86c182ee 100644 --- a/interp/src/flatten/flat_ir/control/structures.rs +++ b/interp/src/flatten/flat_ir/control/structures.rs @@ -2,7 +2,10 @@ use smallvec::SmallVec; use crate::flatten::{ flat_ir::prelude::*, - structures::{index_trait::impl_index, indexed_map::IndexedMap}, + structures::{ + index_trait::{impl_index, IndexRange}, + indexed_map::IndexedMap, + }, }; #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd)] @@ -158,9 +161,27 @@ impl While { } } +#[derive(Debug)] +pub struct InvokeSignature { + /// The ports attached to the input of the invoked cell, an association list + /// of the port ref in the **PARENT** context, and the port connected + /// to it in the parent context i.e. (dst, src) + pub inputs: SmallVec<[(PortRef, PortRef); 1]>, + /// The ports attached to the outputs of the invoked cell, an association list + /// of the port ref in the **PARENT** context, and the port connected + /// to it in the parent context. i.e. (dst, src) + pub outputs: SmallVec<[(PortRef, PortRef); 1]>, +} + +impl InvokeSignature { + pub fn iter(&self) -> impl Iterator { + self.inputs.iter().chain(self.outputs.iter()) + } +} + /// Invoke control node /// -/// TODO Griffin: Consider making this smaller? +/// TODO Griffin: Consider making this smaller? Move ref_cells into signature box? #[derive(Debug)] pub struct Invoke { /// The cell being invoked @@ -172,14 +193,12 @@ pub struct Invoke { /// association list of the refcell offset in the invoked context, and the /// cell realizing it in the parent context pub ref_cells: SmallVec<[(LocalRefCellOffset, CellRef); 1]>, - /// The ports attached to the input of the invoked cell, an association list - /// of the port ref in the **PARENT** context, and the port connected - /// to it in the parent context i.e. (dst, src) - pub inputs: SmallVec<[(PortRef, PortRef); 1]>, - /// The ports attached to the outputs of the invoked cell, an association list - /// of the port ref in the **PARENT** context, and the port connected - /// to it in the parent context. i.e. (dst, src) - pub outputs: SmallVec<[(PortRef, PortRef); 1]>, + /// The signature (behind a box for space reasons) + pub signature: Box, + + pub go: PortRef, + pub done: PortRef, + pub assignments: IndexRange, } impl Invoke { @@ -189,6 +208,8 @@ impl Invoke { ref_cells: R, inputs: I, outputs: O, + go: PortRef, + done: PortRef, ) -> Self where R: IntoIterator, @@ -199,8 +220,13 @@ impl Invoke { cell, comb_group, ref_cells: ref_cells.into_iter().collect(), - inputs: inputs.into_iter().collect(), - outputs: outputs.into_iter().collect(), + signature: Box::new(InvokeSignature { + inputs: inputs.into_iter().collect(), + outputs: outputs.into_iter().collect(), + }), + go, + done, + assignments: IndexRange::empty_interval(), } } } diff --git a/interp/src/flatten/flat_ir/control/translator.rs b/interp/src/flatten/flat_ir/control/translator.rs index 0d520519de..ef961dc4a2 100644 --- a/interp/src/flatten/flat_ir/control/translator.rs +++ b/interp/src/flatten/flat_ir/control/translator.rs @@ -1,5 +1,6 @@ use ahash::{HashMap, HashMapExt}; -use calyx_ir::{self as cir, RRC}; +use calyx_ir::{self as cir, NumAttr, RRC}; +use itertools::Itertools; use crate::{ flatten::{ @@ -192,6 +193,8 @@ fn translate_component( // and this is not possible when it is inside the context let mut taken_control = std::mem::take(&mut taken_ctx.primary.control); + let ctrl_idx_start = taken_control.peek_next_idx(); + let argument_tuple = (group_mapper, layout, taken_ctx, auxillary_component_info); @@ -208,17 +211,57 @@ fn translate_component( Some(ctrl_node) }; + let ctrl_idx_end = taken_control.peek_next_idx(); + // unwrap all the stuff packed into the argument tuple - let (_, _layout, mut taken_ctx, auxillary_component_info) = argument_tuple; + let (_, layout, mut taken_ctx, auxillary_component_info) = argument_tuple; // put stuff back taken_ctx.primary.control = taken_control; *ctx = taken_ctx; + for node in IndexRange::new(ctrl_idx_start, ctrl_idx_end).iter() { + if let ControlNode::Invoke(i) = &mut ctx.primary.control[node] { + let assign_start_index = ctx.primary.assignments.peek_next_idx(); + + for (dst, src) in i.signature.iter() { + ctx.primary.assignments.push(Assignment { + dst: *dst, + src: *src, + guard: ctx.primary.guards.push(Guard::True), + }); + } + + let assign_end_index = ctx.primary.assignments.peek_next_idx(); + i.assignments = + IndexRange::new(assign_start_index, assign_end_index); + } + } + + let go_ports = comp + .signature + .borrow() + .find_all_with_attr(NumAttr::Go) + .collect_vec(); + let done_ports = comp + .signature + .borrow() + .find_all_with_attr(NumAttr::Done) + .collect_vec(); + + // Will need to rethink this at some point + if go_ports.len() != 1 || done_ports.len() != 1 { + todo!("handle multiple go and done ports"); + } + let go_port = &go_ports[0]; + let done_port = &done_ports[0]; + let comp_core = ComponentCore { control, continuous_assignments, is_comb: comp.is_comb, + go: *layout.port_map[&go_port.as_raw()].unwrap_local(), + done: *layout.port_map[&done_port.as_raw()].unwrap_local(), }; let ctrl_ref = ctx.primary.components.push(comp_core); @@ -594,6 +637,24 @@ impl FlattenTree for cir::Control { ) }); + let go = inv + .comp + .borrow() + .find_all_with_attr(NumAttr::Go) + .collect_vec(); + assert!(go.len() == 1, "cannot handle multiple go ports yet or the invoked cell has none"); + let comp_go = layout.port_map[&go[0].as_raw()]; + let done = inv + .comp + .borrow() + .find_all_with_attr(NumAttr::Done) + .collect_vec(); + assert!( + done.len() == 1, + "cannot handle multiple done ports yet or the invoked cell has none" + ); + let comp_done = layout.port_map[&done[0].as_raw()]; + ControlNode::Invoke(Invoke::new( invoked_cell, inv.comb_group @@ -602,6 +663,8 @@ impl FlattenTree for cir::Control { ref_cells, inputs, outputs, + comp_go, + comp_done, )) } cir::Control::Enable(e) => ControlNode::Enable(Enable::new( diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index fb616bbd7f..45a8690c0a 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -14,10 +14,11 @@ use crate::{ flat_ir::{ cell_prototype::{CellPrototype, PrimType1}, prelude::{ - AssignedValue, AssignmentIdx, BaseIndices, ComponentIdx, - ControlNode, GlobalCellIdx, GlobalPortIdx, GlobalPortRef, - GlobalRefCellIdx, GlobalRefPortIdx, GuardIdx, PortRef, - PortValue, + AssignedValue, AssignmentIdx, BaseIndices, + CellDefinitionRef::{Local, Ref}, + CellRef, ComponentIdx, ControlNode, GlobalCellIdx, + GlobalCellRef, GlobalPortIdx, GlobalPortRef, GlobalRefCellIdx, + GlobalRefPortIdx, GuardIdx, Invoke, PortRef, PortValue, }, wires::guards::Guard, }, @@ -113,12 +114,19 @@ pub(crate) struct ComponentLedger { impl ComponentLedger { /// Convert a relative offset to a global one. Perhaps should take an owned /// value rather than a pointer - pub fn convert_to_global(&self, port: &PortRef) -> GlobalPortRef { + pub fn convert_to_global_port(&self, port: &PortRef) -> GlobalPortRef { match port { PortRef::Local(l) => (&self.index_bases + l).into(), PortRef::Ref(r) => (&self.index_bases + r).into(), } } + + pub fn convert_to_global_cell(&self, cell: &CellRef) -> GlobalCellRef { + match cell { + CellRef::Local(l) => (&self.index_bases + l).into(), + CellRef::Ref(r) => (&self.index_bases + r).into(), + } + } } /// An enum encapsulating cell functionality. It is either a pointer to a @@ -219,7 +227,7 @@ impl<'a> Environment<'a> { ref_ports: RefPortMap::with_capacity( aux.ref_port_offset_map.count(), ), - pc: ProgramCounter::new(ctx), + pc: ProgramCounter::new_empty(), ctx, }; @@ -227,6 +235,19 @@ impl<'a> Environment<'a> { let root = env.cells.push(root_node); env.layout_component(root, data_map); + // Initialize program counter + // TODO griffin: Maybe refactor into a separate function + for (idx, ledger) in env.cells.iter() { + if let CellLedger::Component(comp) = ledger { + if let Some(ctrl) = &env.ctx.primary[comp.comp_id].control { + env.pc.vec_mut().push(ControlPoint { + comp: idx, + control_node_idx: *ctrl, + }) + } + } + } + env } @@ -341,6 +362,26 @@ impl<'a> Environment<'a> { ) } } + + pub fn get_comp_go(&self, comp: GlobalCellIdx) -> GlobalPortIdx { + let ledger = self.cells[comp] + .as_comp() + .expect("Called get_comp_go with a non-component cell."); + + &ledger.index_bases + self.ctx.primary[ledger.comp_id].go + } + + pub fn get_comp_done(&self, comp: GlobalCellIdx) -> GlobalPortIdx { + let ledger = self.cells[comp] + .as_comp() + .expect("Called get_comp_done with a non-component cell."); + + &ledger.index_bases + self.ctx.primary[ledger.comp_id].done + } + + pub fn get_root_done(&self) -> GlobalPortIdx { + self.get_comp_done(GlobalCellIdx::new(0)) + } } // ===================== Environment print implementations ===================== @@ -450,7 +491,9 @@ pub struct Simulator<'a> { impl<'a> Simulator<'a> { pub fn new(env: Environment<'a>) -> Self { - Self { env } + let mut output = Self { env }; + output.set_root_go_high(); + output } pub fn _print_env(&self) { @@ -480,18 +523,38 @@ impl<'a> Simulator<'a> { } #[inline] - fn get_global_idx( + fn lookup_global_cell_id(&self, cell: GlobalCellRef) -> GlobalCellIdx { + match cell { + GlobalCellRef::Cell(c) => c, + // TODO Griffin: Please make sure this error message is correct with + // respect to the compiler + GlobalCellRef::Ref(r) => self.env.ref_cells[r].expect("A ref cell is being queried without a supplied ref-cell. This is an error?"), + } + } + + #[inline] + fn get_global_port_idx( &self, port: &PortRef, comp: GlobalCellIdx, ) -> GlobalPortIdx { let ledger = self.env.cells[comp].unwrap_comp(); - self.lookup_global_port_id(ledger.convert_to_global(port)) + self.lookup_global_port_id(ledger.convert_to_global_port(port)) + } + + #[inline] + fn get_global_cell_idx( + &self, + cell: &CellRef, + comp: GlobalCellIdx, + ) -> GlobalCellIdx { + let ledger = self.env.cells[comp].unwrap_comp(); + self.lookup_global_cell_id(ledger.convert_to_global_cell(cell)) } #[inline] fn get_value(&self, port: &PortRef, comp: GlobalCellIdx) -> &PortValue { - let port_idx = self.get_global_idx(port, comp); + let port_idx = self.get_global_port_idx(port, comp); &self.env.ports[port_idx] } @@ -499,6 +562,13 @@ impl<'a> Simulator<'a> { self.env.cells.first().unwrap().as_comp().unwrap() } + /// Finds the root component of the simulation and sets its go port to high + fn set_root_go_high(&mut self) { + let ledger = self.get_root_component(); + let go = &ledger.index_bases + self.env.ctx.primary[ledger.comp_id].go; + self.env.ports[go] = PortValue::new_implicit(Value::bit_high()); + } + /// Attempt to find the parent cell for a port. If no such cell exists (i.e. /// it is a hole port, then it returns None) fn _get_parent_cell( @@ -560,9 +630,11 @@ impl<'a> Simulator<'a> { ) } - ControlNode::Invoke(_) => { - todo!("invokes not yet implemented") - } + ControlNode::Invoke(i) => ScheduledAssignments::new( + node.comp, + i.assignments, + None, + ), ControlNode::Empty(_) => { unreachable!( @@ -592,6 +664,111 @@ impl<'a> Simulator<'a> { .collect() } + /// A helper function which inserts indicies for the ref cells and ports + /// used in the invoke statement + fn intialize_ref_cells( + &mut self, + parent_comp: GlobalCellIdx, + invoke: &Invoke, + ) { + if invoke.ref_cells.is_empty() { + return; + } + + let parent_ledger = self.env.cells[parent_comp].unwrap_comp(); + let parent_info = &self.env.ctx.secondary[parent_ledger.comp_id]; + + let child_comp = self.get_global_cell_idx(&invoke.cell, parent_comp); + // this unwrap should never fail because ref-cells can only exist on + // components, not primitives + let child_ledger = self.env.cells[child_comp] + .as_comp() + .expect("malformed invoke?"); + let child_info = &self.env.ctx.secondary[child_ledger.comp_id]; + + for (offset, cell_ref) in invoke.ref_cells.iter() { + // first set the ref cell + let global_ref_cell_idx = &child_ledger.index_bases + offset; + let global_actual_cell_idx = + self.get_global_cell_idx(cell_ref, parent_comp); + self.env.ref_cells[global_ref_cell_idx] = + Some(global_actual_cell_idx); + + // then set the ports + let child_ref_cell_info = &self.env.ctx.secondary + [child_info.ref_cell_offset_map[*offset]]; + + let cell_info_idx = parent_info.get_cell_info_idx(*cell_ref); + match cell_info_idx { + Local(l) => { + let info = &self.env.ctx.secondary[l]; + assert_eq!( + child_ref_cell_info.ports.size(), + info.ports.size() + ); + + for (dest, source) in + child_ref_cell_info.ports.iter().zip(info.ports.iter()) + { + let dest_idx = &child_ledger.index_bases + dest; + let source_idx = &parent_ledger.index_bases + source; + self.env.ref_ports[dest_idx] = Some(source_idx); + } + } + Ref(r) => { + let info = &self.env.ctx.secondary[r]; + assert_eq!( + child_ref_cell_info.ports.size(), + info.ports.size() + ); + + for (dest, source) in + child_ref_cell_info.ports.iter().zip(info.ports.iter()) + { + let dest_idx = &child_ledger.index_bases + dest; + let source_ref_idx = + &parent_ledger.index_bases + source; + // TODO griffin: Make this error message actually useful + let source_idx_actual = self.env.ref_ports + [source_ref_idx] + .expect("ref port not instantiated, this is a bug"); + + self.env.ref_ports[dest_idx] = Some(source_idx_actual); + } + } + } + } + } + + fn cleanup_ref_cells( + &mut self, + parent_comp: GlobalCellIdx, + invoke: &Invoke, + ) { + let child_comp = self.get_global_cell_idx(&invoke.cell, parent_comp); + // this unwrap should never fail because ref-cells can only exist on + // components, not primitives + let child_ledger = self.env.cells[child_comp] + .as_comp() + .expect("malformed invoke?"); + let child_info = &self.env.ctx.secondary[child_ledger.comp_id]; + + for (offset, _) in invoke.ref_cells.iter() { + // first unset the ref cell + let global_ref_cell_idx = &child_ledger.index_bases + offset; + self.env.ref_cells[global_ref_cell_idx] = None; + + // then unset the ports + let child_ref_cell_info = &self.env.ctx.secondary + [child_info.ref_cell_offset_map[*offset]]; + + for port in child_ref_cell_info.ports.iter() { + let port_idx = &child_ledger.index_bases + port; + self.env.ref_ports[port_idx] = None; + } + } + } + pub fn step(&mut self) -> InterpreterResult<()> { // place to keep track of what groups we need to conclude at the end of // this step. These are indices into the program counter @@ -600,13 +777,23 @@ impl<'a> Simulator<'a> { // buffers. Can pick anything from zero to the number of nodes in the // program counter as the size let mut leaf_nodes = vec![]; + let mut set_done = vec![]; let mut new_nodes = vec![]; let (mut vecs, mut par_map, mut with_map) = self.env.pc.take_fields(); + // TODO griffin: This has become an unwieldy mess and should really be + // refactored into a handful of internal functions vecs.retain_mut(|node| { + let comp_go = self.env.get_comp_go(node.comp); + let comp_done = self.env.get_comp_done(node.comp); + if !self.env.ports[comp_go].as_bool().unwrap_or_default() || self.env.ports[comp_done].as_bool().unwrap_or_default() { + // if the go port is low or the done port is high, we skip the node without doing anything + return true; + } + // just considering a single node case for the moment - match &self.env.ctx.primary[node.control_node_idx] { + let retain_bool = match &self.env.ctx.primary[node.control_node_idx] { ControlNode::Seq(seq) => { if !seq.is_empty() { let next = seq.stms()[0]; @@ -640,45 +827,44 @@ impl<'a> Simulator<'a> { } } ControlNode::If(i) => { - if i.cond_group().is_some() { - if with_map.contains_key(node) { - with_map.remove(node); - return node.mutate_into_next(self.env.ctx); - } else { - let comb_group = i.cond_group().unwrap(); - let comb_assigns = ScheduledAssignments::new(node.comp, self.env.ctx.primary[comb_group].assignments, None); + // this is bad but it works for now, what a headache + let contains_node = with_map.contains_key(node); - with_map.insert(node.clone(), comb_group); + if i.cond_group().is_some() && !contains_node { + let comb_group = i.cond_group().unwrap(); + let comb_assigns = ScheduledAssignments::new(node.comp, self.env.ctx.primary[comb_group].assignments, None); - // TODO griffin: Sort out a way to make this error less terrible - // NOTE THIS MIGHT INTRODUCE A BUG SINCE THE PORTS - // HAVE NOT BEEN UNDEFINED YET - self.simulate_combinational(&[comb_assigns]).expect("something went wrong in evaluating with clause for if statement"); + with_map.insert(node.clone(), comb_group); - // now we fall through and proceed as normal - } - } + // TODO griffin: Sort out a way to make this error less terrible + // NOTE THIS MIGHT INTRODUCE A BUG SINCE THE PORTS + // HAVE NOT BEEN UNDEFINED YET + self.simulate_combinational(&[comb_assigns]).expect("something went wrong in evaluating with clause for if statement"); - let target = GlobalPortRef::from_local( - i.cond_port(), - &self.env.cells[node.comp].unwrap_comp().index_bases, - ); + // now we fall through and proceed as normal + } - let result = match target { - GlobalPortRef::Port(p) => self.env.ports[p] - .as_bool() - .expect("if condition is undefined"), - GlobalPortRef::Ref(r) => { - let index = self.env.ref_ports[r].unwrap(); - self.env.ports[index] + if i.cond_group().is_some() && contains_node { + with_map.remove(node); + node.mutate_into_next(self.env.ctx) + } else { + let target = GlobalPortRef::from_local(i.cond_port(), &self.env.cells[node.comp].unwrap_comp().index_bases); + let result = match target { + GlobalPortRef::Port(p) => self.env.ports[p] .as_bool() - .expect("if condition is undefined") - } - }; + .expect("if condition is undefined"), + GlobalPortRef::Ref(r) => { + let index = self.env.ref_ports[r].unwrap(); + self.env.ports[index] + .as_bool() + .expect("if condition is undefined") + } + }; - let target = if result { i.tbranch() } else { i.fbranch() }; - *node = node.new_retain_comp(target); - true + let target = if result { i.tbranch() } else { i.fbranch() }; + *node = node.new_retain_comp(target); + true + } } ControlNode::While(w) => { if w.cond_group().is_some() { @@ -742,8 +928,41 @@ impl<'a> Simulator<'a> { node.mutate_into_next(self.env.ctx) } } - ControlNode::Invoke(_) => todo!("invokes not implemented yet"), + ControlNode::Invoke(i) => { + let done = self.get_global_port_idx(&i.done, node.comp); + + if i.comb_group.is_some() && !with_map.contains_key(node) { + with_map.insert(node.clone(), i.comb_group.unwrap()); + } + + + if !self.env.ports[done].as_bool().unwrap_or_default() { + leaf_nodes.push(node.clone()); + true + } else { + self.cleanup_ref_cells(node.comp, i); + + if i.comb_group.is_some() { + with_map.remove(node); + } + + node.mutate_into_next(self.env.ctx) + } + }, + }; + + if !retain_bool && ControlPoint::get_next(node, self.env.ctx).is_none() && + // either we are not a par node, or we are the last par node + (!matches!(&self.env.ctx.primary[node.control_node_idx], ControlNode::Par(_)) || !par_map.contains_key(node)) { + + set_done.push(self.env.get_comp_done(node.comp)); + let comp_ledger = self.env.cells[node.comp].unwrap_comp(); + *node = node.new_retain_comp(self.env.ctx.primary[comp_ledger.comp_id].control.unwrap()); + true + } else { + retain_bool } + }); self.env.pc.restore_fields(vecs, par_map, with_map); @@ -752,6 +971,10 @@ impl<'a> Simulator<'a> { self.env.pc.vec_mut().extend(new_nodes); self.undef_all_ports(); + self.set_root_go_high(); + for port in set_done { + self.env.ports[port] = PortValue::new_implicit(Value::bit_high()); + } for node in &leaf_nodes { match &self.env.ctx.primary[node.control_node_idx] { @@ -767,7 +990,13 @@ impl<'a> Simulator<'a> { self.env.ports[go_idx] = PortValue::new_implicit(Value::bit_high()); } - ControlNode::Invoke(_) => todo!(), + ControlNode::Invoke(i) => { + let go = self.get_global_port_idx(&i.go, node.comp); + self.env.ports[go] = + PortValue::new_implicit(Value::bit_high()); + + self.intialize_ref_cells(node.comp, i); + } non_leaf => { unreachable!("non-leaf node {:?} included in list of leaf nodes. This should never happen, please report it.", non_leaf) } @@ -791,12 +1020,9 @@ impl<'a> Simulator<'a> { } fn is_done(&self) -> bool { - assert!( - self.ctx().primary[self.ctx().entry_point].control.is_some(), - "flat interpreter doesn't handle a fully structural entrypoint program yet" - ); - // TODO griffin: need to handle structural components - self.env.pc.is_done() + self.env.ports[self.env.get_root_done()] + .as_bool() + .unwrap_or_default() } /// Evaluate the entire program @@ -829,8 +1055,10 @@ impl<'a> Simulator<'a> { Guard::Comp(c, a, b) => { let comp_v = self.env.cells[comp].unwrap_comp(); - let a = self.lookup_global_port_id(comp_v.convert_to_global(a)); - let b = self.lookup_global_port_id(comp_v.convert_to_global(b)); + let a = self + .lookup_global_port_id(comp_v.convert_to_global_port(a)); + let b = self + .lookup_global_port_id(comp_v.convert_to_global_port(b)); let a_val = self.env.ports[a].val()?; let b_val = self.env.ports[b].val()?; @@ -846,8 +1074,8 @@ impl<'a> Simulator<'a> { } Guard::Port(p) => { let comp_v = self.env.cells[comp].unwrap_comp(); - let p_idx = - self.lookup_global_port_id(comp_v.convert_to_global(p)); + let p_idx = self + .lookup_global_port_id(comp_v.convert_to_global_port(p)); self.env.ports[p_idx].as_bool() } } @@ -897,6 +1125,8 @@ impl<'a> Simulator<'a> { .as_ref() .map(|x| &ledger.index_bases + x.done); + let comp_go = self.env.get_comp_go(*active_cell); + for assign_idx in assignments { let assign = &self.env.ctx.primary[assign_idx]; @@ -909,14 +1139,16 @@ impl<'a> Simulator<'a> { // the go for the group is high && go .as_ref() - .map(|g| self.env.ports[*g].as_bool().unwrap_or_default()) + // the group must have its go signal high and the go + // signal of the component must also be high + .map(|g| self.env.ports[*g].as_bool().unwrap_or_default() && self.env.ports[comp_go].as_bool().unwrap_or_default()) // if there is no go signal, then we want to run the // assignment .unwrap_or(true) { let val = self.get_value(&assign.src, *active_cell); let dest = - self.get_global_idx(&assign.dst, *active_cell); + self.get_global_port_idx(&assign.dst, *active_cell); if let Some(done) = done { if dest != done { diff --git a/interp/src/flatten/structures/environment/program_counter.rs b/interp/src/flatten/structures/environment/program_counter.rs index a4e0f0bd21..ad212c5e09 100644 --- a/interp/src/flatten/structures/environment/program_counter.rs +++ b/interp/src/flatten/structures/environment/program_counter.rs @@ -344,27 +344,9 @@ pub(crate) struct ProgramCounter { // we need a few things from the program counter impl ProgramCounter { - pub(crate) fn new(ctx: &Context) -> Self { - let root = ctx.entry_point; - // this relies on the fact that we construct the root cell-ledger - // as the first possible cell in the program. If that changes this will break. - let root_cell = GlobalCellIdx::new(0); - - let mut vec = Vec::with_capacity(CONTROL_POINT_PREALLOCATE); - - if let Some(current) = ctx.primary[root].control { - vec.push(ControlPoint { - comp: root_cell, - control_node_idx: current, - }) - } else { - todo!( - "Flat interpreter does not support control-less components yet" - ) - } - + pub(crate) fn new_empty() -> Self { Self { - vec, + vec: Vec::with_capacity(CONTROL_POINT_PREALLOCATE), par_map: HashMap::new(), continuous_assigns: Vec::new(), with_map: HashMap::new(), @@ -375,10 +357,6 @@ impl ProgramCounter { self.vec.iter() } - pub fn is_done(&self) -> bool { - self.vec.is_empty() - } - pub fn _iter_mut(&mut self) -> impl Iterator { self.vec.iter_mut() } diff --git a/interp/src/flatten/structures/printer.rs b/interp/src/flatten/structures/printer.rs index 8a3f5ba5c2..3f92aaed5d 100644 --- a/interp/src/flatten/structures/printer.rs +++ b/interp/src/flatten/structures/printer.rs @@ -266,8 +266,10 @@ impl<'a> Printer<'a> { let ref_cells = self.format_invoke_ref_cell_list(i, parent); out += &format!("[{}]", ref_cells); } - let inputs = self.format_invoke_port_lists(&i.inputs, parent); - let outputs = self.format_invoke_port_lists(&i.outputs, parent); + let inputs = + self.format_invoke_port_lists(&i.signature.inputs, parent); + let outputs = + self.format_invoke_port_lists(&i.signature.outputs, parent); out += &format!("({inputs})({outputs})");