sim: implement memories, still needs testing
This commit is contained in:
parent
cd0dd7b7ee
commit
e4cf66adf8
|
@ -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> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -294,3 +294,4 @@ fn test_shift_register() {
|
|||
}
|
||||
|
||||
// TODO: add tests for enums
|
||||
// TODO: add tests for memories
|
||||
|
|
Loading…
Reference in a new issue