From e4cf66adf8e50cfe0b2d5ac95ba220878aa295ba Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 9 Dec 2024 23:03:01 -0800 Subject: [PATCH] sim: implement memories, still needs testing --- crates/fayalite/src/sim.rs | 809 ++++++++++++++++++++++--- crates/fayalite/src/sim/interpreter.rs | 90 ++- crates/fayalite/tests/sim.rs | 1 + 3 files changed, 808 insertions(+), 92 deletions(-) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 321ab49..d2906e1 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -26,12 +26,12 @@ use crate::{ reset::{ResetType, ResetTypeDispatch}, sim::{ interpreter::{ - Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed, - StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, - StatePartKindMemories, StatePartKindSmallSlots, StatePartLayout, StatePartLen, - StatePartsValue, TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange, - TypeLayout, TypeLen, TypeParts, + Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding, + InsnsBuildingDone, InsnsBuildingKind, Label, SlotDebugData, SmallUInt, State, + StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, StatePartIndexRange, + StatePartKind, StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, + TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, time::{SimDuration, SimInstant}, }, @@ -970,7 +970,7 @@ struct Assignment { inputs: SlotSet, outputs: SlotSet, conditions: Interned<[Cond]>, - insns: Vec, + insns: Vec, source_location: SourceLocation, } @@ -1116,12 +1116,16 @@ impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<' impl Assignment { fn new( conditions: Interned<[Cond]>, - insns: Vec, + insns: Vec, source_location: SourceLocation, ) -> Self { let mut inputs = SlotSet::default(); let mut outputs = SlotSet::default(); for insn in &insns { + let insn = match insn { + InsnOrLabel::Insn(insn) => insn, + InsnOrLabel::Label(_) => continue, + }; for InsnField { ty, kind } in insn.fields() { match (kind, ty) { (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { @@ -1220,11 +1224,29 @@ struct MemoryPort { clk_triggered: StatePartIndex, addr_delayed: Vec>, en_delayed: Vec>, - data: CompiledValue, - read_data_delayed: Vec, - write_data_delayed: Vec, - write_mask_delayed: Vec, + data_layout: CompiledTypeLayout, + read_data_delayed: Vec, + write_data_delayed: Vec, + write_mask_delayed: Vec, write_mode_delayed: Vec>, + write_insns: Vec, +} + +struct MemoryPortReadInsns<'a> { + addr: StatePartIndex, + en: StatePartIndex, + write_mode: Option>, + data: TypeIndexRange, + insns: &'a mut Vec, +} + +struct MemoryPortWriteInsns<'a> { + addr: StatePartIndex, + en: StatePartIndex, + write_mode: Option>, + data: TypeIndexRange, + mask: TypeIndexRange, + insns: &'a mut Vec, } #[derive(Debug)] @@ -1749,13 +1771,13 @@ impl Compiler { self.compiled_exprs_to_values.insert(expr, retval); retval } - fn add_assignment( + fn add_assignment>( &mut self, conditions: Interned<[Cond]>, - insns: impl IntoIterator, + insns: impl IntoIterator, source_location: SourceLocation, ) { - let insns = Vec::from_iter(insns); + let insns = Vec::from_iter(insns.into_iter().map(Into::into)); self.assignments .push(Assignment::new(conditions, insns, source_location)); } @@ -1828,12 +1850,14 @@ impl Compiler { { return retval; } + let mut ty = compiled_value.layout.ty; + ty.width = ty.width.min(SmallUInt::BITS as usize); let retval = match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, TypeLen::A_BIG_SLOT => { let debug_data = SlotDebugData { name: Interned::default(), - ty: UInt::<{ SmallUInt::BITS as usize }>::TYPE.canonical(), + ty: ty.canonical(), }; let dest = self .insns @@ -3324,6 +3348,443 @@ impl Compiler { } self.make_trace_decl(*parent_module, target_base) } + fn allocate_delay_chain( + &mut self, + len: usize, + layout: &TypeLayout, + first: Option, + last: Option, + mut from_allocation: impl FnMut(TypeIndexRange) -> T, + ) -> Vec { + match (len, first, last) { + (0, _, _) => Vec::new(), + (1, Some(v), _) | (1, None, Some(v)) => vec![v], + (2, Some(first), Some(last)) => vec![first, last], + (len, first, last) => { + let inner_len = len - first.is_some() as usize - last.is_some() as usize; + first + .into_iter() + .chain( + (0..inner_len) + .map(|_| from_allocation(self.insns.allocate_variable(layout))), + ) + .chain(last) + .collect() + } + } + } + fn allocate_delay_chain_small( + &mut self, + len: usize, + ty: CanonicalType, + first: Option>, + last: Option>, + ) -> Vec> { + self.allocate_delay_chain( + len, + &TypeLayout { + small_slots: StatePartLayout::scalar( + SlotDebugData { + name: Interned::default(), + ty, + }, + (), + ), + big_slots: StatePartLayout::empty(), + }, + first, + last, + |range| range.small_slots.start, + ) + } + fn compile_memory_port_rw_helper( + &mut self, + memory: StatePartIndex, + stride: usize, + mut start: usize, + data_layout: CompiledTypeLayout, + mask_layout: CompiledTypeLayout, + mut read: Option>, + mut write: Option>, + ) { + match data_layout.body { + CompiledTypeLayoutBody::Scalar => { + let CompiledTypeLayoutBody::Scalar = mask_layout.body else { + unreachable!(); + }; + let signed = match data_layout.ty { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => false, + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + let width = data_layout.ty.bit_width(); + if let Some(MemoryPortReadInsns { + addr, + en: _, + write_mode: _, + data, + insns, + }) = read + { + insns.push( + match data.len() { + TypeLen::A_BIG_SLOT => { + let dest = data.big_slots.start; + if signed { + Insn::MemoryReadSInt { + dest, + memory, + addr, + stride, + start, + width, + } + } else { + Insn::MemoryReadUInt { + dest, + memory, + addr, + stride, + start, + width, + } + } + } + TypeLen::A_SMALL_SLOT => { + let _dest = data.small_slots.start; + todo!("memory ports' data are always big for now"); + } + _ => unreachable!(), + } + .into(), + ); + } + if let Some(MemoryPortWriteInsns { + addr, + en: _, + write_mode: _, + data, + mask, + insns, + }) = write + { + let end_label = self.insns.new_label(); + insns.push( + match mask.len() { + TypeLen::A_BIG_SLOT => Insn::BranchIfZero { + target: end_label.0, + value: mask.big_slots.start, + }, + TypeLen::A_SMALL_SLOT => Insn::BranchIfSmallZero { + target: end_label.0, + value: mask.small_slots.start, + }, + _ => unreachable!(), + } + .into(), + ); + insns.push( + match data.len() { + TypeLen::A_BIG_SLOT => { + let dest = data.big_slots.start; + if signed { + Insn::MemoryReadSInt { + dest, + memory, + addr, + stride, + start, + width, + } + } else { + Insn::MemoryReadUInt { + dest, + memory, + addr, + stride, + start, + width, + } + } + } + TypeLen::A_SMALL_SLOT => { + let _dest = data.small_slots.start; + todo!("memory ports' data are always big for now"); + } + _ => unreachable!(), + } + .into(), + ); + insns.push(end_label.into()); + } + } + CompiledTypeLayoutBody::Array { element } => { + let CompiledTypeLayoutBody::Array { + element: mask_element, + } = mask_layout.body + else { + unreachable!(); + }; + let ty = ::from_canonical(data_layout.ty); + let element_bit_width = ty.element().bit_width(); + let element_size = element.layout.len(); + let mask_element_size = mask_element.layout.len(); + for element_index in 0..ty.len() { + self.compile_memory_port_rw_helper( + memory, + stride, + start, + *element, + *mask_element, + read.as_mut().map( + |MemoryPortReadInsns { + addr, + en, + write_mode, + data, + insns, + }| MemoryPortReadInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.index_array(element_size, element_index), + insns, + }, + ), + write.as_mut().map( + |MemoryPortWriteInsns { + addr, + en, + write_mode, + data, + mask, + insns, + }| { + MemoryPortWriteInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.index_array(element_size, element_index), + mask: mask.index_array(mask_element_size, element_index), + insns, + } + }, + ), + ); + start += element_bit_width; + } + } + CompiledTypeLayoutBody::Bundle { fields } => { + let CompiledTypeLayoutBody::Bundle { + fields: mask_fields, + } = mask_layout.body + else { + unreachable!(); + }; + assert_eq!(fields.len(), mask_fields.len()); + for (field, mask_field) in fields.into_iter().zip(mask_fields) { + let field_index_range = + TypeIndexRange::new(field.offset, field.ty.layout.len()); + let mask_field_index_range = + TypeIndexRange::new(mask_field.offset, mask_field.ty.layout.len()); + self.compile_memory_port_rw_helper( + memory, + stride, + start, + field.ty, + mask_field.ty, + read.as_mut().map( + |MemoryPortReadInsns { + addr, + en, + write_mode, + data, + insns, + }| MemoryPortReadInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.slice(field_index_range), + insns, + }, + ), + write.as_mut().map( + |MemoryPortWriteInsns { + addr, + en, + write_mode, + data, + mask, + insns, + }| { + MemoryPortWriteInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.slice(field_index_range), + mask: mask.slice(mask_field_index_range), + insns, + } + }, + ), + ); + start = start + field.ty.ty.bit_width(); + } + } + } + } + fn compile_memory_port_rw( + &mut self, + memory: StatePartIndex, + data_layout: CompiledTypeLayout, + mask_layout: CompiledTypeLayout, + mut read: Option>, + mut write: Option>, + ) { + let read_else_label = read.as_mut().map( + |MemoryPortReadInsns { + addr: _, + en, + write_mode, + data: _, + insns, + }| { + let else_label = self.insns.new_label(); + insns.push( + Insn::BranchIfSmallZero { + target: else_label.0, + value: *en, + } + .into(), + ); + if let Some(write_mode) = *write_mode { + insns.push( + Insn::BranchIfSmallNonZero { + target: else_label.0, + value: write_mode, + } + .into(), + ); + } + else_label + }, + ); + let write_end_label = write.as_mut().map( + |MemoryPortWriteInsns { + addr: _, + en, + write_mode, + data: _, + mask: _, + insns, + }| { + let end_label = self.insns.new_label(); + insns.push( + Insn::BranchIfSmallZero { + target: end_label.0, + value: *en, + } + .into(), + ); + if let Some(write_mode) = *write_mode { + insns.push( + Insn::BranchIfSmallZero { + target: end_label.0, + value: write_mode, + } + .into(), + ); + } + end_label + }, + ); + self.compile_memory_port_rw_helper( + memory, + data_layout.ty.bit_width(), + 0, + data_layout, + mask_layout, + read.as_mut().map( + |MemoryPortReadInsns { + addr, + en, + write_mode, + data, + insns, + }| MemoryPortReadInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: *data, + insns: *insns, + }, + ), + write.as_mut().map( + |MemoryPortWriteInsns { + addr, + en, + write_mode, + data, + mask, + insns, + }| MemoryPortWriteInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: *data, + mask: *mask, + insns: *insns, + }, + ), + ); + if let ( + Some(else_label), + Some(MemoryPortReadInsns { + addr: _, + en: _, + write_mode: _, + data, + insns, + }), + ) = (read_else_label, read) + { + let end_label = self.insns.new_label(); + insns.push( + Insn::Branch { + target: end_label.0, + } + .into(), + ); + insns.push(else_label.into()); + let TypeIndexRange { + small_slots, + big_slots, + } = data; + for dest in small_slots.iter() { + insns.push(Insn::ConstSmall { dest, value: 0 }.into()); + } + for dest in big_slots.iter() { + insns.push( + Insn::Const { + dest, + value: BigInt::ZERO.intern_sized(), + } + .into(), + ); + } + insns.push(end_label.into()); + } + if let (Some(end_label), Some(write)) = (write_end_label, write) { + write.insns.push(end_label.into()); + } + } fn compile_memory( &mut self, mem: Mem, @@ -3331,6 +3792,22 @@ impl Compiler { conditions: Interned<[Cond]>, trace_decls: &mut Vec, ) { + let data_layout = CompiledTypeLayout::get(mem.array_type().element()); + let mask_layout = CompiledTypeLayout::get(mem.array_type().element().mask_type()); + let read_latency_plus_1 = mem + .read_latency() + .checked_add(1) + .expect("read latency too big"); + let write_latency_plus_1 = mem + .write_latency() + .get() + .checked_add(1) + .expect("write latency too big"); + let read_cycle = match mem.read_under_write() { + ReadUnderWrite::Old => 0, + ReadUnderWrite::New => mem.read_latency(), + ReadUnderWrite::Undefined => mem.read_latency() / 2, // something other than Old or New + }; let memory = self .insns .state_layout @@ -3356,42 +3833,188 @@ impl Compiler { }; self.decl_conditions.insert(target, conditions); trace_decls.push(self.make_trace_decl(instantiated_module, target_base)); - match port.port_kind() { - PortKind::ReadOnly => MemoryPort { - clk_triggered: todo!(), - addr_delayed: todo!(), - en_delayed: todo!(), - data: todo!(), - read_data_delayed: todo!(), - write_data_delayed: todo!(), - write_mask_delayed: todo!(), - write_mode_delayed: todo!(), + let clk = Expr::field(port.to_expr(), "clk"); + let clk = self.compile_expr(instantiated_module, clk); + let clk = self.compiled_expr_to_value(clk, mem.source_location()); + let clk_triggered = self + .compile_clock(clk.map_ty(Clock::from_canonical), mem.source_location()) + .clk_triggered; + let en = Expr::field(port.to_expr(), "en"); + let en = self.compile_expr(instantiated_module, en); + let en = self.compiled_expr_to_value(en, mem.source_location()); + let en = self.compiled_value_bool_dest_is_small(en, mem.source_location()); + let addr = Expr::field(port.to_expr(), "addr"); + let addr = self.compile_expr(instantiated_module, addr); + let addr = self.compiled_expr_to_value(addr, mem.source_location()); + let addr_ty = addr.layout.ty; + let addr = self.compiled_value_to_dyn_array_index( + addr.map_ty(UInt::from_canonical), + mem.source_location(), + ); + let read_data = port.port_kind().rdata_name().map(|name| { + let read_data = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let read_data = self.compiled_expr_to_value(read_data, mem.source_location()); + read_data.range + }); + let write_data = port.port_kind().wdata_name().map(|name| { + let write_data = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let write_data = self.compiled_expr_to_value(write_data, mem.source_location()); + write_data.range + }); + let write_mask = port.port_kind().wmask_name().map(|name| { + let write_mask = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let write_mask = self.compiled_expr_to_value(write_mask, mem.source_location()); + write_mask.range + }); + let write_mode = port.port_kind().wmode_name().map(|name| { + let write_mode = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let write_mode = self.compiled_expr_to_value(write_mode, mem.source_location()); + self.compiled_value_bool_dest_is_small(write_mode, mem.source_location()) + }); + struct PortParts { + en_delayed_len: usize, + addr_delayed_len: usize, + read_data_delayed_len: usize, + write_data_delayed_len: usize, + write_mask_delayed_len: usize, + write_mode_delayed_len: usize, + read_cycle: Option, + write_cycle: Option, + } + let PortParts { + en_delayed_len, + addr_delayed_len, + read_data_delayed_len, + write_data_delayed_len, + write_mask_delayed_len, + write_mode_delayed_len, + read_cycle, + write_cycle, + } = match port.port_kind() { + PortKind::ReadOnly => PortParts { + en_delayed_len: read_cycle + 1, + addr_delayed_len: read_cycle + 1, + read_data_delayed_len: read_latency_plus_1 - read_cycle, + write_data_delayed_len: 0, + write_mask_delayed_len: 0, + write_mode_delayed_len: 0, + read_cycle: Some(read_cycle), + write_cycle: None, }, - PortKind::WriteOnly => MemoryPort { - clk_triggered: todo!(), - addr_delayed: todo!(), - en_delayed: todo!(), - data: todo!(), - read_data_delayed: todo!(), - write_data_delayed: todo!(), - write_mask_delayed: todo!(), - write_mode_delayed: todo!(), - }, - PortKind::ReadWrite => MemoryPort { - clk_triggered: todo!(), - addr_delayed: todo!(), - en_delayed: todo!(), - data: todo!(), - read_data_delayed: todo!(), - write_data_delayed: todo!(), - write_mask_delayed: todo!(), - write_mode_delayed: todo!(), + PortKind::WriteOnly => PortParts { + en_delayed_len: write_latency_plus_1, + addr_delayed_len: write_latency_plus_1, + read_data_delayed_len: 0, + write_data_delayed_len: write_latency_plus_1, + write_mask_delayed_len: write_latency_plus_1, + write_mode_delayed_len: 0, + read_cycle: None, + write_cycle: Some(mem.write_latency().get()), }, + PortKind::ReadWrite => { + let can_rw_at_end = match mem.read_under_write() { + ReadUnderWrite::Old => false, + ReadUnderWrite::New | ReadUnderWrite::Undefined => true, + }; + let latency_plus_1 = read_latency_plus_1; + if latency_plus_1 != write_latency_plus_1 || !can_rw_at_end { + todo!( + "not sure what to do, issue: \ + https://github.com/chipsalliance/firrtl-spec/issues/263" + ); + } + PortParts { + en_delayed_len: latency_plus_1, + addr_delayed_len: latency_plus_1, + read_data_delayed_len: 1, + write_data_delayed_len: latency_plus_1, + write_mask_delayed_len: latency_plus_1, + write_mode_delayed_len: latency_plus_1, + read_cycle: Some(latency_plus_1 - 1), + write_cycle: Some(latency_plus_1 - 1), + } + } + }; + let addr_delayed = self.allocate_delay_chain_small( + addr_delayed_len, + addr_ty.canonical(), + Some(addr), + None, + ); + let en_delayed = self.allocate_delay_chain_small( + en_delayed_len, + Bool.canonical(), + Some(en), + None, + ); + let read_data_delayed = self.allocate_delay_chain( + read_data_delayed_len, + &data_layout.layout, + None, + read_data, + |v| v, + ); + let write_data_delayed = self.allocate_delay_chain( + write_data_delayed_len, + &data_layout.layout, + write_data, + None, + |v| v, + ); + let write_mask_delayed = self.allocate_delay_chain( + write_mask_delayed_len, + &mask_layout.layout, + write_mask, + None, + |v| v, + ); + let write_mode_delayed = self.allocate_delay_chain_small( + write_mode_delayed_len, + Bool.canonical(), + write_mode, + None, + ); + let mut read_insns = Vec::new(); + let mut write_insns = Vec::new(); + self.compile_memory_port_rw( + memory, + data_layout, + mask_layout, + read_cycle.map(|read_cycle| MemoryPortReadInsns { + addr: addr_delayed[read_cycle], + en: en_delayed[read_cycle], + write_mode: write_mode_delayed.get(read_cycle).copied(), + data: read_data_delayed[0], + insns: &mut read_insns, + }), + write_cycle.map(|write_cycle| MemoryPortWriteInsns { + addr: addr_delayed[write_cycle], + en: en_delayed[write_cycle], + write_mode: write_mode_delayed.get(write_cycle).copied(), + data: write_data_delayed[write_cycle], + mask: write_mask_delayed[write_cycle], + insns: &mut write_insns, + }), + ); + self.add_assignment(Interned::default(), read_insns, mem.source_location()); + MemoryPort { + clk_triggered, + addr_delayed, + en_delayed, + data_layout, + read_data_delayed, + write_data_delayed, + write_mask_delayed, + write_mode_delayed, + write_insns, } }) .collect(); self.memories.push(Memory { mem, memory, ports }); - todo!("implement memory"); } fn compile_block( &mut self, @@ -3549,7 +4172,7 @@ impl Compiler { }; struct CondStackEntry<'a> { cond: &'a Cond, - end_label_index: usize, + end_label: Label, } let mut cond_stack = Vec::>::new(); for assignment_index in assignments_order { @@ -3568,35 +4191,33 @@ impl Compiler { same_len = index + 1; } while cond_stack.len() > same_len { - let CondStackEntry { - cond: _, - end_label_index, - } = cond_stack.pop().expect("just checked len"); - self.insns.define_label_at_next_insn(end_label_index); + let CondStackEntry { cond: _, end_label } = + cond_stack.pop().expect("just checked len"); + self.insns.define_label_at_next_insn(end_label); } for cond in &conditions[cond_stack.len()..] { - let end_label_index = self.insns.new_label(); + let end_label = self.insns.new_label(); match cond.body { CondBody::IfTrue { cond: cond_value } | CondBody::IfFalse { cond: cond_value } => { let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() { TypeLen::A_SMALL_SLOT => ( Insn::BranchIfSmallZero { - target: end_label_index, + target: end_label.0, value: cond_value.range.small_slots.start, }, Insn::BranchIfSmallNonZero { - target: end_label_index, + target: end_label.0, value: cond_value.range.small_slots.start, }, ), TypeLen::A_BIG_SLOT => ( Insn::BranchIfZero { - target: end_label_index, + target: end_label.0, value: cond_value.range.big_slots.start, }, Insn::BranchIfNonZero { - target: end_label_index, + target: end_label.0, value: cond_value.range.big_slots.start, }, ), @@ -3617,7 +4238,7 @@ impl Compiler { } => { self.insns.push( Insn::BranchIfSmallNeImmediate { - target: end_label_index, + target: end_label.0, lhs: discriminant, rhs: variant_index as _, }, @@ -3625,14 +4246,9 @@ impl Compiler { ); } } - cond_stack.push(CondStackEntry { - cond, - end_label_index, - }); - } - for insn in insns { - self.insns.push(*insn, *source_location); + cond_stack.push(CondStackEntry { cond, end_label }); } + self.insns.extend(insns.iter().copied(), *source_location); } } fn process_clocks(&mut self) -> Interned<[StatePartIndex]> { @@ -3674,11 +4290,11 @@ impl Compiler { let reg_end = self.insns.new_label(); let reg_reset = self.insns.new_label(); let branch_if_reset = Insn::BranchIfSmallNonZero { - target: reg_reset, + target: reg_reset.0, value: rst, }; let branch_if_not_triggered = Insn::BranchIfSmallZero { - target: reg_end, + target: reg_end.0, value: clk_triggered, }; if is_async { @@ -3693,7 +4309,7 @@ impl Compiler { source_location, ); self.insns - .push(Insn::Branch { target: reg_end }, source_location); + .push(Insn::Branch { target: reg_end.0 }, source_location); self.insns.define_label_at_next_insn(reg_reset); self.insns .extend(value.range.insns_for_copy_from(init.range), source_location); @@ -3703,7 +4319,7 @@ impl Compiler { let reg_end = self.insns.new_label(); self.insns.push( Insn::BranchIfSmallZero { - target: reg_end, + target: reg_end.0, value: clk_triggered, }, source_location, @@ -3718,8 +4334,59 @@ impl Compiler { } } fn process_memories(&mut self) { - for memory in mem::take(&mut self.memories) { - todo!(); + for Memory { + mem, + memory: _, + ports, + } in mem::take(&mut self.memories) + { + for MemoryPort { + clk_triggered, + addr_delayed, + en_delayed, + data_layout: _, + read_data_delayed, + write_data_delayed, + write_mask_delayed, + write_mode_delayed, + write_insns, + } in ports + { + let port_end = self.insns.new_label(); + self.insns.push( + Insn::BranchIfSmallZero { + target: port_end.0, + value: clk_triggered, + }, + mem.source_location(), + ); + self.insns.extend(write_insns, mem.source_location()); + let small_shift_reg = + |this: &mut Self, values: &[StatePartIndex]| { + for pair in values.windows(2).rev() { + this.insns.push( + Insn::CopySmall { + dest: pair[1], + src: pair[0], + }, + mem.source_location(), + ); + } + }; + let shift_reg = |this: &mut Self, values: &[TypeIndexRange]| { + for pair in values.windows(2).rev() { + this.insns + .extend(pair[0].insns_for_copy_to(pair[1]), mem.source_location()); + } + }; + small_shift_reg(self, &addr_delayed); + small_shift_reg(self, &en_delayed); + shift_reg(self, &read_data_delayed); + shift_reg(self, &write_data_delayed); + shift_reg(self, &write_mask_delayed); + small_shift_reg(self, &write_mode_delayed); + self.insns.define_label_at_next_insn(port_end); + } } } pub fn compile(mut self) -> Compiled { diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 0fd0591..23d1275 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -477,14 +477,50 @@ impl InsnsBuildingKind for InsnsBuilding { } } -struct Label { +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct Label(pub(crate) usize); + +impl fmt::Debug for Label { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "L{}:", self.0) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum InsnOrLabel { + Insn(Insn), + Label(Label), +} + +impl From for InsnOrLabel { + fn from(value: Insn) -> Self { + Self::Insn(value) + } +} + +impl From