forked from libre-chip/fayalite
4160 lines
150 KiB
Rust
4160 lines
150 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
|
|
//! Fayalite Simulation
|
|
|
|
use crate::{
|
|
bundle::{BundleField, BundleType},
|
|
expr::{
|
|
ops,
|
|
target::{
|
|
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
|
|
TargetPathElement,
|
|
},
|
|
ExprEnum, Flow, ToLiteralBits,
|
|
},
|
|
int::BoolOrIntType,
|
|
intern::{Intern, Interned, Memoize},
|
|
module::{
|
|
AnnotatedModuleIO, Block, Id, ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt,
|
|
StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg,
|
|
StmtWire,
|
|
},
|
|
prelude::*,
|
|
sim::{
|
|
interpreter::{
|
|
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
|
SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed,
|
|
StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
|
|
StatePartKindSmallSlots, StatePartLayout, StatePartLen, StatePartsValue,
|
|
TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeLen,
|
|
TypeParts,
|
|
},
|
|
time::{SimDuration, SimInstant},
|
|
},
|
|
ty::StaticType,
|
|
util::{BitSliceWriteWithBase, DebugAsDisplay},
|
|
};
|
|
use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec};
|
|
use hashbrown::{HashMap, HashSet};
|
|
use num_bigint::BigInt;
|
|
use num_traits::{Signed, ToPrimitive, Zero};
|
|
use petgraph::visit::{
|
|
GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable,
|
|
};
|
|
use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut};
|
|
|
|
mod interpreter;
|
|
pub mod time;
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
enum CondBody {
|
|
IfTrue {
|
|
cond: CompiledValue<Bool>,
|
|
},
|
|
IfFalse {
|
|
cond: CompiledValue<Bool>,
|
|
},
|
|
MatchArm {
|
|
enum_expr: CompiledValue<Enum>,
|
|
variant_index: usize,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
struct Cond {
|
|
body: CondBody,
|
|
source_location: SourceLocation,
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
|
enum InstantiatedModule {
|
|
Base(Interned<Module<Bundle>>),
|
|
Child {
|
|
parent: Interned<InstantiatedModule>,
|
|
instance: Interned<Instance<Bundle>>,
|
|
},
|
|
}
|
|
|
|
impl InstantiatedModule {
|
|
fn leaf_module(self) -> Interned<Module<Bundle>> {
|
|
match self {
|
|
InstantiatedModule::Base(base) => base,
|
|
InstantiatedModule::Child { instance, .. } => instance.instantiated(),
|
|
}
|
|
}
|
|
fn write_path(self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
InstantiatedModule::Base(base) => fmt::Debug::fmt(&base.name_id(), f),
|
|
InstantiatedModule::Child { parent, instance } => {
|
|
parent.write_path(f)?;
|
|
write!(f, ".{}", instance.name_id())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for InstantiatedModule {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "InstantiatedModule(")?;
|
|
self.write_path(f)?;
|
|
write!(f, ": {})", self.leaf_module().name_id())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
struct TargetInInstantiatedModule {
|
|
instantiated_module: InstantiatedModule,
|
|
target: Target,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
struct CompiledBundleField {
|
|
offset: TypeIndex,
|
|
ty: CompiledTypeLayout<CanonicalType>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
enum CompiledTypeLayoutBody {
|
|
Scalar,
|
|
Array {
|
|
/// debug names are ignored, use parent's layout instead
|
|
element: Interned<CompiledTypeLayout<CanonicalType>>,
|
|
},
|
|
Bundle {
|
|
/// debug names are ignored, use parent's layout instead
|
|
fields: Interned<[CompiledBundleField]>,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
struct CompiledTypeLayout<T: Type> {
|
|
ty: T,
|
|
layout: TypeLayout<InsnsBuildingDone>,
|
|
body: CompiledTypeLayoutBody,
|
|
}
|
|
|
|
impl<T: Type> CompiledTypeLayout<T> {
|
|
fn with_prefixed_debug_names(self, prefix: &str) -> Self {
|
|
let Self { ty, layout, body } = self;
|
|
Self {
|
|
ty,
|
|
layout: layout.with_prefixed_debug_names(prefix),
|
|
body,
|
|
}
|
|
}
|
|
fn with_anonymized_debug_info(self) -> Self {
|
|
let Self { ty, layout, body } = self;
|
|
Self {
|
|
ty,
|
|
layout: layout.with_anonymized_debug_info(),
|
|
body,
|
|
}
|
|
}
|
|
fn get(ty: T) -> Self {
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
struct MyMemoize;
|
|
impl Memoize for MyMemoize {
|
|
type Input = CanonicalType;
|
|
type InputOwned = CanonicalType;
|
|
type Output = CompiledTypeLayout<CanonicalType>;
|
|
|
|
fn inner(self, input: &Self::Input) -> Self::Output {
|
|
match input {
|
|
CanonicalType::UInt(_)
|
|
| CanonicalType::SInt(_)
|
|
| CanonicalType::Bool(_)
|
|
| CanonicalType::Enum(_)
|
|
| CanonicalType::AsyncReset(_)
|
|
| CanonicalType::SyncReset(_)
|
|
| CanonicalType::Reset(_)
|
|
| CanonicalType::Clock(_) => {
|
|
let mut layout = TypeLayout::empty();
|
|
let debug_data = SlotDebugData {
|
|
name: Interned::default(),
|
|
ty: *input,
|
|
};
|
|
layout.big_slots = StatePartLayout::scalar(debug_data);
|
|
CompiledTypeLayout {
|
|
ty: *input,
|
|
layout: layout.into(),
|
|
body: CompiledTypeLayoutBody::Scalar,
|
|
}
|
|
}
|
|
CanonicalType::Array(array) => {
|
|
let mut layout = TypeLayout::empty();
|
|
let element = CompiledTypeLayout::get(array.element()).intern_sized();
|
|
for index in 0..array.len() {
|
|
layout.allocate(
|
|
&element
|
|
.layout
|
|
.with_prefixed_debug_names(&format!("[{index}]")),
|
|
);
|
|
}
|
|
CompiledTypeLayout {
|
|
ty: *input,
|
|
layout: layout.into(),
|
|
body: CompiledTypeLayoutBody::Array { element },
|
|
}
|
|
}
|
|
CanonicalType::Bundle(bundle) => {
|
|
let mut layout = TypeLayout::empty();
|
|
let fields = bundle
|
|
.fields()
|
|
.iter()
|
|
.map(
|
|
|BundleField {
|
|
name,
|
|
flipped: _,
|
|
ty,
|
|
}| {
|
|
let ty = CompiledTypeLayout::get(*ty);
|
|
let offset = layout
|
|
.allocate(
|
|
&ty.layout
|
|
.with_prefixed_debug_names(&format!(".{name}")),
|
|
)
|
|
.start();
|
|
CompiledBundleField { offset, ty }
|
|
},
|
|
)
|
|
.collect();
|
|
CompiledTypeLayout {
|
|
ty: *input,
|
|
layout: layout.into(),
|
|
body: CompiledTypeLayoutBody::Bundle { fields },
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let CompiledTypeLayout {
|
|
ty: _,
|
|
layout,
|
|
body,
|
|
} = MyMemoize.get_owned(ty.canonical());
|
|
Self { ty, layout, body }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
|
struct CompiledValue<T: Type> {
|
|
layout: CompiledTypeLayout<T>,
|
|
range: TypeIndexRange,
|
|
write: Option<(CompiledTypeLayout<T>, TypeIndexRange)>,
|
|
}
|
|
|
|
impl<T: Type> CompiledValue<T> {
|
|
fn write(self) -> (CompiledTypeLayout<T>, TypeIndexRange) {
|
|
self.write.unwrap_or((self.layout, self.range))
|
|
}
|
|
fn map<U: Type>(
|
|
self,
|
|
mut f: impl FnMut(
|
|
CompiledTypeLayout<T>,
|
|
TypeIndexRange,
|
|
) -> (CompiledTypeLayout<U>, TypeIndexRange),
|
|
) -> CompiledValue<U> {
|
|
let (layout, range) = f(self.layout, self.range);
|
|
CompiledValue {
|
|
layout,
|
|
range,
|
|
write: self.write.map(|(layout, range)| f(layout, range)),
|
|
}
|
|
}
|
|
fn map_ty<U: Type>(self, mut f: impl FnMut(T) -> U) -> CompiledValue<U> {
|
|
self.map(|CompiledTypeLayout { ty, layout, body }, range| {
|
|
(
|
|
CompiledTypeLayout {
|
|
ty: f(ty),
|
|
layout,
|
|
body,
|
|
},
|
|
range,
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
impl CompiledValue<Enum> {
|
|
fn add_discriminant_to_set(self, inputs: &mut SlotSet) {
|
|
inputs.extend([self.range]);
|
|
}
|
|
}
|
|
|
|
impl CompiledValue<Bundle> {
|
|
fn field_by_index(self, field_index: usize) -> CompiledValue<CanonicalType> {
|
|
self.map(|layout, range| {
|
|
let CompiledTypeLayout {
|
|
ty: _,
|
|
layout: _,
|
|
body: CompiledTypeLayoutBody::Bundle { fields },
|
|
} = layout
|
|
else {
|
|
unreachable!();
|
|
};
|
|
(
|
|
fields[field_index].ty,
|
|
range.slice(TypeIndexRange::new(
|
|
fields[field_index].offset,
|
|
fields[field_index].ty.layout.len(),
|
|
)),
|
|
)
|
|
})
|
|
}
|
|
fn field_by_name(self, name: Interned<str>) -> CompiledValue<CanonicalType> {
|
|
self.field_by_index(self.layout.ty.name_indexes()[&name])
|
|
}
|
|
}
|
|
|
|
impl CompiledValue<Array> {
|
|
fn element(self, index: usize) -> CompiledValue<CanonicalType> {
|
|
self.map(|layout, range| {
|
|
let CompiledTypeLayoutBody::Array { element } = layout.body else {
|
|
unreachable!();
|
|
};
|
|
(*element, range.index_array(element.layout.len(), index))
|
|
})
|
|
}
|
|
fn element_dyn(
|
|
self,
|
|
index_slot: StatePartIndex<StatePartKindSmallSlots>,
|
|
) -> CompiledExpr<CanonicalType> {
|
|
CompiledExpr::from(self).element_dyn(index_slot)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
|
struct CompiledExpr<T: Type> {
|
|
static_part: CompiledValue<T>,
|
|
indexes: TypeArrayIndexes,
|
|
}
|
|
|
|
impl<T: Type> From<CompiledValue<T>> for CompiledExpr<T> {
|
|
fn from(static_part: CompiledValue<T>) -> Self {
|
|
Self {
|
|
static_part,
|
|
indexes: TypeArrayIndexes::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Type> CompiledExpr<T> {
|
|
fn map_ty<U: Type>(self, f: impl FnMut(T) -> U) -> CompiledExpr<U> {
|
|
let Self {
|
|
static_part,
|
|
indexes,
|
|
} = self;
|
|
CompiledExpr {
|
|
static_part: static_part.map_ty(f),
|
|
indexes,
|
|
}
|
|
}
|
|
fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) {
|
|
let Self {
|
|
static_part,
|
|
indexes,
|
|
} = self;
|
|
indexes.as_ref().for_each_offset(|offset| {
|
|
inputs.extend([static_part.range.offset(offset)]);
|
|
});
|
|
}
|
|
fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) {
|
|
let Self {
|
|
static_part: _,
|
|
indexes,
|
|
} = self;
|
|
self.add_target_without_indexes_to_set(inputs);
|
|
inputs.extend(indexes.as_ref().iter());
|
|
}
|
|
}
|
|
|
|
impl CompiledExpr<Bundle> {
|
|
fn field_by_index(self, field_index: usize) -> CompiledExpr<CanonicalType> {
|
|
CompiledExpr {
|
|
static_part: self.static_part.field_by_index(field_index),
|
|
indexes: self.indexes,
|
|
}
|
|
}
|
|
fn field_by_name(self, name: Interned<str>) -> CompiledExpr<CanonicalType> {
|
|
CompiledExpr {
|
|
static_part: self.static_part.field_by_name(name),
|
|
indexes: self.indexes,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CompiledExpr<Array> {
|
|
fn element(self, index: usize) -> CompiledExpr<CanonicalType> {
|
|
CompiledExpr {
|
|
static_part: self.static_part.element(index),
|
|
indexes: self.indexes,
|
|
}
|
|
}
|
|
fn element_dyn(
|
|
self,
|
|
index_slot: StatePartIndex<StatePartKindSmallSlots>,
|
|
) -> CompiledExpr<CanonicalType> {
|
|
let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else {
|
|
unreachable!();
|
|
};
|
|
let stride = element.layout.len();
|
|
let indexes = self.indexes.join(TypeArrayIndex::from_parts(
|
|
index_slot,
|
|
self.static_part.layout.ty.len(),
|
|
stride,
|
|
));
|
|
CompiledExpr {
|
|
static_part: self.static_part.map(|layout, range| {
|
|
let CompiledTypeLayoutBody::Array { element } = layout.body else {
|
|
unreachable!();
|
|
};
|
|
(*element, range.index_array(stride, 0))
|
|
}),
|
|
indexes,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
enum AssignmentOrSlotIndex {
|
|
AssignmentIndex(usize),
|
|
SmallSlots(StatePartIndex<StatePartKindSmallSlots>),
|
|
BigSlots(StatePartIndex<StatePartKindBigSlots>),
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
enum AssignmentIO {
|
|
BigInput {
|
|
assignment_index: usize,
|
|
slot: StatePartIndex<StatePartKindBigSlots>,
|
|
},
|
|
SmallInput {
|
|
assignment_index: usize,
|
|
slot: StatePartIndex<StatePartKindSmallSlots>,
|
|
},
|
|
BigOutput {
|
|
assignment_index: usize,
|
|
slot: StatePartIndex<StatePartKindBigSlots>,
|
|
},
|
|
SmallOutput {
|
|
assignment_index: usize,
|
|
slot: StatePartIndex<StatePartKindSmallSlots>,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Assignments {
|
|
Accumulating {
|
|
assignments: Vec<Assignment>,
|
|
},
|
|
Finalized {
|
|
assignments: Box<[Assignment]>,
|
|
slot_readers: SlotToAssignmentIndexFullMap,
|
|
slot_writers: SlotToAssignmentIndexFullMap,
|
|
assignment_immediate_predecessors: Box<[Box<[usize]>]>,
|
|
assignment_immediate_successors: Box<[Box<[usize]>]>,
|
|
},
|
|
}
|
|
|
|
impl Default for Assignments {
|
|
fn default() -> Self {
|
|
Self::Accumulating {
|
|
assignments: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Assignments {
|
|
fn finalize(&mut self, slots_len: TypeLen) {
|
|
let Self::Accumulating { assignments } = self else {
|
|
unreachable!("already finalized");
|
|
};
|
|
let assignments = mem::take(assignments).into_boxed_slice();
|
|
let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len);
|
|
let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len);
|
|
let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()];
|
|
let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()];
|
|
for (assignment_index, assignment) in assignments.iter().enumerate() {
|
|
slot_readers
|
|
.keys_for_assignment(assignment_index)
|
|
.extend([&assignment.inputs]);
|
|
let SlotSet(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = &assignment.outputs;
|
|
for &slot in small_slots {
|
|
if let Some(&pred) = slot_writers[slot].last() {
|
|
assignment_immediate_predecessors[assignment_index].insert(pred);
|
|
assignment_immediate_successors[pred].insert(assignment_index);
|
|
}
|
|
slot_writers[slot].push(assignment_index);
|
|
}
|
|
for &slot in big_slots {
|
|
if let Some(&pred) = slot_writers[slot].last() {
|
|
assignment_immediate_predecessors[assignment_index].insert(pred);
|
|
assignment_immediate_successors[pred].insert(assignment_index);
|
|
}
|
|
slot_writers[slot].push(assignment_index);
|
|
}
|
|
}
|
|
*self = Self::Finalized {
|
|
assignments,
|
|
slot_readers,
|
|
slot_writers,
|
|
assignment_immediate_predecessors: assignment_immediate_predecessors
|
|
.into_iter()
|
|
.map(Box::from_iter)
|
|
.collect(),
|
|
assignment_immediate_successors: assignment_immediate_successors
|
|
.into_iter()
|
|
.map(Box::from_iter)
|
|
.collect(),
|
|
};
|
|
}
|
|
fn push(&mut self, v: Assignment) {
|
|
let Self::Accumulating { assignments } = self else {
|
|
unreachable!("already finalized");
|
|
};
|
|
assignments.push(v);
|
|
}
|
|
fn assignments(&self) -> &[Assignment] {
|
|
let Self::Finalized { assignments, .. } = self else {
|
|
unreachable!("Assignments::finalize should have been called");
|
|
};
|
|
assignments
|
|
}
|
|
fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap {
|
|
let Self::Finalized { slot_readers, .. } = self else {
|
|
unreachable!("Assignments::finalize should have been called");
|
|
};
|
|
slot_readers
|
|
}
|
|
fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap {
|
|
let Self::Finalized { slot_writers, .. } = self else {
|
|
unreachable!("Assignments::finalize should have been called");
|
|
};
|
|
slot_writers
|
|
}
|
|
fn assignment_immediate_predecessors(&self) -> &[Box<[usize]>] {
|
|
let Self::Finalized {
|
|
assignment_immediate_predecessors,
|
|
..
|
|
} = self
|
|
else {
|
|
unreachable!("Assignments::finalize should have been called");
|
|
};
|
|
assignment_immediate_predecessors
|
|
}
|
|
fn assignment_immediate_successors(&self) -> &[Box<[usize]>] {
|
|
let Self::Finalized {
|
|
assignment_immediate_successors,
|
|
..
|
|
} = self
|
|
else {
|
|
unreachable!("Assignments::finalize should have been called");
|
|
};
|
|
assignment_immediate_successors
|
|
}
|
|
}
|
|
|
|
impl GraphBase for Assignments {
|
|
type EdgeId = AssignmentIO;
|
|
type NodeId = AssignmentOrSlotIndex;
|
|
}
|
|
|
|
struct AssignmentsNodeIdentifiers {
|
|
assignment_indexes: std::ops::Range<usize>,
|
|
small_slots: std::ops::Range<u32>,
|
|
big_slots: std::ops::Range<u32>,
|
|
}
|
|
|
|
impl Iterator for AssignmentsNodeIdentifiers {
|
|
type Item = AssignmentOrSlotIndex;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let Self {
|
|
assignment_indexes,
|
|
small_slots,
|
|
big_slots,
|
|
} = self;
|
|
assignment_indexes
|
|
.next()
|
|
.map(AssignmentOrSlotIndex::AssignmentIndex)
|
|
.or_else(|| {
|
|
small_slots.next().map(|value| {
|
|
AssignmentOrSlotIndex::SmallSlots(StatePartIndex {
|
|
value,
|
|
_phantom: PhantomData,
|
|
})
|
|
})
|
|
})
|
|
.or_else(|| {
|
|
big_slots.next().map(|value| {
|
|
AssignmentOrSlotIndex::BigSlots(StatePartIndex {
|
|
value,
|
|
_phantom: PhantomData,
|
|
})
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoNodeIdentifiers for &'a Assignments {
|
|
type NodeIdentifiers = AssignmentsNodeIdentifiers;
|
|
|
|
fn node_identifiers(self) -> Self::NodeIdentifiers {
|
|
let TypeLen {
|
|
small_slots,
|
|
big_slots,
|
|
} = self.slot_readers().len();
|
|
AssignmentsNodeIdentifiers {
|
|
assignment_indexes: 0..self.assignments().len(),
|
|
small_slots: 0..small_slots.value,
|
|
big_slots: 0..big_slots.value,
|
|
}
|
|
}
|
|
}
|
|
|
|
struct AssignmentsNeighborsDirected<'a> {
|
|
assignment_indexes: std::slice::Iter<'a, usize>,
|
|
small_slots: std::collections::btree_set::Iter<'a, StatePartIndex<StatePartKindSmallSlots>>,
|
|
big_slots: std::collections::btree_set::Iter<'a, StatePartIndex<StatePartKindBigSlots>>,
|
|
}
|
|
|
|
impl Iterator for AssignmentsNeighborsDirected<'_> {
|
|
type Item = AssignmentOrSlotIndex;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let Self {
|
|
assignment_indexes,
|
|
small_slots,
|
|
big_slots,
|
|
} = self;
|
|
if let retval @ Some(_) = assignment_indexes
|
|
.next()
|
|
.copied()
|
|
.map(AssignmentOrSlotIndex::AssignmentIndex)
|
|
{
|
|
retval
|
|
} else if let retval @ Some(_) = small_slots
|
|
.next()
|
|
.copied()
|
|
.map(AssignmentOrSlotIndex::SmallSlots)
|
|
{
|
|
retval
|
|
} else if let retval @ Some(_) = big_slots
|
|
.next()
|
|
.copied()
|
|
.map(AssignmentOrSlotIndex::BigSlots)
|
|
{
|
|
retval
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoNeighbors for &'a Assignments {
|
|
type Neighbors = AssignmentsNeighborsDirected<'a>;
|
|
|
|
fn neighbors(self, n: Self::NodeId) -> Self::Neighbors {
|
|
self.neighbors_directed(n, petgraph::Direction::Outgoing)
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoNeighborsDirected for &'a Assignments {
|
|
type NeighborsDirected = AssignmentsNeighborsDirected<'a>;
|
|
|
|
fn neighbors_directed(
|
|
self,
|
|
n: Self::NodeId,
|
|
d: petgraph::Direction,
|
|
) -> Self::NeighborsDirected {
|
|
use petgraph::Direction::*;
|
|
let slot_map = match d {
|
|
Outgoing => self.slot_readers(),
|
|
Incoming => self.slot_writers(),
|
|
};
|
|
match n {
|
|
AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => {
|
|
let assignment = &self.assignments()[assignment_index];
|
|
let (
|
|
assignment_indexes,
|
|
SlotSet(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}),
|
|
) = match d {
|
|
Outgoing => (
|
|
&self.assignment_immediate_successors()[assignment_index],
|
|
&assignment.outputs,
|
|
),
|
|
Incoming => (
|
|
&self.assignment_immediate_predecessors()[assignment_index],
|
|
&assignment.inputs,
|
|
),
|
|
};
|
|
AssignmentsNeighborsDirected {
|
|
assignment_indexes: assignment_indexes.iter(),
|
|
small_slots: small_slots.iter(),
|
|
big_slots: big_slots.iter(),
|
|
}
|
|
}
|
|
AssignmentOrSlotIndex::SmallSlots(slot) => AssignmentsNeighborsDirected {
|
|
assignment_indexes: slot_map[slot].iter(),
|
|
small_slots: Default::default(),
|
|
big_slots: Default::default(),
|
|
},
|
|
AssignmentOrSlotIndex::BigSlots(slot) => AssignmentsNeighborsDirected {
|
|
assignment_indexes: slot_map[slot].iter(),
|
|
small_slots: Default::default(),
|
|
big_slots: Default::default(),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
struct AssignmentsVisitMap {
|
|
assignments: Vec<bool>,
|
|
slots: DenseSlotSet,
|
|
}
|
|
|
|
impl VisitMap<AssignmentOrSlotIndex> for AssignmentsVisitMap {
|
|
fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool {
|
|
match n {
|
|
AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => {
|
|
!mem::replace(&mut self.assignments[assignment_index], true)
|
|
}
|
|
AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.insert(slot),
|
|
AssignmentOrSlotIndex::BigSlots(slot) => self.slots.insert(slot),
|
|
}
|
|
}
|
|
|
|
fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool {
|
|
match *n {
|
|
AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => {
|
|
self.assignments[assignment_index]
|
|
}
|
|
AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.contains(slot),
|
|
AssignmentOrSlotIndex::BigSlots(slot) => self.slots.contains(slot),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Visitable for Assignments {
|
|
type Map = AssignmentsVisitMap;
|
|
|
|
fn visit_map(self: &Self) -> Self::Map {
|
|
AssignmentsVisitMap {
|
|
assignments: vec![false; self.assignments().len()],
|
|
slots: DenseSlotSet::new(self.slot_readers().len()),
|
|
}
|
|
}
|
|
|
|
fn reset_map(self: &Self, map: &mut Self::Map) {
|
|
let AssignmentsVisitMap { assignments, slots } = map;
|
|
assignments.clear();
|
|
assignments.resize(self.assignments().len(), false);
|
|
if slots.len() != self.slot_readers().len() {
|
|
*slots = DenseSlotSet::new(self.slot_readers().len());
|
|
} else {
|
|
slots.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
struct DenseSlotSet(TypeParts<Self>);
|
|
|
|
impl DenseSlotSet {
|
|
fn new(len: TypeLen) -> Self {
|
|
let TypeLen {
|
|
small_slots,
|
|
big_slots,
|
|
} = len;
|
|
Self(TypeParts {
|
|
small_slots: vec![false; small_slots.value.try_into().expect("length too big")]
|
|
.into_boxed_slice(),
|
|
big_slots: vec![false; big_slots.value.try_into().expect("length too big")]
|
|
.into_boxed_slice(),
|
|
})
|
|
}
|
|
fn len(&self) -> TypeLen {
|
|
TypeLen {
|
|
small_slots: StatePartLen {
|
|
value: self.0.small_slots.len() as _,
|
|
_phantom: PhantomData,
|
|
},
|
|
big_slots: StatePartLen {
|
|
value: self.0.big_slots.len() as _,
|
|
_phantom: PhantomData,
|
|
},
|
|
}
|
|
}
|
|
fn clear(&mut self) {
|
|
let Self(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = self;
|
|
small_slots.fill(false);
|
|
big_slots.fill(false);
|
|
}
|
|
}
|
|
|
|
impl StatePartsValue for DenseSlotSet {
|
|
type Value<K: StatePartKind> = Box<[bool]>;
|
|
}
|
|
|
|
trait DenseSlotSetMethods<K: StatePartKind>: Extend<StatePartIndex<K>> {
|
|
fn contains(&self, k: StatePartIndex<K>) -> bool;
|
|
fn remove(&mut self, k: StatePartIndex<K>) -> bool {
|
|
self.take(k).is_some()
|
|
}
|
|
fn take(&mut self, k: StatePartIndex<K>) -> Option<StatePartIndex<K>>;
|
|
fn replace(&mut self, k: StatePartIndex<K>) -> Option<StatePartIndex<K>>;
|
|
fn insert(&mut self, k: StatePartIndex<K>) -> bool {
|
|
self.replace(k).is_none()
|
|
}
|
|
}
|
|
|
|
impl<K: StatePartKind> Extend<StatePartIndex<K>> for DenseSlotSet
|
|
where
|
|
Self: DenseSlotSetMethods<K>,
|
|
{
|
|
fn extend<T: IntoIterator<Item = StatePartIndex<K>>>(&mut self, iter: T) {
|
|
iter.into_iter().for_each(|v| {
|
|
self.insert(v);
|
|
});
|
|
}
|
|
}
|
|
|
|
impl DenseSlotSetMethods<StatePartKindSmallSlots> for DenseSlotSet {
|
|
fn contains(&self, k: StatePartIndex<StatePartKindSmallSlots>) -> bool {
|
|
self.0.small_slots[k.as_usize()]
|
|
}
|
|
|
|
fn take(
|
|
&mut self,
|
|
k: StatePartIndex<StatePartKindSmallSlots>,
|
|
) -> Option<StatePartIndex<StatePartKindSmallSlots>> {
|
|
mem::replace(self.0.small_slots.get_mut(k.as_usize())?, false).then_some(k)
|
|
}
|
|
|
|
fn replace(
|
|
&mut self,
|
|
k: StatePartIndex<StatePartKindSmallSlots>,
|
|
) -> Option<StatePartIndex<StatePartKindSmallSlots>> {
|
|
mem::replace(&mut self.0.small_slots[k.as_usize()], true).then_some(k)
|
|
}
|
|
}
|
|
|
|
impl DenseSlotSetMethods<StatePartKindBigSlots> for DenseSlotSet {
|
|
fn contains(&self, k: StatePartIndex<StatePartKindBigSlots>) -> bool {
|
|
self.0.big_slots[k.as_usize()]
|
|
}
|
|
|
|
fn take(
|
|
&mut self,
|
|
k: StatePartIndex<StatePartKindBigSlots>,
|
|
) -> Option<StatePartIndex<StatePartKindBigSlots>> {
|
|
mem::replace(self.0.big_slots.get_mut(k.as_usize())?, false).then_some(k)
|
|
}
|
|
|
|
fn replace(
|
|
&mut self,
|
|
k: StatePartIndex<StatePartKindBigSlots>,
|
|
) -> Option<StatePartIndex<StatePartKindBigSlots>> {
|
|
mem::replace(&mut self.0.big_slots[k.as_usize()], true).then_some(k)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
|
struct SlotVec(TypeParts<Self>);
|
|
|
|
impl SlotVec {
|
|
fn is_empty(&self) -> bool {
|
|
let Self(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = self;
|
|
small_slots.is_empty() && big_slots.is_empty()
|
|
}
|
|
}
|
|
|
|
impl StatePartsValue for SlotVec {
|
|
type Value<K: StatePartKind> = Vec<StatePartIndex<K>>;
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
|
struct SlotSet(TypeParts<Self>);
|
|
|
|
impl SlotSet {
|
|
fn is_empty(&self) -> bool {
|
|
let Self(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = self;
|
|
small_slots.is_empty() && big_slots.is_empty()
|
|
}
|
|
fn for_each(
|
|
&self,
|
|
small_slots_fn: impl FnMut(StatePartIndex<StatePartKindSmallSlots>),
|
|
big_slots_fn: impl FnMut(StatePartIndex<StatePartKindBigSlots>),
|
|
) {
|
|
let Self(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = self;
|
|
small_slots.iter().copied().for_each(small_slots_fn);
|
|
big_slots.iter().copied().for_each(big_slots_fn);
|
|
}
|
|
fn all(
|
|
&self,
|
|
small_slots_fn: impl FnMut(StatePartIndex<StatePartKindSmallSlots>) -> bool,
|
|
big_slots_fn: impl FnMut(StatePartIndex<StatePartKindBigSlots>) -> bool,
|
|
) -> bool {
|
|
let Self(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = self;
|
|
small_slots.iter().copied().all(small_slots_fn)
|
|
&& big_slots.iter().copied().all(big_slots_fn)
|
|
}
|
|
}
|
|
|
|
impl StatePartsValue for SlotSet {
|
|
type Value<K: StatePartKind> = BTreeSet<StatePartIndex<K>>;
|
|
}
|
|
|
|
impl Extend<StatePartIndex<StatePartKindSmallSlots>> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = StatePartIndex<StatePartKindSmallSlots>>>(&mut self, iter: T) {
|
|
self.0.small_slots.extend(iter);
|
|
}
|
|
}
|
|
|
|
impl Extend<StatePartIndex<StatePartKindBigSlots>> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = StatePartIndex<StatePartKindBigSlots>>>(&mut self, iter: T) {
|
|
self.0.big_slots.extend(iter);
|
|
}
|
|
}
|
|
|
|
impl<K: StatePartKind> Extend<StatePartIndexRange<K>> for SlotSet
|
|
where
|
|
Self: Extend<StatePartIndex<K>>,
|
|
{
|
|
fn extend<T: IntoIterator<Item = StatePartIndexRange<K>>>(&mut self, iter: T) {
|
|
self.extend(iter.into_iter().flat_map(|v| v.iter()));
|
|
}
|
|
}
|
|
|
|
impl Extend<TypeIndexRange> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = TypeIndexRange>>(&mut self, iter: T) {
|
|
iter.into_iter().for_each(
|
|
|TypeIndexRange {
|
|
small_slots,
|
|
big_slots,
|
|
}| {
|
|
self.extend(small_slots.iter());
|
|
self.extend(big_slots.iter());
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Extend<TypeArrayIndex> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = TypeArrayIndex>>(&mut self, iter: T) {
|
|
iter.into_iter().for_each(
|
|
|TypeArrayIndex {
|
|
small_slots,
|
|
big_slots,
|
|
}| {
|
|
self.extend([small_slots]);
|
|
self.extend([big_slots]);
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<K: StatePartKind> Extend<StatePartArrayIndex<K>> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = StatePartArrayIndex<K>>>(&mut self, iter: T) {
|
|
self.extend(iter.into_iter().map(|v| v.index));
|
|
}
|
|
}
|
|
|
|
impl Extend<CondBody> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = CondBody>>(&mut self, iter: T) {
|
|
iter.into_iter().for_each(|cond_body| match cond_body {
|
|
CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => {
|
|
self.extend([cond.range]);
|
|
}
|
|
CondBody::MatchArm {
|
|
enum_expr,
|
|
variant_index: _,
|
|
} => enum_expr.add_discriminant_to_set(self),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Extend<Cond> for SlotSet {
|
|
fn extend<T: IntoIterator<Item = Cond>>(&mut self, iter: T) {
|
|
self.extend(iter.into_iter().map(|v| v.body))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Assignment {
|
|
inputs: SlotSet,
|
|
outputs: SlotSet,
|
|
conditions: Interned<[Cond]>,
|
|
insns: Vec<Insn>,
|
|
source_location: SourceLocation,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct SlotToAssignmentIndexFullMap(TypeParts<Self>);
|
|
|
|
impl StatePartsValue for SlotToAssignmentIndexFullMap {
|
|
type Value<K: StatePartKind> = Box<[Vec<usize>]>;
|
|
}
|
|
|
|
impl SlotToAssignmentIndexFullMap {
|
|
fn new(len: TypeLen) -> Self {
|
|
let TypeLen {
|
|
small_slots,
|
|
big_slots,
|
|
} = len;
|
|
Self(TypeParts {
|
|
small_slots: vec![Vec::new(); small_slots.value.try_into().expect("length too big")]
|
|
.into_boxed_slice(),
|
|
big_slots: vec![Vec::new(); big_slots.value.try_into().expect("length too big")]
|
|
.into_boxed_slice(),
|
|
})
|
|
}
|
|
fn len(&self) -> TypeLen {
|
|
TypeLen {
|
|
small_slots: StatePartLen {
|
|
value: self.0.small_slots.len() as _,
|
|
_phantom: PhantomData,
|
|
},
|
|
big_slots: StatePartLen {
|
|
value: self.0.big_slots.len() as _,
|
|
_phantom: PhantomData,
|
|
},
|
|
}
|
|
}
|
|
fn keys_for_assignment(
|
|
&mut self,
|
|
assignment_index: usize,
|
|
) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> {
|
|
SlotToAssignmentIndexFullMapKeysForAssignment {
|
|
map: self,
|
|
assignment_index,
|
|
}
|
|
}
|
|
fn for_each(
|
|
&self,
|
|
mut small_slots_fn: impl FnMut(StatePartIndex<StatePartKindSmallSlots>, &[usize]),
|
|
mut big_slots_fn: impl FnMut(StatePartIndex<StatePartKindBigSlots>, &[usize]),
|
|
) {
|
|
let Self(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
}) = self;
|
|
small_slots.iter().enumerate().for_each(|(k, v)| {
|
|
small_slots_fn(
|
|
StatePartIndex {
|
|
value: k as _,
|
|
_phantom: PhantomData,
|
|
},
|
|
v,
|
|
)
|
|
});
|
|
big_slots.iter().enumerate().for_each(|(k, v)| {
|
|
big_slots_fn(
|
|
StatePartIndex {
|
|
value: k as _,
|
|
_phantom: PhantomData,
|
|
},
|
|
v,
|
|
)
|
|
});
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<StatePartIndex<StatePartKindSmallSlots>> for SlotToAssignmentIndexFullMap {
|
|
type Output = Vec<usize>;
|
|
|
|
fn index(&self, index: StatePartIndex<StatePartKindSmallSlots>) -> &Self::Output {
|
|
&self.0.small_slots[index.as_usize()]
|
|
}
|
|
}
|
|
|
|
impl std::ops::IndexMut<StatePartIndex<StatePartKindSmallSlots>> for SlotToAssignmentIndexFullMap {
|
|
fn index_mut(&mut self, index: StatePartIndex<StatePartKindSmallSlots>) -> &mut Self::Output {
|
|
&mut self.0.small_slots[index.as_usize()]
|
|
}
|
|
}
|
|
|
|
impl std::ops::Index<StatePartIndex<StatePartKindBigSlots>> for SlotToAssignmentIndexFullMap {
|
|
type Output = Vec<usize>;
|
|
|
|
fn index(&self, index: StatePartIndex<StatePartKindBigSlots>) -> &Self::Output {
|
|
&self.0.big_slots[index.as_usize()]
|
|
}
|
|
}
|
|
|
|
impl std::ops::IndexMut<StatePartIndex<StatePartKindBigSlots>> for SlotToAssignmentIndexFullMap {
|
|
fn index_mut(&mut self, index: StatePartIndex<StatePartKindBigSlots>) -> &mut Self::Output {
|
|
&mut self.0.big_slots[index.as_usize()]
|
|
}
|
|
}
|
|
|
|
struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> {
|
|
map: &'a mut SlotToAssignmentIndexFullMap,
|
|
assignment_index: usize,
|
|
}
|
|
|
|
impl<'a, K: StatePartKind> Extend<&'a StatePartIndex<K>>
|
|
for SlotToAssignmentIndexFullMapKeysForAssignment<'_>
|
|
where
|
|
Self: Extend<StatePartIndex<K>>,
|
|
{
|
|
fn extend<T: IntoIterator<Item = &'a StatePartIndex<K>>>(&mut self, iter: T) {
|
|
self.extend(iter.into_iter().copied());
|
|
}
|
|
}
|
|
|
|
impl<K: StatePartKind> Extend<StatePartIndex<K>>
|
|
for SlotToAssignmentIndexFullMapKeysForAssignment<'_>
|
|
where
|
|
SlotToAssignmentIndexFullMap: IndexMut<StatePartIndex<K>, Output = Vec<usize>>,
|
|
{
|
|
fn extend<T: IntoIterator<Item = StatePartIndex<K>>>(&mut self, iter: T) {
|
|
iter.into_iter()
|
|
.for_each(|slot| self.map[slot].push(self.assignment_index));
|
|
}
|
|
}
|
|
|
|
impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> {
|
|
fn extend<T: IntoIterator<Item = &'a SlotSet>>(&mut self, iter: T) {
|
|
iter.into_iter().for_each(
|
|
|SlotSet(TypeParts {
|
|
small_slots,
|
|
big_slots,
|
|
})| {
|
|
self.extend(small_slots);
|
|
self.extend(big_slots);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
impl Assignment {
|
|
fn new(
|
|
conditions: Interned<[Cond]>,
|
|
insns: Vec<Insn>,
|
|
source_location: SourceLocation,
|
|
) -> Self {
|
|
let mut inputs = SlotSet::default();
|
|
let mut outputs = SlotSet::default();
|
|
for insn in &insns {
|
|
for InsnField { ty, kind } in insn.fields() {
|
|
match (kind, ty) {
|
|
(InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => {
|
|
inputs.extend([slot]);
|
|
}
|
|
(InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => {
|
|
inputs.extend([slot]);
|
|
}
|
|
(
|
|
InsnFieldKind::Input,
|
|
InsnFieldType::SmallSlotArrayIndexed(&array_indexed),
|
|
) => {
|
|
array_indexed.for_each_target(|slot| inputs.extend([slot]));
|
|
inputs.extend(array_indexed.indexes);
|
|
}
|
|
(InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => {
|
|
array_indexed.for_each_target(|slot| inputs.extend([slot]));
|
|
inputs.extend(array_indexed.indexes);
|
|
}
|
|
(InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => {
|
|
outputs.extend([slot]);
|
|
}
|
|
(InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => {
|
|
outputs.extend([slot]);
|
|
}
|
|
(
|
|
InsnFieldKind::Output,
|
|
InsnFieldType::SmallSlotArrayIndexed(&array_indexed),
|
|
) => {
|
|
array_indexed.for_each_target(|slot| {
|
|
outputs.extend([slot]);
|
|
});
|
|
inputs.extend(array_indexed.indexes);
|
|
}
|
|
(InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => {
|
|
array_indexed.for_each_target(|slot| {
|
|
outputs.extend([slot]);
|
|
});
|
|
inputs.extend(array_indexed.indexes);
|
|
}
|
|
(
|
|
_,
|
|
InsnFieldType::SmallUInt(_)
|
|
| InsnFieldType::SmallSInt(_)
|
|
| InsnFieldType::InternedBigInt(_)
|
|
| InsnFieldType::U8(_)
|
|
| InsnFieldType::USize(_)
|
|
| InsnFieldType::Empty(_),
|
|
)
|
|
| (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {}
|
|
}
|
|
}
|
|
}
|
|
Self {
|
|
inputs,
|
|
outputs,
|
|
conditions,
|
|
insns,
|
|
source_location,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Compiler {
|
|
insns: Insns<InsnsBuilding>,
|
|
base_module: Interned<Module<Bundle>>,
|
|
modules: HashMap<InstantiatedModule, CompiledModule>,
|
|
compiled_values: HashMap<TargetInInstantiatedModule, CompiledValue<CanonicalType>>,
|
|
compiled_exprs: HashMap<Expr<CanonicalType>, CompiledExpr<CanonicalType>>,
|
|
compiled_exprs_to_values: HashMap<CompiledExpr<CanonicalType>, CompiledValue<CanonicalType>>,
|
|
decl_conditions: HashMap<TargetInInstantiatedModule, Interned<[Cond]>>,
|
|
compiled_values_to_dyn_array_indexes:
|
|
HashMap<CompiledValue<UInt>, StatePartIndex<StatePartKindSmallSlots>>,
|
|
assignments: Assignments,
|
|
traces: Vec<SimTrace<()>>,
|
|
}
|
|
|
|
impl Compiler {
|
|
pub fn new(base_module: Interned<Module<Bundle>>) -> Self {
|
|
Self {
|
|
insns: Insns::new(),
|
|
base_module,
|
|
modules: HashMap::new(),
|
|
compiled_values: HashMap::new(),
|
|
compiled_exprs: HashMap::new(),
|
|
compiled_exprs_to_values: HashMap::new(),
|
|
decl_conditions: HashMap::new(),
|
|
compiled_values_to_dyn_array_indexes: HashMap::new(),
|
|
assignments: Assignments::default(),
|
|
traces: Vec::new(),
|
|
}
|
|
}
|
|
fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId {
|
|
let id = TraceScalarId(self.traces.len());
|
|
self.traces.push(SimTrace {
|
|
id,
|
|
kind,
|
|
state: (),
|
|
last_state: (),
|
|
});
|
|
id
|
|
}
|
|
fn make_trace_scalar_helper(
|
|
&mut self,
|
|
target: TargetInInstantiatedModule,
|
|
small_kind: impl FnOnce(StatePartIndex<StatePartKindSmallSlots>) -> SimTraceKind,
|
|
big_kind: impl FnOnce(StatePartIndex<StatePartKindBigSlots>) -> SimTraceKind,
|
|
) -> TraceScalarId {
|
|
let compiled_value = self.compile_value(target);
|
|
self.new_sim_trace(match compiled_value.range.len() {
|
|
TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start),
|
|
TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start),
|
|
_ => unreachable!(),
|
|
})
|
|
}
|
|
fn make_trace_scalar(
|
|
&mut self,
|
|
target: TargetInInstantiatedModule,
|
|
name: Interned<str>,
|
|
) -> TraceDecl {
|
|
let flow = target.target.flow();
|
|
match target.target.canonical_ty() {
|
|
CanonicalType::UInt(ty) => TraceUInt {
|
|
id: self.make_trace_scalar_helper(
|
|
target,
|
|
|index| SimTraceKind::SmallUInt { index, ty },
|
|
|index| SimTraceKind::BigUInt { index, ty },
|
|
),
|
|
name,
|
|
ty,
|
|
flow,
|
|
}
|
|
.into(),
|
|
CanonicalType::SInt(ty) => TraceSInt {
|
|
id: self.make_trace_scalar_helper(
|
|
target,
|
|
|index| SimTraceKind::SmallSInt { index, ty },
|
|
|index| SimTraceKind::BigSInt { index, ty },
|
|
),
|
|
name,
|
|
ty,
|
|
flow,
|
|
}
|
|
.into(),
|
|
CanonicalType::Bool(ty) => TraceBool {
|
|
id: self.make_trace_scalar_helper(
|
|
target,
|
|
|index| SimTraceKind::SmallBool { index },
|
|
|index| SimTraceKind::BigBool { index },
|
|
),
|
|
name,
|
|
flow,
|
|
}
|
|
.into(),
|
|
CanonicalType::Array(ty) => unreachable!(),
|
|
CanonicalType::Enum(ty) => todo!(),
|
|
CanonicalType::Bundle(ty) => unreachable!(),
|
|
CanonicalType::AsyncReset(ty) => TraceAsyncReset {
|
|
id: self.make_trace_scalar_helper(
|
|
target,
|
|
|index| SimTraceKind::SmallAsyncReset { index },
|
|
|index| SimTraceKind::BigAsyncReset { index },
|
|
),
|
|
name,
|
|
flow,
|
|
}
|
|
.into(),
|
|
CanonicalType::SyncReset(ty) => TraceSyncReset {
|
|
id: self.make_trace_scalar_helper(
|
|
target,
|
|
|index| SimTraceKind::SmallSyncReset { index },
|
|
|index| SimTraceKind::BigSyncReset { index },
|
|
),
|
|
name,
|
|
flow,
|
|
}
|
|
.into(),
|
|
CanonicalType::Reset(ty) => todo!(),
|
|
CanonicalType::Clock(ty) => TraceClock {
|
|
id: self.make_trace_scalar_helper(
|
|
target,
|
|
|index| SimTraceKind::SmallClock { index },
|
|
|index| SimTraceKind::BigClock { index },
|
|
),
|
|
name,
|
|
flow,
|
|
}
|
|
.into(),
|
|
}
|
|
}
|
|
fn make_trace_decl_child(
|
|
&mut self,
|
|
target: TargetInInstantiatedModule,
|
|
name: Interned<str>,
|
|
) -> TraceDecl {
|
|
match target.target.canonical_ty() {
|
|
CanonicalType::Array(ty) => {
|
|
let elements = Interned::from_iter((0..ty.len()).map(|index| {
|
|
self.make_trace_decl_child(
|
|
TargetInInstantiatedModule {
|
|
instantiated_module: target.instantiated_module,
|
|
target: target.target.join(
|
|
TargetPathElement::from(TargetPathArrayElement { index })
|
|
.intern_sized(),
|
|
),
|
|
},
|
|
Intern::intern_owned(format!("[{index}]")),
|
|
)
|
|
}));
|
|
TraceArray {
|
|
name,
|
|
elements,
|
|
ty,
|
|
flow: target.target.flow(),
|
|
}
|
|
.into()
|
|
}
|
|
CanonicalType::Enum(ty) => todo!(),
|
|
CanonicalType::Bundle(ty) => {
|
|
let fields = Interned::from_iter(ty.fields().iter().map(|field| {
|
|
self.make_trace_decl_child(
|
|
TargetInInstantiatedModule {
|
|
instantiated_module: target.instantiated_module,
|
|
target: target.target.join(
|
|
TargetPathElement::from(TargetPathBundleField { name: field.name })
|
|
.intern_sized(),
|
|
),
|
|
},
|
|
field.name,
|
|
)
|
|
}));
|
|
TraceBundle {
|
|
name,
|
|
fields,
|
|
ty,
|
|
flow: target.target.flow(),
|
|
}
|
|
.into()
|
|
}
|
|
CanonicalType::UInt(_)
|
|
| CanonicalType::SInt(_)
|
|
| CanonicalType::Bool(_)
|
|
| CanonicalType::AsyncReset(_)
|
|
| CanonicalType::SyncReset(_)
|
|
| CanonicalType::Reset(_)
|
|
| CanonicalType::Clock(_) => self.make_trace_scalar(target, name),
|
|
}
|
|
}
|
|
fn make_trace_decl(
|
|
&mut self,
|
|
instantiated_module: InstantiatedModule,
|
|
target_base: TargetBase,
|
|
) -> TraceDecl {
|
|
let target = TargetInInstantiatedModule {
|
|
instantiated_module,
|
|
target: target_base.into(),
|
|
};
|
|
match target_base {
|
|
TargetBase::ModuleIO(module_io) => TraceModuleIO {
|
|
name: module_io.name(),
|
|
child: self
|
|
.make_trace_decl_child(target, module_io.name())
|
|
.intern(),
|
|
ty: module_io.ty(),
|
|
flow: module_io.flow(),
|
|
}
|
|
.into(),
|
|
TargetBase::MemPort(mem_port) => {
|
|
let name = Intern::intern_owned(mem_port.port_name().to_string());
|
|
let TraceDecl::Scope(TraceScope::Bundle(bundle)) =
|
|
self.make_trace_decl_child(target, name)
|
|
else {
|
|
unreachable!()
|
|
};
|
|
TraceMemPort {
|
|
name,
|
|
bundle,
|
|
ty: mem_port.ty(),
|
|
}
|
|
.into()
|
|
}
|
|
TargetBase::Reg(reg) => TraceReg {
|
|
name: reg.name(),
|
|
child: self.make_trace_decl_child(target, reg.name()).intern(),
|
|
ty: reg.ty(),
|
|
}
|
|
.into(),
|
|
TargetBase::Wire(wire) => TraceWire {
|
|
name: wire.name(),
|
|
child: self.make_trace_decl_child(target, wire.name()).intern(),
|
|
ty: wire.ty(),
|
|
}
|
|
.into(),
|
|
TargetBase::Instance(instance) => {
|
|
let TraceDecl::Scope(TraceScope::Bundle(instance_io)) =
|
|
self.make_trace_decl_child(target, instance.name())
|
|
else {
|
|
unreachable!()
|
|
};
|
|
let compiled_module = &self.modules[&InstantiatedModule::Child {
|
|
parent: target.instantiated_module.intern(),
|
|
instance: instance.intern(),
|
|
}];
|
|
TraceInstance {
|
|
name: instance.name(),
|
|
instance_io,
|
|
module: compiled_module.trace_decls,
|
|
ty: instance.ty(),
|
|
}
|
|
.into()
|
|
}
|
|
}
|
|
}
|
|
fn compile_value(
|
|
&mut self,
|
|
target: TargetInInstantiatedModule,
|
|
) -> CompiledValue<CanonicalType> {
|
|
if let Some(&retval) = self.compiled_values.get(&target) {
|
|
return retval;
|
|
}
|
|
let retval = match target.target {
|
|
Target::Base(base) => {
|
|
let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty());
|
|
let layout = unprefixed_layout.with_prefixed_debug_names(&format!(
|
|
"{:?}.{:?}",
|
|
target.instantiated_module,
|
|
base.target_name()
|
|
));
|
|
let range = self.insns.allocate_variable(&layout.layout);
|
|
let write = match *base {
|
|
TargetBase::ModuleIO(_)
|
|
| TargetBase::MemPort(_)
|
|
| TargetBase::Wire(_)
|
|
| TargetBase::Instance(_) => None,
|
|
TargetBase::Reg(_) => {
|
|
let write_layout = unprefixed_layout.with_prefixed_debug_names(&format!(
|
|
"{:?}.{:?}$next",
|
|
target.instantiated_module,
|
|
base.target_name()
|
|
));
|
|
Some((
|
|
write_layout,
|
|
self.insns.allocate_variable(&write_layout.layout),
|
|
))
|
|
}
|
|
};
|
|
CompiledValue {
|
|
range,
|
|
layout,
|
|
write,
|
|
}
|
|
}
|
|
Target::Child(target_child) => {
|
|
let parent = self.compile_value(TargetInInstantiatedModule {
|
|
instantiated_module: target.instantiated_module,
|
|
target: *target_child.parent(),
|
|
});
|
|
match *target_child.path_element() {
|
|
TargetPathElement::BundleField(TargetPathBundleField { name }) => {
|
|
parent.map_ty(Bundle::from_canonical).field_by_name(name)
|
|
}
|
|
TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => {
|
|
parent.map_ty(Array::from_canonical).element(index)
|
|
}
|
|
TargetPathElement::DynArrayElement(_) => unreachable!(),
|
|
}
|
|
}
|
|
};
|
|
self.compiled_values.insert(target, retval);
|
|
retval
|
|
}
|
|
fn compiled_expr_to_value(
|
|
&mut self,
|
|
expr: CompiledExpr<CanonicalType>,
|
|
source_location: SourceLocation,
|
|
) -> CompiledValue<CanonicalType> {
|
|
if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) {
|
|
return retval;
|
|
}
|
|
assert!(
|
|
expr.static_part.layout.ty.is_passive(),
|
|
"invalid expression passed to compiled_expr_to_value -- type must be passive",
|
|
);
|
|
let CompiledExpr {
|
|
static_part,
|
|
indexes,
|
|
} = expr;
|
|
let retval = if indexes.as_ref().is_empty() {
|
|
CompiledValue {
|
|
layout: static_part.layout,
|
|
range: static_part.range,
|
|
write: None,
|
|
}
|
|
} else {
|
|
let layout = static_part.layout.with_anonymized_debug_info();
|
|
let retval = CompiledValue {
|
|
layout,
|
|
range: self.insns.allocate_variable(&layout.layout),
|
|
write: None,
|
|
};
|
|
let TypeIndexRange {
|
|
small_slots,
|
|
big_slots,
|
|
} = retval.range;
|
|
self.add_assignment(
|
|
Interned::default(),
|
|
small_slots
|
|
.iter()
|
|
.zip(static_part.range.small_slots.iter())
|
|
.map(|(dest, base)| Insn::ReadSmallIndexed {
|
|
dest,
|
|
src: StatePartArrayIndexed {
|
|
base,
|
|
indexes: indexes.small_slots,
|
|
},
|
|
})
|
|
.chain(
|
|
big_slots
|
|
.iter()
|
|
.zip(static_part.range.big_slots.iter())
|
|
.map(|(dest, base)| Insn::ReadIndexed {
|
|
dest,
|
|
src: StatePartArrayIndexed {
|
|
base,
|
|
indexes: indexes.big_slots,
|
|
},
|
|
}),
|
|
),
|
|
source_location,
|
|
);
|
|
retval
|
|
};
|
|
self.compiled_exprs_to_values.insert(expr, retval);
|
|
retval
|
|
}
|
|
fn add_assignment(
|
|
&mut self,
|
|
conditions: Interned<[Cond]>,
|
|
insns: impl IntoIterator<Item = Insn>,
|
|
source_location: SourceLocation,
|
|
) {
|
|
let insns = Vec::from_iter(insns);
|
|
self.assignments
|
|
.push(Assignment::new(conditions, insns, source_location));
|
|
}
|
|
fn simple_nary_big_expr<const N: usize>(
|
|
&mut self,
|
|
instantiated_module: InstantiatedModule,
|
|
dest_ty: CanonicalType,
|
|
inputs: [Expr<CanonicalType>; N],
|
|
make_insns: impl FnOnce(
|
|
StatePartIndex<StatePartKindBigSlots>,
|
|
[StatePartIndex<StatePartKindBigSlots>; N],
|
|
) -> Vec<Insn>,
|
|
) -> CompiledValue<CanonicalType> {
|
|
let inputs = inputs.map(|input| {
|
|
let input = self.compile_expr(instantiated_module, input);
|
|
let input = self
|
|
.compiled_expr_to_value(input, instantiated_module.leaf_module().source_location());
|
|
let TypeIndexRange {
|
|
small_slots,
|
|
big_slots,
|
|
} = input.range;
|
|
assert_eq!(small_slots.len.value, 0);
|
|
assert_eq!(big_slots.len.value, 1);
|
|
big_slots.start
|
|
});
|
|
let layout = CompiledTypeLayout::get(dest_ty);
|
|
let range = self.insns.allocate_variable(&layout.layout);
|
|
let TypeIndexRange {
|
|
small_slots,
|
|
big_slots,
|
|
} = range;
|
|
assert_eq!(small_slots.len.value, 0);
|
|
assert_eq!(big_slots.len.value, 1);
|
|
let retval = CompiledValue {
|
|
layout,
|
|
range,
|
|
write: None,
|
|
};
|
|
self.add_assignment(
|
|
Interned::default(),
|
|
make_insns(big_slots.start, inputs),
|
|
instantiated_module.leaf_module().source_location(),
|
|
);
|
|
retval
|
|
}
|
|
fn compiled_value_to_dyn_array_index(
|
|
&mut self,
|
|
compiled_value: CompiledValue<UInt>,
|
|
source_location: SourceLocation,
|
|
) -> StatePartIndex<StatePartKindSmallSlots> {
|
|
if let Some(&retval) = self
|
|
.compiled_values_to_dyn_array_indexes
|
|
.get(&compiled_value)
|
|
{
|
|
return retval;
|
|
}
|
|
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(),
|
|
};
|
|
let dest = self
|
|
.insns
|
|
.allocate_variable(&TypeLayout {
|
|
small_slots: StatePartLayout::scalar(debug_data),
|
|
big_slots: StatePartLayout::empty(),
|
|
})
|
|
.small_slots
|
|
.start;
|
|
self.add_assignment(
|
|
Interned::default(),
|
|
vec![Insn::CastBigToArrayIndex {
|
|
dest,
|
|
src: compiled_value.range.big_slots.start,
|
|
}],
|
|
source_location,
|
|
);
|
|
dest
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
self.compiled_values_to_dyn_array_indexes
|
|
.insert(compiled_value, retval);
|
|
retval
|
|
}
|
|
fn compile_expr(
|
|
&mut self,
|
|
instantiated_module: InstantiatedModule,
|
|
expr: Expr<CanonicalType>,
|
|
) -> CompiledExpr<CanonicalType> {
|
|
if let Some(&retval) = self.compiled_exprs.get(&expr) {
|
|
return retval;
|
|
}
|
|
let mut cast_bit = |arg: Expr<CanonicalType>| {
|
|
let src_signed = match Expr::ty(arg) {
|
|
CanonicalType::UInt(_) => false,
|
|
CanonicalType::SInt(_) => true,
|
|
CanonicalType::Bool(_) => false,
|
|
CanonicalType::Array(_) => unreachable!(),
|
|
CanonicalType::Enum(_) => unreachable!(),
|
|
CanonicalType::Bundle(_) => unreachable!(),
|
|
CanonicalType::AsyncReset(_) => false,
|
|
CanonicalType::SyncReset(_) => false,
|
|
CanonicalType::Reset(_) => false,
|
|
CanonicalType::Clock(_) => false,
|
|
};
|
|
let dest_signed = match Expr::ty(expr) {
|
|
CanonicalType::UInt(_) => false,
|
|
CanonicalType::SInt(_) => true,
|
|
CanonicalType::Bool(_) => false,
|
|
CanonicalType::Array(_) => unreachable!(),
|
|
CanonicalType::Enum(_) => unreachable!(),
|
|
CanonicalType::Bundle(_) => unreachable!(),
|
|
CanonicalType::AsyncReset(_) => false,
|
|
CanonicalType::SyncReset(_) => false,
|
|
CanonicalType::Reset(_) => false,
|
|
CanonicalType::Clock(_) => false,
|
|
};
|
|
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
|
|
match (src_signed, dest_signed) {
|
|
(false, false) | (true, true) => {
|
|
vec![Insn::Copy { dest, src }]
|
|
}
|
|
(false, true) => vec![Insn::CastToSInt {
|
|
dest,
|
|
src,
|
|
dest_width: 1,
|
|
}],
|
|
(true, false) => vec![Insn::CastToUInt {
|
|
dest,
|
|
src,
|
|
dest_width: 1,
|
|
}],
|
|
}
|
|
})
|
|
.into()
|
|
};
|
|
let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) {
|
|
ExprEnum::UIntLiteral(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[],
|
|
|dest, []| {
|
|
vec![Insn::Const {
|
|
dest,
|
|
value: expr.to_bigint().intern_sized(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::SIntLiteral(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[],
|
|
|dest, []| {
|
|
vec![Insn::Const {
|
|
dest,
|
|
value: expr.to_bigint().intern_sized(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::BoolLiteral(expr) => self
|
|
.simple_nary_big_expr(instantiated_module, Bool.canonical(), [], |dest, []| {
|
|
vec![Insn::Const {
|
|
dest,
|
|
value: BigInt::from(expr).intern_sized(),
|
|
}]
|
|
})
|
|
.into(),
|
|
ExprEnum::BundleLiteral(expr) => todo!(),
|
|
ExprEnum::ArrayLiteral(expr) => todo!(),
|
|
ExprEnum::EnumLiteral(expr) => todo!(),
|
|
ExprEnum::Uninit(expr) => todo!(),
|
|
ExprEnum::NotU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Expr::ty(expr.arg()).canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::NotU {
|
|
dest,
|
|
src,
|
|
width: Expr::ty(expr.arg()).width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::NotS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Expr::ty(expr.arg()).canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| vec![Insn::NotS { dest, src }],
|
|
)
|
|
.into(),
|
|
ExprEnum::NotB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Expr::ty(expr.arg()).canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::NotU {
|
|
dest,
|
|
src,
|
|
width: 1,
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::Neg(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| vec![Insn::Neg { dest, src }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitAndU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitAndS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitAndB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitOrU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitOrS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitOrB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitXorU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitXorS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::BitXorB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::AddU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::AddS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::SubU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| {
|
|
vec![Insn::SubU {
|
|
dest,
|
|
lhs,
|
|
rhs,
|
|
dest_width: expr.ty().width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::SubS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::MulU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::MulS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::DivU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::DivS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::RemU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::RemS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::DynShlU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::DynShlS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::DynShrU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::DynShrS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::FixedShlU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs())],
|
|
|dest, [lhs]| {
|
|
vec![Insn::Shl {
|
|
dest,
|
|
lhs,
|
|
rhs: expr.rhs(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::FixedShlS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs())],
|
|
|dest, [lhs]| {
|
|
vec![Insn::Shl {
|
|
dest,
|
|
lhs,
|
|
rhs: expr.rhs(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::FixedShrU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs())],
|
|
|dest, [lhs]| {
|
|
vec![Insn::Shr {
|
|
dest,
|
|
lhs,
|
|
rhs: expr.rhs(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::FixedShrS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.lhs())],
|
|
|dest, [lhs]| {
|
|
vec![Insn::Shr {
|
|
dest,
|
|
lhs,
|
|
rhs: expr.rhs(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpLtB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpLeB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpGtB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
// swap both comparison direction and lhs/rhs
|
|
[Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpGeB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
// swap both comparison direction and lhs/rhs
|
|
[Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpEqB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpNeB(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpLtU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpLeU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpGtU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
// swap both comparison direction and lhs/rhs
|
|
[Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpGeU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
// swap both comparison direction and lhs/rhs
|
|
[Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpEqU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpNeU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpLtS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpLeS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpGtS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
// swap both comparison direction and lhs/rhs
|
|
[Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpGeS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
// swap both comparison direction and lhs/rhs
|
|
[Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpEqS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CmpNeS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
Bool.canonical(),
|
|
[Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())],
|
|
|dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }],
|
|
)
|
|
.into(),
|
|
ExprEnum::CastUIntToUInt(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::CastToUInt {
|
|
dest,
|
|
src,
|
|
dest_width: expr.ty().width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::CastUIntToSInt(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::CastToSInt {
|
|
dest,
|
|
src,
|
|
dest_width: expr.ty().width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::CastSIntToUInt(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::CastToUInt {
|
|
dest,
|
|
src,
|
|
dest_width: expr.ty().width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::CastSIntToSInt(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
expr.ty().canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::CastToSInt {
|
|
dest,
|
|
src,
|
|
dest_width: expr.ty().width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::CastBoolToUInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastBoolToSInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastUIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastBoolToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastUIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastBoolToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastUIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastAsyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastAsyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastAsyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastAsyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastBoolToClock(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastUIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastSIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastClockToBool(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastClockToUInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::CastClockToSInt(expr) => cast_bit(Expr::canonical(expr.arg())),
|
|
ExprEnum::FieldAccess(expr) => self
|
|
.compile_expr(instantiated_module, Expr::canonical(expr.base()))
|
|
.map_ty(Bundle::from_canonical)
|
|
.field_by_index(expr.field_index()),
|
|
ExprEnum::VariantAccess(expr) => todo!(),
|
|
ExprEnum::ArrayIndex(expr) => self
|
|
.compile_expr(instantiated_module, Expr::canonical(expr.base()))
|
|
.map_ty(Array::from_canonical)
|
|
.element(expr.element_index()),
|
|
ExprEnum::DynArrayIndex(expr) => {
|
|
let element_index =
|
|
self.compile_expr(instantiated_module, Expr::canonical(expr.element_index()));
|
|
let element_index = self.compiled_expr_to_value(
|
|
element_index,
|
|
instantiated_module.leaf_module().source_location(),
|
|
);
|
|
let index_slot = self.compiled_value_to_dyn_array_index(
|
|
element_index.map_ty(UInt::from_canonical),
|
|
instantiated_module.leaf_module().source_location(),
|
|
);
|
|
self.compile_expr(instantiated_module, Expr::canonical(expr.base()))
|
|
.map_ty(Array::from_canonical)
|
|
.element_dyn(index_slot)
|
|
}
|
|
ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
|
self.compile_expr(instantiated_module, Expr::canonical(true.to_expr()))
|
|
} else {
|
|
self.compile_expr(
|
|
instantiated_module,
|
|
Expr::canonical(
|
|
expr.arg()
|
|
.cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)),
|
|
),
|
|
)
|
|
}
|
|
.into(),
|
|
ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
|
self.compile_expr(instantiated_module, Expr::canonical(true.to_expr()))
|
|
} else {
|
|
self.compile_expr(
|
|
instantiated_module,
|
|
Expr::canonical(
|
|
expr.arg()
|
|
.cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)),
|
|
),
|
|
)
|
|
}
|
|
.into(),
|
|
ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
|
self.compile_expr(instantiated_module, Expr::canonical(false.to_expr()))
|
|
} else {
|
|
self.compile_expr(
|
|
instantiated_module,
|
|
Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))),
|
|
)
|
|
}
|
|
.into(),
|
|
ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 {
|
|
self.compile_expr(instantiated_module, Expr::canonical(false.to_expr()))
|
|
} else {
|
|
self.compile_expr(
|
|
instantiated_module,
|
|
Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))),
|
|
)
|
|
}
|
|
.into(),
|
|
ExprEnum::ReduceBitXorU(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
UInt::<1>::TYPE.canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::ReduceBitXor {
|
|
dest,
|
|
src,
|
|
input_width: Expr::ty(expr.arg()).width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::ReduceBitXorS(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
UInt::<1>::TYPE.canonical(),
|
|
[Expr::canonical(expr.arg())],
|
|
|dest, [src]| {
|
|
vec![Insn::ReduceBitXor {
|
|
dest,
|
|
src,
|
|
input_width: Expr::ty(expr.arg()).width(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::SliceUInt(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
UInt::new_dyn(expr.range().len()).canonical(),
|
|
[Expr::canonical(expr.base())],
|
|
|dest, [src]| {
|
|
vec![Insn::SliceInt {
|
|
dest,
|
|
src,
|
|
start: expr.range().start,
|
|
len: expr.range().len(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::SliceSInt(expr) => self
|
|
.simple_nary_big_expr(
|
|
instantiated_module,
|
|
UInt::new_dyn(expr.range().len()).canonical(),
|
|
[Expr::canonical(expr.base())],
|
|
|dest, [src]| {
|
|
vec![Insn::SliceInt {
|
|
dest,
|
|
src,
|
|
start: expr.range().start,
|
|
len: expr.range().len(),
|
|
}]
|
|
},
|
|
)
|
|
.into(),
|
|
ExprEnum::CastToBits(expr) => todo!(),
|
|
ExprEnum::CastBitsTo(expr) => todo!(),
|
|
ExprEnum::ModuleIO(expr) => self
|
|
.compile_value(TargetInInstantiatedModule {
|
|
instantiated_module,
|
|
target: expr.into(),
|
|
})
|
|
.into(),
|
|
ExprEnum::Instance(expr) => self
|
|
.compile_value(TargetInInstantiatedModule {
|
|
instantiated_module,
|
|
target: expr.into(),
|
|
})
|
|
.into(),
|
|
ExprEnum::Wire(expr) => self
|
|
.compile_value(TargetInInstantiatedModule {
|
|
instantiated_module,
|
|
target: expr.into(),
|
|
})
|
|
.into(),
|
|
ExprEnum::Reg(expr) => self
|
|
.compile_value(TargetInInstantiatedModule {
|
|
instantiated_module,
|
|
target: expr.into(),
|
|
})
|
|
.into(),
|
|
ExprEnum::MemPort(expr) => self
|
|
.compile_value(TargetInInstantiatedModule {
|
|
instantiated_module,
|
|
target: expr.into(),
|
|
})
|
|
.into(),
|
|
};
|
|
self.compiled_exprs.insert(expr, retval);
|
|
retval
|
|
}
|
|
fn compile_simple_connect(
|
|
&mut self,
|
|
conditions: Interned<[Cond]>,
|
|
lhs: CompiledExpr<CanonicalType>,
|
|
rhs: CompiledValue<CanonicalType>,
|
|
source_location: SourceLocation,
|
|
) {
|
|
let CompiledExpr {
|
|
static_part: lhs_static_part,
|
|
indexes,
|
|
} = lhs;
|
|
let (lhs_layout, lhs_range) = lhs_static_part.write();
|
|
assert!(
|
|
lhs_layout.ty.is_passive(),
|
|
"invalid expression passed to compile_simple_connect -- type must be passive",
|
|
);
|
|
let TypeIndexRange {
|
|
small_slots,
|
|
big_slots,
|
|
} = lhs_range;
|
|
self.add_assignment(
|
|
conditions,
|
|
small_slots
|
|
.iter()
|
|
.zip(rhs.range.small_slots.iter())
|
|
.map(|(base, src)| {
|
|
if indexes.small_slots.is_empty() {
|
|
Insn::CopySmall { dest: base, src }
|
|
} else {
|
|
Insn::WriteSmallIndexed {
|
|
dest: StatePartArrayIndexed {
|
|
base,
|
|
indexes: indexes.small_slots,
|
|
},
|
|
src,
|
|
}
|
|
}
|
|
})
|
|
.chain(
|
|
big_slots
|
|
.iter()
|
|
.zip(rhs.range.big_slots.iter())
|
|
.map(|(base, src)| {
|
|
if indexes.big_slots.is_empty() {
|
|
Insn::Copy { dest: base, src }
|
|
} else {
|
|
Insn::WriteIndexed {
|
|
dest: StatePartArrayIndexed {
|
|
base,
|
|
indexes: indexes.big_slots,
|
|
},
|
|
src,
|
|
}
|
|
}
|
|
}),
|
|
),
|
|
source_location,
|
|
);
|
|
}
|
|
fn compile_connect(
|
|
&mut self,
|
|
lhs_instantiated_module: InstantiatedModule,
|
|
lhs_conditions: Interned<[Cond]>,
|
|
lhs: Expr<CanonicalType>,
|
|
rhs_instantiated_module: InstantiatedModule,
|
|
rhs_conditions: Interned<[Cond]>,
|
|
mut rhs: Expr<CanonicalType>,
|
|
source_location: SourceLocation,
|
|
) {
|
|
if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() {
|
|
match Expr::ty(lhs) {
|
|
CanonicalType::UInt(lhs_ty) => {
|
|
rhs = Expr::canonical(Expr::<UInt>::from_canonical(rhs).cast_to(lhs_ty));
|
|
}
|
|
CanonicalType::SInt(lhs_ty) => {
|
|
rhs = Expr::canonical(Expr::<SInt>::from_canonical(rhs).cast_to(lhs_ty));
|
|
}
|
|
CanonicalType::Bool(_) => unreachable!(),
|
|
CanonicalType::Array(lhs_ty) => {
|
|
let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else {
|
|
unreachable!();
|
|
};
|
|
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
|
let lhs = Expr::<Array>::from_canonical(lhs);
|
|
let rhs = Expr::<Array>::from_canonical(rhs);
|
|
for index in 0..lhs_ty.len() {
|
|
self.compile_connect(
|
|
lhs_instantiated_module,
|
|
lhs_conditions,
|
|
lhs[index],
|
|
rhs_instantiated_module,
|
|
rhs_conditions,
|
|
rhs[index],
|
|
source_location,
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
CanonicalType::Enum(lhs_ty) => {
|
|
let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else {
|
|
unreachable!();
|
|
};
|
|
todo!("handle connect with different enum types");
|
|
}
|
|
CanonicalType::Bundle(lhs_ty) => {
|
|
let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else {
|
|
unreachable!();
|
|
};
|
|
assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len());
|
|
let lhs = Expr::<Bundle>::from_canonical(lhs);
|
|
let rhs = Expr::<Bundle>::from_canonical(rhs);
|
|
for (
|
|
field_index,
|
|
(
|
|
BundleField {
|
|
name,
|
|
flipped,
|
|
ty: _,
|
|
},
|
|
rhs_field,
|
|
),
|
|
) in lhs_ty.fields().into_iter().zip(rhs_ty.fields()).enumerate()
|
|
{
|
|
assert_eq!(name, rhs_field.name);
|
|
assert_eq!(flipped, rhs_field.flipped);
|
|
let lhs_expr = ops::FieldAccess::new_by_index(lhs, field_index).to_expr();
|
|
let rhs_expr = ops::FieldAccess::new_by_index(rhs, field_index).to_expr();
|
|
if flipped {
|
|
// swap lhs/rhs
|
|
self.compile_connect(
|
|
rhs_instantiated_module,
|
|
rhs_conditions,
|
|
rhs_expr,
|
|
lhs_instantiated_module,
|
|
lhs_conditions,
|
|
lhs_expr,
|
|
source_location,
|
|
);
|
|
} else {
|
|
self.compile_connect(
|
|
lhs_instantiated_module,
|
|
lhs_conditions,
|
|
lhs_expr,
|
|
rhs_instantiated_module,
|
|
rhs_conditions,
|
|
rhs_expr,
|
|
source_location,
|
|
);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
CanonicalType::AsyncReset(_) => unreachable!(),
|
|
CanonicalType::SyncReset(_) => unreachable!(),
|
|
CanonicalType::Reset(_) => unreachable!(),
|
|
CanonicalType::Clock(_) => unreachable!(),
|
|
}
|
|
}
|
|
let Some(target) = lhs.target() else {
|
|
unreachable!("connect lhs must have target");
|
|
};
|
|
let lhs_decl_conditions = self.decl_conditions[&TargetInInstantiatedModule {
|
|
instantiated_module: lhs_instantiated_module,
|
|
target: target.base().into(),
|
|
}];
|
|
let lhs = self.compile_expr(lhs_instantiated_module, lhs);
|
|
let rhs = self.compile_expr(rhs_instantiated_module, rhs);
|
|
let rhs = self.compiled_expr_to_value(rhs, source_location);
|
|
self.compile_simple_connect(
|
|
lhs_conditions[lhs_decl_conditions.len()..].intern(),
|
|
lhs,
|
|
rhs,
|
|
source_location,
|
|
);
|
|
}
|
|
fn compile_declaration(
|
|
&mut self,
|
|
declaration: StmtDeclaration,
|
|
parent_module: Interned<InstantiatedModule>,
|
|
conditions: Interned<[Cond]>,
|
|
) -> TraceDecl {
|
|
let target_base: TargetBase = match &declaration {
|
|
StmtDeclaration::Wire(v) => v.wire.into(),
|
|
StmtDeclaration::Reg(v) => v.reg.into(),
|
|
StmtDeclaration::Instance(v) => v.instance.into(),
|
|
};
|
|
let target = TargetInInstantiatedModule {
|
|
instantiated_module: *parent_module,
|
|
target: target_base.into(),
|
|
};
|
|
self.decl_conditions.insert(target, conditions);
|
|
self.compile_value(target);
|
|
match declaration {
|
|
StmtDeclaration::Wire(StmtWire { annotations, wire }) => {}
|
|
StmtDeclaration::Reg(StmtReg { annotations, reg }) => {
|
|
todo!();
|
|
}
|
|
StmtDeclaration::Instance(StmtInstance {
|
|
annotations,
|
|
instance,
|
|
}) => {
|
|
let inner_instantiated_module = InstantiatedModule::Child {
|
|
parent: parent_module,
|
|
instance: instance.intern_sized(),
|
|
}
|
|
.intern_sized();
|
|
let instance_expr = instance.to_expr();
|
|
self.compile_module(inner_instantiated_module);
|
|
for (field_index, module_io) in
|
|
instance.instantiated().module_io().into_iter().enumerate()
|
|
{
|
|
let instance_field =
|
|
ops::FieldAccess::new_by_index(instance_expr, field_index).to_expr();
|
|
match Expr::flow(instance_field) {
|
|
Flow::Source => {
|
|
// we need to supply the value to the instance since the
|
|
// parent module expects to read from the instance
|
|
self.compile_connect(
|
|
*parent_module,
|
|
conditions,
|
|
instance_field,
|
|
*inner_instantiated_module,
|
|
Interned::default(),
|
|
module_io.module_io.to_expr(),
|
|
instance.source_location(),
|
|
);
|
|
}
|
|
Flow::Sink => {
|
|
// we need to take the value from the instance since the
|
|
// parent module expects to write to the instance
|
|
self.compile_connect(
|
|
*inner_instantiated_module,
|
|
Interned::default(),
|
|
module_io.module_io.to_expr(),
|
|
*parent_module,
|
|
conditions,
|
|
instance_field,
|
|
instance.source_location(),
|
|
);
|
|
}
|
|
Flow::Duplex => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.make_trace_decl(*parent_module, target_base)
|
|
}
|
|
fn compile_block(
|
|
&mut self,
|
|
parent_module: Interned<InstantiatedModule>,
|
|
block: Block,
|
|
conditions: Interned<[Cond]>,
|
|
trace_decls: &mut Vec<TraceDecl>,
|
|
) {
|
|
let Block { memories, stmts } = block;
|
|
for memory in memories {
|
|
todo!("implement memory");
|
|
}
|
|
for stmt in stmts {
|
|
match stmt {
|
|
Stmt::Connect(StmtConnect {
|
|
lhs,
|
|
rhs,
|
|
source_location,
|
|
}) => self.compile_connect(
|
|
*parent_module,
|
|
conditions,
|
|
lhs,
|
|
*parent_module,
|
|
conditions,
|
|
rhs,
|
|
source_location,
|
|
),
|
|
Stmt::Formal(StmtFormal { .. }) => todo!("implement simulating formal statements"),
|
|
Stmt::If(StmtIf {
|
|
cond,
|
|
source_location,
|
|
blocks: [then_block, else_block],
|
|
}) => {
|
|
let cond = self.compile_expr(*parent_module, Expr::canonical(cond));
|
|
let cond = self.compiled_expr_to_value(cond, source_location);
|
|
let cond = cond.map_ty(Bool::from_canonical);
|
|
self.compile_block(
|
|
parent_module,
|
|
then_block,
|
|
Interned::from_iter(conditions.iter().copied().chain([Cond {
|
|
body: CondBody::IfTrue { cond },
|
|
source_location,
|
|
}])),
|
|
trace_decls,
|
|
);
|
|
self.compile_block(
|
|
parent_module,
|
|
else_block,
|
|
Interned::from_iter(conditions.iter().copied().chain([Cond {
|
|
body: CondBody::IfFalse { cond },
|
|
source_location,
|
|
}])),
|
|
trace_decls,
|
|
);
|
|
}
|
|
Stmt::Match(StmtMatch {
|
|
expr,
|
|
source_location,
|
|
blocks,
|
|
}) => {
|
|
let enum_expr = self.compile_expr(*parent_module, Expr::canonical(expr));
|
|
let enum_expr = self.compiled_expr_to_value(enum_expr, source_location);
|
|
let enum_expr = enum_expr.map_ty(Enum::from_canonical);
|
|
for (variant_index, block) in blocks.into_iter().enumerate() {
|
|
self.compile_block(
|
|
parent_module,
|
|
block,
|
|
Interned::from_iter(conditions.iter().copied().chain([Cond {
|
|
body: CondBody::MatchArm {
|
|
enum_expr,
|
|
variant_index,
|
|
},
|
|
source_location,
|
|
}])),
|
|
trace_decls,
|
|
);
|
|
}
|
|
}
|
|
Stmt::Declaration(declaration) => {
|
|
trace_decls.push(self.compile_declaration(
|
|
declaration,
|
|
parent_module,
|
|
conditions,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fn compile_module(&mut self, module: Interned<InstantiatedModule>) -> &CompiledModule {
|
|
let mut trace_decls = Vec::new();
|
|
let module_io = module
|
|
.leaf_module()
|
|
.module_io()
|
|
.iter()
|
|
.map(
|
|
|&AnnotatedModuleIO {
|
|
annotations: _,
|
|
module_io,
|
|
}| {
|
|
let target = TargetInInstantiatedModule {
|
|
instantiated_module: *module,
|
|
target: Target::from(module_io),
|
|
};
|
|
self.decl_conditions.insert(target, Interned::default());
|
|
trace_decls.push(self.make_trace_decl(*module, module_io.into()));
|
|
self.compile_value(target)
|
|
},
|
|
)
|
|
.collect();
|
|
match module.leaf_module().body() {
|
|
ModuleBody::Normal(NormalModuleBody { body }) => {
|
|
self.compile_block(module, body, Interned::default(), &mut trace_decls);
|
|
}
|
|
ModuleBody::Extern(_extern_module_body) => {
|
|
todo!("simulating extern module: {:?}", module);
|
|
}
|
|
}
|
|
let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else {
|
|
unreachable!("compiled same instantiated module twice");
|
|
};
|
|
entry.insert(CompiledModule {
|
|
module_io,
|
|
trace_decls: TraceModule {
|
|
name: module.leaf_module().name(),
|
|
children: Intern::intern_owned(trace_decls),
|
|
},
|
|
})
|
|
}
|
|
fn process_assignments(&mut self) {
|
|
self.assignments
|
|
.finalize(self.insns.state_layout().len().ty);
|
|
let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) {
|
|
Ok(nodes) => nodes
|
|
.into_iter()
|
|
.filter_map(|n| match n {
|
|
AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v),
|
|
_ => None,
|
|
})
|
|
.collect(),
|
|
Err(e) => match e.node_id() {
|
|
AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!(
|
|
"combinatorial logic cycle detected at: {}",
|
|
self.assignments.assignments()[assignment_index].source_location,
|
|
),
|
|
AssignmentOrSlotIndex::SmallSlots(slot) => panic!(
|
|
"combinatorial logic cycle detected through: {}",
|
|
self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name,
|
|
),
|
|
AssignmentOrSlotIndex::BigSlots(slot) => panic!(
|
|
"combinatorial logic cycle detected through: {}",
|
|
self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name,
|
|
),
|
|
},
|
|
};
|
|
struct CondStackEntry<'a> {
|
|
cond: &'a Cond,
|
|
end_label_index: usize,
|
|
}
|
|
let mut cond_stack = Vec::<CondStackEntry<'_>>::new();
|
|
for assignment_index in assignments_order {
|
|
let Assignment {
|
|
inputs: _,
|
|
outputs: _,
|
|
conditions,
|
|
insns,
|
|
source_location,
|
|
} = &self.assignments.assignments()[assignment_index];
|
|
let mut same_len = 0;
|
|
for (index, (entry, cond)) in cond_stack.iter().zip(conditions).enumerate() {
|
|
if entry.cond != cond {
|
|
break;
|
|
}
|
|
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);
|
|
}
|
|
for cond in &conditions[cond_stack.len()..] {
|
|
let end_label_index = 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,
|
|
value: cond_value.range.small_slots.start,
|
|
},
|
|
Insn::BranchIfSmallNonZero {
|
|
target: end_label_index,
|
|
value: cond_value.range.small_slots.start,
|
|
},
|
|
),
|
|
TypeLen::A_BIG_SLOT => (
|
|
Insn::BranchIfZero {
|
|
target: end_label_index,
|
|
value: cond_value.range.big_slots.start,
|
|
},
|
|
Insn::BranchIfNonZero {
|
|
target: end_label_index,
|
|
value: cond_value.range.big_slots.start,
|
|
},
|
|
),
|
|
_ => unreachable!(),
|
|
};
|
|
self.insns.push(
|
|
if let CondBody::IfTrue { .. } = cond.body {
|
|
branch_if_zero
|
|
} else {
|
|
branch_if_non_zero
|
|
},
|
|
cond.source_location,
|
|
);
|
|
}
|
|
CondBody::MatchArm {
|
|
enum_expr,
|
|
variant_index,
|
|
} => todo!(),
|
|
}
|
|
cond_stack.push(CondStackEntry {
|
|
cond,
|
|
end_label_index,
|
|
});
|
|
}
|
|
for insn in insns {
|
|
self.insns.push(*insn, *source_location);
|
|
}
|
|
}
|
|
}
|
|
pub fn compile(mut self) -> Compiled<Bundle> {
|
|
let base_module =
|
|
*self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized());
|
|
self.process_assignments();
|
|
self.insns
|
|
.push(Insn::Return, self.base_module.source_location());
|
|
Compiled {
|
|
insns: Insns::from(self.insns).intern_sized(),
|
|
base_module,
|
|
io: Instance::new_unchecked(
|
|
ScopedNameId(
|
|
NameId("<simulator>".intern(), Id::new()),
|
|
self.base_module.name_id(),
|
|
),
|
|
self.base_module,
|
|
self.base_module.source_location(),
|
|
),
|
|
traces: Intern::intern_owned(self.traces),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
struct CompiledModule {
|
|
module_io: Interned<[CompiledValue<CanonicalType>]>,
|
|
trace_decls: TraceModule,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub struct Compiled<T: BundleType> {
|
|
insns: Interned<Insns<InsnsBuildingDone>>,
|
|
base_module: CompiledModule,
|
|
io: Instance<T>,
|
|
traces: Interned<[SimTrace<()>]>,
|
|
}
|
|
|
|
impl<T: BundleType> Compiled<T> {
|
|
pub fn new(module: Interned<Module<T>>) -> Self {
|
|
Self::from_canonical(Compiler::new(module.canonical().intern()).compile())
|
|
}
|
|
pub fn canonical(self) -> Compiled<Bundle> {
|
|
let Self {
|
|
insns,
|
|
base_module,
|
|
io,
|
|
traces,
|
|
} = self;
|
|
Compiled {
|
|
insns,
|
|
base_module,
|
|
io: Instance::from_canonical(io.canonical()),
|
|
traces,
|
|
}
|
|
}
|
|
pub fn from_canonical(canonical: Compiled<Bundle>) -> Self {
|
|
let Compiled {
|
|
insns,
|
|
base_module,
|
|
io,
|
|
traces,
|
|
} = canonical;
|
|
Self {
|
|
insns,
|
|
base_module,
|
|
io: Instance::from_canonical(io.canonical()),
|
|
traces,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct TraceScalarId(usize);
|
|
|
|
impl fmt::Debug for TraceScalarId {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "TraceScalarId({})", self.0)
|
|
}
|
|
}
|
|
|
|
impl TraceScalarId {
|
|
pub fn as_usize(self) -> usize {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_trace_decl {
|
|
(
|
|
$(
|
|
#[kind = $category_kind:ident]
|
|
$(#[$category_meta:meta])*
|
|
$category_variant:ident($category_enum:ident {
|
|
fn $category_property_fn:ident(self) -> $category_property_fn_ret_ty:ty;
|
|
$(
|
|
$(#[$meta:meta])*
|
|
$variant:ident($struct:ident {
|
|
fn $property_fn:ident($property_fn_self:ident) -> _ $property_fn_block:block
|
|
$($(#[$field_meta:meta])*
|
|
$field_name:ident: $field_ty:ty,)*
|
|
}),
|
|
)*
|
|
}),
|
|
)*
|
|
) => {
|
|
$(
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
#[non_exhaustive]
|
|
$(#[$category_meta])*
|
|
pub enum $category_kind {
|
|
$($(#[$meta])*
|
|
$variant,)*
|
|
}
|
|
|
|
impl From<$category_kind> for TraceKind {
|
|
fn from(v: $category_kind) -> Self {
|
|
TraceKind::$category_variant(v)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
#[non_exhaustive]
|
|
$(#[$category_meta])*
|
|
pub enum $category_enum {
|
|
$($(#[$meta])*
|
|
$variant($struct),)*
|
|
}
|
|
|
|
impl fmt::Debug for $category_enum {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match *self {
|
|
$(Self::$variant(v) => v.fmt(f),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl $category_enum {
|
|
pub fn kind(self) -> $category_kind {
|
|
match self {
|
|
$(Self::$variant(_) => $category_kind::$variant,)*
|
|
}
|
|
}
|
|
pub fn name(self) -> Interned<str> {
|
|
match self {
|
|
$(Self::$variant(v) => v.name,)*
|
|
}
|
|
}
|
|
pub fn $category_property_fn(self) -> $category_property_fn_ret_ty {
|
|
match self {
|
|
$(Self::$variant(v) => v.$property_fn(),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<$category_enum> for TraceDecl {
|
|
fn from(v: $category_enum) -> Self {
|
|
TraceDecl::$category_variant(v)
|
|
}
|
|
}
|
|
|
|
$(
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
#[non_exhaustive]
|
|
$(#[$meta])*
|
|
pub struct $struct {
|
|
$($(#[$field_meta])*
|
|
pub $field_name: $field_ty,)*
|
|
}
|
|
|
|
impl $struct {
|
|
pub fn $property_fn($property_fn_self) -> $category_property_fn_ret_ty $property_fn_block
|
|
}
|
|
|
|
impl From<$struct> for $category_enum {
|
|
fn from(v: $struct) -> Self {
|
|
$category_enum::$variant(v)
|
|
}
|
|
}
|
|
|
|
impl From<$struct> for TraceDecl {
|
|
fn from(v: $struct) -> Self {
|
|
TraceDecl::$category_variant($category_enum::$variant(v))
|
|
}
|
|
}
|
|
)*
|
|
)*
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub enum TraceKind {
|
|
$($(#[$category_meta])*
|
|
$category_variant($category_kind),)*
|
|
}
|
|
|
|
impl fmt::Debug for TraceKind {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match *self {
|
|
$(Self::$category_variant(v) => v.fmt(f),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum TraceDecl {
|
|
$($(#[$category_meta])*
|
|
$category_variant($category_enum),)*
|
|
}
|
|
|
|
impl fmt::Debug for TraceDecl {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match *self {
|
|
$(Self::$category_variant(v) => v.fmt(f),)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_trace_decl! {
|
|
#[kind = TraceScopeKind]
|
|
Scope(TraceScope {
|
|
fn children(self) -> Interned<[TraceDecl]>;
|
|
Module(TraceModule {
|
|
fn children(self) -> _ {
|
|
self.children
|
|
}
|
|
name: Interned<str>,
|
|
children: Interned<[TraceDecl]>,
|
|
}),
|
|
Instance(TraceInstance {
|
|
fn children(self) -> _ {
|
|
[self.instance_io.into(), self.module.into()][..].intern()
|
|
}
|
|
name: Interned<str>,
|
|
instance_io: TraceBundle,
|
|
module: TraceModule,
|
|
ty: Bundle,
|
|
}),
|
|
MemPort(TraceMemPort {
|
|
fn children(self) -> _ {
|
|
[self.bundle.into()][..].intern()
|
|
}
|
|
name: Interned<str>,
|
|
bundle: TraceBundle,
|
|
ty: Bundle,
|
|
}),
|
|
Wire(TraceWire {
|
|
fn children(self) -> _ {
|
|
[*self.child][..].intern()
|
|
}
|
|
name: Interned<str>,
|
|
child: Interned<TraceDecl>,
|
|
ty: CanonicalType,
|
|
}),
|
|
Reg(TraceReg {
|
|
fn children(self) -> _ {
|
|
[*self.child][..].intern()
|
|
}
|
|
name: Interned<str>,
|
|
child: Interned<TraceDecl>,
|
|
ty: CanonicalType,
|
|
}),
|
|
ModuleIO(TraceModuleIO {
|
|
fn children(self) -> _ {
|
|
[*self.child][..].intern()
|
|
}
|
|
name: Interned<str>,
|
|
child: Interned<TraceDecl>,
|
|
ty: CanonicalType,
|
|
flow: Flow,
|
|
}),
|
|
Bundle(TraceBundle {
|
|
fn children(self) -> _ {
|
|
self.fields
|
|
}
|
|
name: Interned<str>,
|
|
fields: Interned<[TraceDecl]>,
|
|
ty: Bundle,
|
|
flow: Flow,
|
|
}),
|
|
Array(TraceArray {
|
|
fn children(self) -> _ {
|
|
self.elements
|
|
}
|
|
name: Interned<str>,
|
|
elements: Interned<[TraceDecl]>,
|
|
ty: Array,
|
|
flow: Flow,
|
|
}),
|
|
EnumWithFields(TraceEnumWithFields {
|
|
fn children(self) -> _ {
|
|
Interned::from_iter([self.discriminant.into()].into_iter().chain(self.non_empty_fields))
|
|
}
|
|
name: Interned<str>,
|
|
discriminant: TraceEnumDiscriminant,
|
|
non_empty_fields: Interned<[TraceDecl]>,
|
|
ty: Enum,
|
|
flow: Flow,
|
|
}),
|
|
}),
|
|
#[kind = TraceScalarKind]
|
|
Scalar(TraceScalar {
|
|
fn id(self) -> TraceScalarId;
|
|
UInt(TraceUInt {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
ty: UInt,
|
|
flow: Flow,
|
|
}),
|
|
SInt(TraceSInt {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
ty: SInt,
|
|
flow: Flow,
|
|
}),
|
|
Bool(TraceBool {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
flow: Flow,
|
|
}),
|
|
FieldlessEnum(TraceFieldlessEnum {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
ty: Enum,
|
|
flow: Flow,
|
|
}),
|
|
EnumDiscriminant(TraceEnumDiscriminant {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
ty: Enum,
|
|
flow: Flow,
|
|
}),
|
|
Clock(TraceClock {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
flow: Flow,
|
|
}),
|
|
SyncReset(TraceSyncReset {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
flow: Flow,
|
|
}),
|
|
AsyncReset(TraceAsyncReset {
|
|
fn id(self) -> _ {
|
|
self.id
|
|
}
|
|
id: TraceScalarId,
|
|
name: Interned<str>,
|
|
flow: Flow,
|
|
}),
|
|
}),
|
|
}
|
|
|
|
pub trait TraceWriterDecls: fmt::Debug + 'static + Sized {
|
|
type Error: std::error::Error + Send + Sync + 'static;
|
|
type TraceWriter: TraceWriter<Error = Self::Error>;
|
|
fn write_decls(self, module: TraceModule) -> Result<Self::TraceWriter, Self::Error>;
|
|
}
|
|
|
|
trait TraceWriterDeclsDynTrait: fmt::Debug {
|
|
fn write_decls_dyn(self: Box<Self>, module: TraceModule) -> std::io::Result<DynTraceWriter>;
|
|
}
|
|
|
|
fn err_into_io<E: std::error::Error + Send + Sync + 'static>(e: E) -> std::io::Error {
|
|
match <dyn std::error::Error + Send + Sync>::downcast::<std::io::Error>(Box::new(e)) {
|
|
Ok(retval) => *retval,
|
|
Err(e) => std::io::Error::other(e),
|
|
}
|
|
}
|
|
|
|
impl<T: TraceWriterDecls> TraceWriterDeclsDynTrait for T {
|
|
fn write_decls_dyn(self: Box<Self>, module: TraceModule) -> std::io::Result<DynTraceWriter> {
|
|
Ok(DynTraceWriter(Box::new(
|
|
TraceWriterDecls::write_decls(*self, module).map_err(err_into_io)?,
|
|
)))
|
|
}
|
|
}
|
|
|
|
pub trait TraceWriter: fmt::Debug + 'static {
|
|
type Error: std::error::Error + Send + Sync + 'static;
|
|
fn finish_init(&mut self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> {
|
|
let _ = instant;
|
|
Ok(())
|
|
}
|
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
|
Ok(())
|
|
}
|
|
fn close(self) -> Result<(), Self::Error>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
Ok(())
|
|
}
|
|
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>;
|
|
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>;
|
|
fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
|
if value {
|
|
self.set_signal_uint(id, bits![1])
|
|
} else {
|
|
self.set_signal_uint(id, bits![0])
|
|
}
|
|
}
|
|
fn set_signal_clock(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
|
self.set_signal_bool(id, value)
|
|
}
|
|
fn set_signal_sync_reset(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
|
self.set_signal_bool(id, value)
|
|
}
|
|
fn set_signal_async_reset(
|
|
&mut self,
|
|
id: TraceScalarId,
|
|
value: bool,
|
|
) -> Result<(), Self::Error> {
|
|
self.set_signal_bool(id, value)
|
|
}
|
|
}
|
|
|
|
pub struct DynTraceWriterDecls(Box<dyn TraceWriterDeclsDynTrait>);
|
|
|
|
impl DynTraceWriterDecls {
|
|
pub fn new<W: TraceWriterDecls>(writer: W) -> Self {
|
|
Self(Box::new(writer))
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for DynTraceWriterDecls {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl TraceWriterDecls for DynTraceWriterDecls {
|
|
type Error = std::io::Error;
|
|
type TraceWriter = DynTraceWriter;
|
|
fn write_decls(self, module: TraceModule) -> Result<Self::TraceWriter, Self::Error> {
|
|
self.0.write_decls_dyn(module)
|
|
}
|
|
}
|
|
|
|
trait TraceWriterDynTrait: fmt::Debug + 'static {
|
|
fn finish_init_dyn(&mut self) -> std::io::Result<()>;
|
|
fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()>;
|
|
fn flush_dyn(&mut self) -> std::io::Result<()>;
|
|
fn close_dyn(self: Box<Self>) -> std::io::Result<()>;
|
|
fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>;
|
|
fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>;
|
|
fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
|
fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
|
fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
|
fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool)
|
|
-> std::io::Result<()>;
|
|
}
|
|
|
|
impl<T: TraceWriter> TraceWriterDynTrait for T {
|
|
fn finish_init_dyn(&mut self) -> std::io::Result<()> {
|
|
Ok(TraceWriter::finish_init(self).map_err(err_into_io)?)
|
|
}
|
|
fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()> {
|
|
Ok(TraceWriter::change_time_to(self, instant).map_err(err_into_io)?)
|
|
}
|
|
fn flush_dyn(&mut self) -> std::io::Result<()> {
|
|
Ok(TraceWriter::flush(self).map_err(err_into_io)?)
|
|
}
|
|
fn close_dyn(self: Box<Self>) -> std::io::Result<()> {
|
|
Ok(TraceWriter::close(*self).map_err(err_into_io)?)
|
|
}
|
|
fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> {
|
|
Ok(TraceWriter::set_signal_uint(self, id, value).map_err(err_into_io)?)
|
|
}
|
|
fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> {
|
|
Ok(TraceWriter::set_signal_sint(self, id, value).map_err(err_into_io)?)
|
|
}
|
|
fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> {
|
|
Ok(TraceWriter::set_signal_bool(self, id, value).map_err(err_into_io)?)
|
|
}
|
|
fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> {
|
|
Ok(TraceWriter::set_signal_clock(self, id, value).map_err(err_into_io)?)
|
|
}
|
|
fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> {
|
|
Ok(TraceWriter::set_signal_sync_reset(self, id, value).map_err(err_into_io)?)
|
|
}
|
|
fn set_signal_async_reset_dyn(
|
|
&mut self,
|
|
id: TraceScalarId,
|
|
value: bool,
|
|
) -> std::io::Result<()> {
|
|
Ok(TraceWriter::set_signal_async_reset(self, id, value).map_err(err_into_io)?)
|
|
}
|
|
}
|
|
|
|
pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
|
|
|
|
impl fmt::Debug for DynTraceWriter {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl TraceWriter for DynTraceWriter {
|
|
type Error = std::io::Error;
|
|
fn finish_init(&mut self) -> Result<(), Self::Error> {
|
|
self.0.finish_init_dyn()
|
|
}
|
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
|
self.0.flush_dyn()
|
|
}
|
|
fn close(self) -> Result<(), Self::Error> {
|
|
self.0.close_dyn()
|
|
}
|
|
fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> {
|
|
self.0.change_time_to_dyn(instant)
|
|
}
|
|
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
|
self.0.set_signal_uint_dyn(id, value)
|
|
}
|
|
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
|
self.0.set_signal_sint_dyn(id, value)
|
|
}
|
|
fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
|
self.0.set_signal_bool_dyn(id, value)
|
|
}
|
|
fn set_signal_clock(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
|
self.0.set_signal_clock_dyn(id, value)
|
|
}
|
|
fn set_signal_sync_reset(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
|
self.0.set_signal_sync_reset_dyn(id, value)
|
|
}
|
|
fn set_signal_async_reset(
|
|
&mut self,
|
|
id: TraceScalarId,
|
|
value: bool,
|
|
) -> Result<(), Self::Error> {
|
|
self.0.set_signal_async_reset_dyn(id, value)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum TraceWriterState<T: TraceWriterDecls> {
|
|
Decls(T),
|
|
Init(T::TraceWriter),
|
|
Running(T::TraceWriter),
|
|
Errored(Option<T::Error>),
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
struct SimTrace<S> {
|
|
id: TraceScalarId,
|
|
kind: SimTraceKind,
|
|
state: S,
|
|
last_state: S,
|
|
}
|
|
|
|
impl fmt::Debug for SimTrace<()> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
id,
|
|
kind,
|
|
state,
|
|
last_state,
|
|
} = self;
|
|
f.debug_struct("SimTrace")
|
|
.field("id", id)
|
|
.field("kind", kind)
|
|
.field("state", state)
|
|
.field("last_state", last_state)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for SimTrace<BitVec> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
id,
|
|
kind,
|
|
state,
|
|
last_state,
|
|
} = self;
|
|
f.debug_struct("SimTrace")
|
|
.field("id", id)
|
|
.field("kind", kind)
|
|
.field("state", &BitSliceWriteWithBase(state))
|
|
.field("last_state", &BitSliceWriteWithBase(last_state))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
enum SimTraceKind {
|
|
BigUInt {
|
|
index: StatePartIndex<StatePartKindBigSlots>,
|
|
ty: UInt,
|
|
},
|
|
BigSInt {
|
|
index: StatePartIndex<StatePartKindBigSlots>,
|
|
ty: SInt,
|
|
},
|
|
BigBool {
|
|
index: StatePartIndex<StatePartKindBigSlots>,
|
|
},
|
|
BigAsyncReset {
|
|
index: StatePartIndex<StatePartKindBigSlots>,
|
|
},
|
|
BigSyncReset {
|
|
index: StatePartIndex<StatePartKindBigSlots>,
|
|
},
|
|
BigClock {
|
|
index: StatePartIndex<StatePartKindBigSlots>,
|
|
},
|
|
SmallUInt {
|
|
index: StatePartIndex<StatePartKindSmallSlots>,
|
|
ty: UInt,
|
|
},
|
|
SmallSInt {
|
|
index: StatePartIndex<StatePartKindSmallSlots>,
|
|
ty: SInt,
|
|
},
|
|
SmallBool {
|
|
index: StatePartIndex<StatePartKindSmallSlots>,
|
|
},
|
|
SmallAsyncReset {
|
|
index: StatePartIndex<StatePartKindSmallSlots>,
|
|
},
|
|
SmallSyncReset {
|
|
index: StatePartIndex<StatePartKindSmallSlots>,
|
|
},
|
|
SmallClock {
|
|
index: StatePartIndex<StatePartKindSmallSlots>,
|
|
},
|
|
}
|
|
|
|
impl SimTraceKind {
|
|
fn make_state(self) -> BitVec {
|
|
match self {
|
|
SimTraceKind::BigUInt { index: _, ty } | SimTraceKind::SmallUInt { index: _, ty } => {
|
|
BitVec::repeat(false, ty.width)
|
|
}
|
|
SimTraceKind::BigSInt { index: _, ty } | SimTraceKind::SmallSInt { index: _, ty } => {
|
|
BitVec::repeat(false, ty.width)
|
|
}
|
|
SimTraceKind::BigBool { index: _ }
|
|
| SimTraceKind::BigAsyncReset { index: _ }
|
|
| SimTraceKind::BigSyncReset { index: _ }
|
|
| SimTraceKind::BigClock { index: _ }
|
|
| SimTraceKind::SmallBool { index: _ }
|
|
| SimTraceKind::SmallAsyncReset { index: _ }
|
|
| SimTraceKind::SmallSyncReset { index: _ }
|
|
| SimTraceKind::SmallClock { index: _ } => BitVec::repeat(false, 1),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SimulationImpl {
|
|
state: interpreter::State,
|
|
io: Expr<Bundle>,
|
|
uninitialized_inputs: HashSet<Target>,
|
|
io_targets: HashMap<Target, CompiledValue<CanonicalType>>,
|
|
made_initial_step: bool,
|
|
trace_decls: TraceModule,
|
|
traces: Box<[SimTrace<BitVec>]>,
|
|
trace_writers: Vec<TraceWriterState<DynTraceWriterDecls>>,
|
|
instant: SimInstant,
|
|
}
|
|
|
|
impl SimulationImpl {
|
|
fn parse_io(&mut self, target: Target, value: CompiledValue<CanonicalType>) {
|
|
self.io_targets.insert(target, value);
|
|
match value.layout.body {
|
|
CompiledTypeLayoutBody::Scalar => match target.flow() {
|
|
Flow::Source => {}
|
|
Flow::Sink => {
|
|
self.uninitialized_inputs.insert(target);
|
|
}
|
|
Flow::Duplex => unreachable!(),
|
|
},
|
|
CompiledTypeLayoutBody::Array { .. } => {
|
|
let value = value.map_ty(Array::from_canonical);
|
|
for index in 0..value.layout.ty.len() {
|
|
self.parse_io(
|
|
target.join(
|
|
TargetPathElement::from(TargetPathArrayElement { index })
|
|
.intern_sized(),
|
|
),
|
|
value.element(index),
|
|
);
|
|
}
|
|
}
|
|
CompiledTypeLayoutBody::Bundle { .. } => {
|
|
let value = value.map_ty(Bundle::from_canonical);
|
|
for BundleField { name, .. } in value.layout.ty.fields() {
|
|
self.parse_io(
|
|
target.join(
|
|
TargetPathElement::from(TargetPathBundleField { name }).intern_sized(),
|
|
),
|
|
value.field_by_name(name),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fn new(compiled: Compiled<Bundle>) -> Self {
|
|
let mut retval = Self {
|
|
state: State::new(compiled.insns),
|
|
io: compiled.io.to_expr(),
|
|
uninitialized_inputs: HashSet::new(),
|
|
io_targets: HashMap::new(),
|
|
made_initial_step: false,
|
|
trace_decls: compiled.base_module.trace_decls,
|
|
traces: Box::from_iter(compiled.traces.iter().map(
|
|
|&SimTrace {
|
|
id,
|
|
kind,
|
|
state: _,
|
|
last_state: _,
|
|
}| SimTrace {
|
|
id,
|
|
kind,
|
|
state: kind.make_state(),
|
|
last_state: kind.make_state(),
|
|
},
|
|
)),
|
|
trace_writers: vec![],
|
|
instant: SimInstant::START,
|
|
};
|
|
let io_target = Target::from(compiled.io);
|
|
for (BundleField { name, .. }, value) in compiled
|
|
.io
|
|
.ty()
|
|
.fields()
|
|
.into_iter()
|
|
.zip(compiled.base_module.module_io)
|
|
{
|
|
retval.parse_io(
|
|
io_target
|
|
.join(TargetPathElement::from(TargetPathBundleField { name }).intern_sized()),
|
|
value,
|
|
);
|
|
}
|
|
retval
|
|
}
|
|
fn write_traces<const ONLY_IF_CHANGED: bool>(
|
|
&mut self,
|
|
mut trace_writer: DynTraceWriter,
|
|
) -> std::io::Result<DynTraceWriter> {
|
|
for &SimTrace {
|
|
id,
|
|
kind,
|
|
ref state,
|
|
ref last_state,
|
|
} in &self.traces
|
|
{
|
|
if ONLY_IF_CHANGED && state == last_state {
|
|
continue;
|
|
}
|
|
match kind {
|
|
SimTraceKind::BigUInt { .. } | SimTraceKind::SmallUInt { .. } => {
|
|
trace_writer.set_signal_uint(id, state)?;
|
|
}
|
|
SimTraceKind::BigSInt { .. } | SimTraceKind::SmallSInt { .. } => {
|
|
trace_writer.set_signal_sint(id, state)?;
|
|
}
|
|
SimTraceKind::BigBool { .. } | SimTraceKind::SmallBool { .. } => {
|
|
trace_writer.set_signal_bool(id, state[0])?;
|
|
}
|
|
SimTraceKind::BigAsyncReset { .. } | SimTraceKind::SmallAsyncReset { .. } => {
|
|
trace_writer.set_signal_async_reset(id, state[0])?;
|
|
}
|
|
SimTraceKind::BigSyncReset { .. } | SimTraceKind::SmallSyncReset { .. } => {
|
|
trace_writer.set_signal_sync_reset(id, state[0])?;
|
|
}
|
|
SimTraceKind::BigClock { .. } | SimTraceKind::SmallClock { .. } => {
|
|
trace_writer.set_signal_clock(id, state[0])?;
|
|
}
|
|
}
|
|
}
|
|
Ok(trace_writer)
|
|
}
|
|
fn init_trace_writer(
|
|
&mut self,
|
|
trace_writer: DynTraceWriter,
|
|
) -> std::io::Result<DynTraceWriter> {
|
|
let mut trace_writer = self.write_traces::<false>(trace_writer)?;
|
|
trace_writer.finish_init()?;
|
|
Ok(trace_writer)
|
|
}
|
|
fn update_trace_writer(
|
|
&mut self,
|
|
trace_writer: DynTraceWriter,
|
|
) -> std::io::Result<DynTraceWriter> {
|
|
self.write_traces::<true>(trace_writer)
|
|
}
|
|
#[inline(never)]
|
|
fn read_traces<const IS_INITIAL_STEP: bool>(&mut self) {
|
|
for &mut SimTrace {
|
|
id: _,
|
|
kind,
|
|
ref mut state,
|
|
ref mut last_state,
|
|
} in &mut self.traces
|
|
{
|
|
if !IS_INITIAL_STEP {
|
|
mem::swap(state, last_state);
|
|
}
|
|
match kind {
|
|
SimTraceKind::BigUInt { index, ty: _ } | SimTraceKind::BigSInt { index, ty: _ } => {
|
|
let bigint = &self.state.big_slots[index];
|
|
let mut bytes = bigint.to_signed_bytes_le();
|
|
bytes.resize(
|
|
state.len().div_ceil(8),
|
|
if bigint.is_negative() { 0xFF } else { 0 },
|
|
);
|
|
let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
|
|
let bitslice = &bitslice[..state.len()];
|
|
state.clone_from_bitslice(bitslice);
|
|
}
|
|
SimTraceKind::BigBool { index }
|
|
| SimTraceKind::BigAsyncReset { index }
|
|
| SimTraceKind::BigSyncReset { index }
|
|
| SimTraceKind::BigClock { index } => {
|
|
state.set(0, !self.state.big_slots[index].is_zero());
|
|
}
|
|
SimTraceKind::SmallUInt { index, ty: _ }
|
|
| SimTraceKind::SmallSInt { index, ty: _ } => {
|
|
let bytes = self.state.small_slots[index].to_le_bytes();
|
|
let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
|
|
let bitslice = &bitslice[..state.len()];
|
|
state.clone_from_bitslice(bitslice);
|
|
}
|
|
SimTraceKind::SmallBool { index }
|
|
| SimTraceKind::SmallAsyncReset { index }
|
|
| SimTraceKind::SmallSyncReset { index }
|
|
| SimTraceKind::SmallClock { index } => {
|
|
state.set(0, self.state.small_slots[index] != 0);
|
|
}
|
|
}
|
|
if IS_INITIAL_STEP {
|
|
last_state.copy_from_bitslice(state);
|
|
}
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn advance_time(&mut self, duration: SimDuration) {
|
|
if !self.made_initial_step {
|
|
self.settle_step();
|
|
}
|
|
self.instant += duration;
|
|
self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
|
|
match &mut trace_writer_state {
|
|
TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(),
|
|
TraceWriterState::Running(trace_writer) => {
|
|
trace_writer.change_time_to(this.instant)?;
|
|
}
|
|
TraceWriterState::Errored(_) => {}
|
|
}
|
|
Ok(trace_writer_state)
|
|
});
|
|
}
|
|
#[track_caller]
|
|
fn settle_step(&mut self) {
|
|
assert!(
|
|
self.uninitialized_inputs.is_empty(),
|
|
"didn't initialize all inputs",
|
|
);
|
|
self.state.setup_call(0);
|
|
self.state.run();
|
|
if self.made_initial_step {
|
|
self.read_traces::<false>();
|
|
} else {
|
|
self.read_traces::<true>();
|
|
}
|
|
self.made_initial_step = true;
|
|
self.for_each_trace_writer_storing_error(|this, trace_writer_state| {
|
|
Ok(match trace_writer_state {
|
|
TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running(
|
|
this.init_trace_writer(trace_writer_decls.write_decls(this.trace_decls)?)?,
|
|
),
|
|
TraceWriterState::Init(trace_writer) => {
|
|
TraceWriterState::Running(this.init_trace_writer(trace_writer)?)
|
|
}
|
|
TraceWriterState::Running(trace_writer) => {
|
|
TraceWriterState::Running(this.update_trace_writer(trace_writer)?)
|
|
}
|
|
TraceWriterState::Errored(e) => TraceWriterState::Errored(e),
|
|
})
|
|
});
|
|
}
|
|
#[track_caller]
|
|
fn get_io(&self, target: Target) -> CompiledValue<CanonicalType> {
|
|
if let Some(&retval) = self.io_targets.get(&target) {
|
|
return retval;
|
|
}
|
|
if Some(&target) == self.io.target().as_deref()
|
|
|| Some(target.base()) != self.io.target().map(|v| v.base())
|
|
{
|
|
panic!("simulator read/write expression must be an array element/field of `Simulator::io()`");
|
|
};
|
|
panic!("simulator read/write expression must not have dynamic array indexes");
|
|
}
|
|
#[track_caller]
|
|
fn read_bool_or_int<I: BoolOrIntType>(&self, io: Expr<I>) -> I::Value {
|
|
let Some(target) = io.target() else {
|
|
panic!("can't read from expression that's not a field/element of `Simulation::io()`");
|
|
};
|
|
let compiled_value = self.get_io(*target);
|
|
if !self.made_initial_step {
|
|
match target.flow() {
|
|
Flow::Source => {
|
|
panic!("can't read from an output before the simulation has made any steps");
|
|
}
|
|
Flow::Sink => {
|
|
if self.uninitialized_inputs.contains(&*target) {
|
|
panic!("can't read from an uninitialized input");
|
|
}
|
|
}
|
|
Flow::Duplex => unreachable!(),
|
|
}
|
|
}
|
|
match compiled_value.range.len() {
|
|
TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping(
|
|
self.state.small_slots[compiled_value.range.small_slots.start],
|
|
),
|
|
TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping(
|
|
self.state.big_slots[compiled_value.range.big_slots.start].clone(),
|
|
),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn write_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>, value: I::Value) {
|
|
let Some(target) = io.target() else {
|
|
panic!("can't write to an expression that's not a field/element of `Simulation::io()`");
|
|
};
|
|
let compiled_value = self.get_io(*target);
|
|
match target.flow() {
|
|
Flow::Source => {
|
|
panic!("can't write to an output");
|
|
}
|
|
Flow::Sink => {}
|
|
Flow::Duplex => unreachable!(),
|
|
}
|
|
let value: BigInt = value.into();
|
|
if !self.made_initial_step {
|
|
self.uninitialized_inputs.remove(&*target);
|
|
}
|
|
match compiled_value.range.len() {
|
|
TypeLen::A_SMALL_SLOT => {
|
|
self.state.small_slots[compiled_value.range.small_slots.start] =
|
|
value.to_u64().expect("value out of range");
|
|
}
|
|
TypeLen::A_BIG_SLOT => {
|
|
self.state.big_slots[compiled_value.range.big_slots.start] = value
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
fn close_all_trace_writers(&mut self) -> std::io::Result<()> {
|
|
let trace_writers = mem::take(&mut self.trace_writers);
|
|
let mut retval = Ok(());
|
|
let close_trace_writer =
|
|
|trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
|
|
TraceWriterState::Decls(v) => v.write_decls(self.trace_decls)?.close(),
|
|
TraceWriterState::Init(v) => v.close(),
|
|
TraceWriterState::Running(v) => v.close(),
|
|
TraceWriterState::Errored(Some(e)) => Err(e),
|
|
TraceWriterState::Errored(None) => Ok(()),
|
|
};
|
|
for trace_writer in trace_writers {
|
|
retval = retval.and(close_trace_writer(trace_writer));
|
|
}
|
|
retval
|
|
}
|
|
fn for_each_trace_writer_storing_error(
|
|
&mut self,
|
|
mut f: impl FnMut(
|
|
&mut Self,
|
|
TraceWriterState<DynTraceWriterDecls>,
|
|
) -> std::io::Result<TraceWriterState<DynTraceWriterDecls>>,
|
|
) {
|
|
let mut trace_writers = mem::take(&mut self.trace_writers);
|
|
for trace_writer in &mut trace_writers {
|
|
*trace_writer = match f(
|
|
self,
|
|
mem::replace(trace_writer, TraceWriterState::Errored(None)),
|
|
) {
|
|
Ok(v) => v,
|
|
Err(e) => TraceWriterState::Errored(Some(e)),
|
|
};
|
|
}
|
|
self.trace_writers = trace_writers;
|
|
}
|
|
fn for_each_trace_writer_getting_error(
|
|
&mut self,
|
|
mut f: impl FnMut(
|
|
&mut Self,
|
|
TraceWriterState<DynTraceWriterDecls>,
|
|
) -> std::io::Result<TraceWriterState<DynTraceWriterDecls>>,
|
|
) -> std::io::Result<()> {
|
|
let mut retval = Ok(());
|
|
let mut trace_writers = mem::take(&mut self.trace_writers);
|
|
for trace_writer in &mut trace_writers {
|
|
*trace_writer = match f(
|
|
self,
|
|
mem::replace(trace_writer, TraceWriterState::Errored(None)),
|
|
) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
if retval.is_ok() {
|
|
retval = Err(e);
|
|
TraceWriterState::Errored(None)
|
|
} else {
|
|
TraceWriterState::Errored(Some(e))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
self.trace_writers = trace_writers;
|
|
retval
|
|
}
|
|
fn close(mut self) -> std::io::Result<()> {
|
|
self.close_all_trace_writers()
|
|
}
|
|
fn flush_traces(&mut self) -> std::io::Result<()> {
|
|
self.for_each_trace_writer_getting_error(
|
|
|this, trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
|
|
TraceWriterState::Decls(v) => {
|
|
let mut v = v.write_decls(this.trace_decls)?;
|
|
v.flush()?;
|
|
Ok(TraceWriterState::Init(v))
|
|
}
|
|
TraceWriterState::Init(mut v) => {
|
|
v.flush()?;
|
|
Ok(TraceWriterState::Init(v))
|
|
}
|
|
TraceWriterState::Running(mut v) => {
|
|
v.flush()?;
|
|
Ok(TraceWriterState::Running(v))
|
|
}
|
|
TraceWriterState::Errored(Some(e)) => Err(e),
|
|
TraceWriterState::Errored(None) => Ok(TraceWriterState::Errored(None)),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Drop for SimulationImpl {
|
|
fn drop(&mut self) {
|
|
self.close_all_trace_writers()
|
|
.expect("error closing trace writers");
|
|
}
|
|
}
|
|
|
|
pub struct Simulation<T: BundleType> {
|
|
sim_impl: SimulationImpl,
|
|
io: Expr<T>,
|
|
}
|
|
|
|
struct SortedSetDebug<'a, T>(&'a HashSet<T>);
|
|
|
|
impl<T: fmt::Debug> fmt::Debug for SortedSetDebug<'_, T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let mut entries = Vec::from_iter(self.0.iter().map(|v| {
|
|
if f.alternate() {
|
|
format!("{v:#?}")
|
|
} else {
|
|
format!("{v:?}")
|
|
}
|
|
}));
|
|
entries.sort();
|
|
f.debug_set()
|
|
.entries(entries.iter().map(DebugAsDisplay))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
struct SortedMapDebug<'a, K, V>(&'a HashMap<K, V>);
|
|
|
|
impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for SortedMapDebug<'_, K, V> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let mut entries = Vec::from_iter(self.0.iter().map(|(k, v)| {
|
|
if f.alternate() {
|
|
(format!("{k:#?}"), format!("{v:#?}"))
|
|
} else {
|
|
(format!("{k:?}"), format!("{v:?}"))
|
|
}
|
|
}));
|
|
entries.sort();
|
|
f.debug_map()
|
|
.entries(
|
|
entries
|
|
.iter()
|
|
.map(|(k, v)| (DebugAsDisplay(k), DebugAsDisplay(v))),
|
|
)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<T: BundleType> fmt::Debug for Simulation<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
sim_impl:
|
|
SimulationImpl {
|
|
state,
|
|
io: _,
|
|
uninitialized_inputs,
|
|
io_targets,
|
|
made_initial_step,
|
|
trace_decls,
|
|
traces,
|
|
trace_writers,
|
|
instant,
|
|
},
|
|
io,
|
|
} = self;
|
|
f.debug_struct("Simulation")
|
|
.field("state", state)
|
|
.field("io", io)
|
|
.field(
|
|
"uninitialized_inputs",
|
|
&SortedSetDebug(uninitialized_inputs),
|
|
)
|
|
.field("io_targets", &SortedMapDebug(io_targets))
|
|
.field("made_initial_step", made_initial_step)
|
|
.field("trace_decls", trace_decls)
|
|
.field("traces", traces)
|
|
.field("trace_writers", trace_writers)
|
|
.field("instant", instant)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<T: BundleType> Simulation<T> {
|
|
pub fn new(module: Interned<Module<T>>) -> Self {
|
|
Self::from_compiled(Compiled::new(module))
|
|
}
|
|
pub fn add_trace_writer<W: TraceWriterDecls>(&mut self, writer: W) {
|
|
self.sim_impl
|
|
.trace_writers
|
|
.push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer)));
|
|
}
|
|
pub fn flush_traces(&mut self) -> std::io::Result<()> {
|
|
self.sim_impl.flush_traces()
|
|
}
|
|
pub fn close(self) -> std::io::Result<()> {
|
|
self.sim_impl.close()
|
|
}
|
|
pub fn canonical(self) -> Simulation<Bundle> {
|
|
let Self { sim_impl, io } = self;
|
|
Simulation {
|
|
sim_impl,
|
|
io: Expr::as_bundle(io),
|
|
}
|
|
}
|
|
pub fn from_canonical(canonical: Simulation<Bundle>) -> Self {
|
|
let Simulation { sim_impl, io } = canonical;
|
|
Self {
|
|
sim_impl,
|
|
io: Expr::from_bundle(io),
|
|
}
|
|
}
|
|
pub fn io(&self) -> Expr<T> {
|
|
self.io.to_expr()
|
|
}
|
|
pub fn from_compiled(compiled: Compiled<T>) -> Self {
|
|
let sim_impl = SimulationImpl::new(compiled.canonical());
|
|
Self {
|
|
io: Expr::from_bundle(sim_impl.io),
|
|
sim_impl,
|
|
}
|
|
}
|
|
#[track_caller]
|
|
pub fn settle_step(&mut self) {
|
|
self.sim_impl.settle_step();
|
|
}
|
|
#[track_caller]
|
|
pub fn advance_time(&mut self, duration: SimDuration) {
|
|
self.sim_impl.advance_time(duration);
|
|
}
|
|
#[track_caller]
|
|
pub fn read_bool_or_int<I: BoolOrIntType>(&self, io: Expr<I>) -> I::Value {
|
|
self.sim_impl.read_bool_or_int(io)
|
|
}
|
|
#[track_caller]
|
|
pub fn write_bool_or_int<I: BoolOrIntType>(
|
|
&mut self,
|
|
io: Expr<I>,
|
|
value: impl ToExpr<Type = I>,
|
|
) {
|
|
let value = value.to_expr();
|
|
assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch");
|
|
let value = value
|
|
.to_literal_bits()
|
|
.expect("the value that is being written to an input must be a literal");
|
|
self.sim_impl
|
|
.write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value)));
|
|
}
|
|
}
|