sim: implement memories, still needs testing
All checks were successful
/ deps (push) Successful in 18s
/ test (push) Successful in 5m15s
/ deps (pull_request) Successful in 14s
/ test (pull_request) Successful in 5m20s

This commit is contained in:
Jacob Lifshay 2024-12-09 23:03:01 -08:00
parent cd0dd7b7ee
commit e4cf66adf8
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
3 changed files with 808 additions and 92 deletions

View file

@ -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<Insn>,
insns: Vec<InsnOrLabel>,
source_location: SourceLocation,
}
@ -1116,12 +1116,16 @@ impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'
impl Assignment {
fn new(
conditions: Interned<[Cond]>,
insns: Vec<Insn>,
insns: Vec<InsnOrLabel>,
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<StatePartKindSmallSlots>,
addr_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
en_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
data: CompiledValue<CanonicalType>,
read_data_delayed: Vec<TypeIndex>,
write_data_delayed: Vec<TypeIndex>,
write_mask_delayed: Vec<TypeIndex>,
data_layout: CompiledTypeLayout<CanonicalType>,
read_data_delayed: Vec<TypeIndexRange>,
write_data_delayed: Vec<TypeIndexRange>,
write_mask_delayed: Vec<TypeIndexRange>,
write_mode_delayed: Vec<StatePartIndex<StatePartKindSmallSlots>>,
write_insns: Vec<InsnOrLabel>,
}
struct MemoryPortReadInsns<'a> {
addr: StatePartIndex<StatePartKindSmallSlots>,
en: StatePartIndex<StatePartKindSmallSlots>,
write_mode: Option<StatePartIndex<StatePartKindSmallSlots>>,
data: TypeIndexRange,
insns: &'a mut Vec<InsnOrLabel>,
}
struct MemoryPortWriteInsns<'a> {
addr: StatePartIndex<StatePartKindSmallSlots>,
en: StatePartIndex<StatePartKindSmallSlots>,
write_mode: Option<StatePartIndex<StatePartKindSmallSlots>>,
data: TypeIndexRange,
mask: TypeIndexRange,
insns: &'a mut Vec<InsnOrLabel>,
}
#[derive(Debug)]
@ -1749,13 +1771,13 @@ impl Compiler {
self.compiled_exprs_to_values.insert(expr, retval);
retval
}
fn add_assignment(
fn add_assignment<T: Into<InsnOrLabel>>(
&mut self,
conditions: Interned<[Cond]>,
insns: impl IntoIterator<Item = Insn>,
insns: impl IntoIterator<Item = T>,
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<T, BK: InsnsBuildingKind>(
&mut self,
len: usize,
layout: &TypeLayout<BK>,
first: Option<T>,
last: Option<T>,
mut from_allocation: impl FnMut(TypeIndexRange) -> T,
) -> Vec<T> {
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<StatePartIndex<StatePartKindSmallSlots>>,
last: Option<StatePartIndex<StatePartKindSmallSlots>>,
) -> Vec<StatePartIndex<StatePartKindSmallSlots>> {
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<StatePartKindMemories>,
stride: usize,
mut start: usize,
data_layout: CompiledTypeLayout<CanonicalType>,
mask_layout: CompiledTypeLayout<CanonicalType>,
mut read: Option<MemoryPortReadInsns<'_>>,
mut write: Option<MemoryPortWriteInsns<'_>>,
) {
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 = <Array>::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<StatePartKindMemories>,
data_layout: CompiledTypeLayout<CanonicalType>,
mask_layout: CompiledTypeLayout<CanonicalType>,
mut read: Option<MemoryPortReadInsns<'_>>,
mut write: Option<MemoryPortWriteInsns<'_>>,
) {
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<TraceDecl>,
) {
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<usize>,
write_cycle: Option<usize>,
}
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::<CondStackEntry<'_>>::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<StatePartKindSmallSlots>]> {
@ -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<StatePartKindSmallSlots>]| {
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<Bundle> {

View file

@ -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<Insn> for InsnOrLabel {
fn from(value: Insn) -> Self {
Self::Insn(value)
}
}
impl From<Label> for InsnOrLabel {
fn from(value: Label) -> Self {
Self::Label(value)
}
}
impl fmt::Debug for InsnOrLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Insn(v) => v.fmt(f),
Self::Label(v) => v.fmt(f),
}
}
}
struct LabelData {
address: Option<usize>,
}
#[derive(Default)]
pub(crate) struct Labels {
labels: Vec<Label>,
address_to_label_indexes_map: HashMap<usize, Vec<usize>>,
labels: Vec<LabelData>,
address_to_label_map: HashMap<usize, Vec<Label>>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
@ -518,12 +554,12 @@ struct InsnDebug<'a, BK: InsnsBuildingKind> {
impl<BK: InsnsBuildingKind> fmt::Debug for InsnDebug<'_, BK> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(label_indexes) = self
if let Some(labels) = self
.labels
.and_then(|labels| labels.address_to_label_indexes_map.get(&self.address))
.and_then(|labels| labels.address_to_label_map.get(&self.address))
{
for &label_index in label_indexes {
writeln!(f, "L{label_index}:")?;
for &label in labels {
writeln!(f, "{label:?}")?;
}
}
if let Some(source_location) = self.source_location {
@ -2019,14 +2055,17 @@ impl Insns<InsnsBuilding> {
self.insns.push(insn);
self.insn_source_locations.push(source_location);
}
pub(crate) fn extend(
pub(crate) fn extend<T: Into<InsnOrLabel>>(
&mut self,
insns: impl IntoIterator<Item = Insn>,
insns_or_labels: impl IntoIterator<Item = T>,
source_location: SourceLocation,
) {
self.insns.extend(insns);
self.insn_source_locations
.resize(self.insns.len(), source_location);
for insn_or_label in insns_or_labels {
match insn_or_label.into() {
InsnOrLabel::Insn(insn) => self.push(insn, source_location),
InsnOrLabel::Label(label) => self.define_label_at_next_insn(label),
}
}
}
pub(crate) fn allocate_variable<BK: InsnsBuildingKind>(
&mut self,
@ -2034,22 +2073,22 @@ impl Insns<InsnsBuilding> {
) -> TypeIndexRange {
self.state_layout.ty.allocate(layout)
}
pub(crate) fn new_label(&mut self) -> usize {
let retval = self.labels.labels.len();
self.labels.labels.push(Label { address: None });
pub(crate) fn new_label(&mut self) -> Label {
let retval = Label(self.labels.labels.len());
self.labels.labels.push(LabelData { address: None });
retval
}
#[track_caller]
pub(crate) fn define_label_at_next_insn(&mut self, label_index: usize) {
pub(crate) fn define_label_at_next_insn(&mut self, label: Label) {
let address = self.next_insn_pc();
let label = &mut self.labels.labels[label_index];
assert!(label.address.is_none(), "label already defined");
label.address = Some(address);
let label_data = &mut self.labels.labels[label.0];
assert!(label_data.address.is_none(), "label already defined");
label_data.address = Some(address);
self.labels
.address_to_label_indexes_map
.address_to_label_map
.entry(address)
.or_default()
.push(label_index);
.push(label);
}
}
@ -2727,6 +2766,15 @@ impl_insns! {
state.big_slots[dest].clone_from(&value);
next!();
}
ConstSmall {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
value: SmallUInt,
} => {
state.small_slots[dest] = value;
next!();
}
Branch {
#[kind = BranchTarget]
target: usize,

View file

@ -294,3 +294,4 @@ fn test_shift_register() {
}
// TODO: add tests for enums
// TODO: add tests for memories