sim: add support for registers
This commit is contained in:
parent
5e0548db26
commit
fd45465d35
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule,
|
StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
reset::{ResetType, ResetTypeDispatch},
|
||||||
sim::{
|
sim::{
|
||||||
interpreter::{
|
interpreter::{
|
||||||
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
||||||
|
@ -208,6 +209,14 @@ impl<T: Type> CompiledValue<T> {
|
||||||
fn write(self) -> (CompiledTypeLayout<T>, TypeIndexRange) {
|
fn write(self) -> (CompiledTypeLayout<T>, TypeIndexRange) {
|
||||||
self.write.unwrap_or((self.layout, self.range))
|
self.write.unwrap_or((self.layout, self.range))
|
||||||
}
|
}
|
||||||
|
fn write_value(self) -> Self {
|
||||||
|
let (layout, range) = self.write();
|
||||||
|
Self {
|
||||||
|
layout,
|
||||||
|
range,
|
||||||
|
write: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
fn map<U: Type>(
|
fn map<U: Type>(
|
||||||
self,
|
self,
|
||||||
mut f: impl FnMut(
|
mut f: impl FnMut(
|
||||||
|
@ -1180,6 +1189,29 @@ impl Assignment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RegisterReset {
|
||||||
|
is_async: bool,
|
||||||
|
init: CompiledValue<CanonicalType>,
|
||||||
|
rst: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct ClockTrigger {
|
||||||
|
last_clk_was_low: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
clk: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
source_location: SourceLocation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Register {
|
||||||
|
value: CompiledValue<CanonicalType>,
|
||||||
|
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
reset: Option<RegisterReset>,
|
||||||
|
source_location: SourceLocation,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
insns: Insns<InsnsBuilding>,
|
insns: Insns<InsnsBuilding>,
|
||||||
|
@ -1192,7 +1224,12 @@ pub struct Compiler {
|
||||||
decl_conditions: HashMap<TargetInInstantiatedModule, Interned<[Cond]>>,
|
decl_conditions: HashMap<TargetInInstantiatedModule, Interned<[Cond]>>,
|
||||||
compiled_values_to_dyn_array_indexes:
|
compiled_values_to_dyn_array_indexes:
|
||||||
HashMap<CompiledValue<UInt>, StatePartIndex<StatePartKindSmallSlots>>,
|
HashMap<CompiledValue<UInt>, StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
|
compiled_value_bool_dest_is_small_map:
|
||||||
|
HashMap<CompiledValue<CanonicalType>, StatePartIndex<StatePartKindSmallSlots>>,
|
||||||
assignments: Assignments,
|
assignments: Assignments,
|
||||||
|
clock_triggers: Vec<ClockTrigger>,
|
||||||
|
compiled_value_to_clock_trigger_map: HashMap<CompiledValue<Clock>, ClockTrigger>,
|
||||||
|
registers: Vec<Register>,
|
||||||
traces: Vec<SimTrace<()>>,
|
traces: Vec<SimTrace<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1211,7 +1248,11 @@ impl Compiler {
|
||||||
compiled_exprs_to_values: HashMap::new(),
|
compiled_exprs_to_values: HashMap::new(),
|
||||||
decl_conditions: HashMap::new(),
|
decl_conditions: HashMap::new(),
|
||||||
compiled_values_to_dyn_array_indexes: HashMap::new(),
|
compiled_values_to_dyn_array_indexes: HashMap::new(),
|
||||||
|
compiled_value_bool_dest_is_small_map: HashMap::new(),
|
||||||
assignments: Assignments::default(),
|
assignments: Assignments::default(),
|
||||||
|
clock_triggers: Vec::new(),
|
||||||
|
compiled_value_to_clock_trigger_map: HashMap::new(),
|
||||||
|
registers: Vec::new(),
|
||||||
traces: Vec::new(),
|
traces: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1664,6 +1705,48 @@ impl Compiler {
|
||||||
.insert(compiled_value, retval);
|
.insert(compiled_value, retval);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
|
fn compiled_value_bool_dest_is_small(
|
||||||
|
&mut self,
|
||||||
|
compiled_value: CompiledValue<CanonicalType>,
|
||||||
|
source_location: SourceLocation,
|
||||||
|
) -> StatePartIndex<StatePartKindSmallSlots> {
|
||||||
|
if let Some(&retval) = self
|
||||||
|
.compiled_value_bool_dest_is_small_map
|
||||||
|
.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: Bool.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::IsNonZeroDestIsSmall {
|
||||||
|
dest,
|
||||||
|
src: compiled_value.range.big_slots.start,
|
||||||
|
}],
|
||||||
|
source_location,
|
||||||
|
);
|
||||||
|
dest
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
self.compiled_value_bool_dest_is_small_map
|
||||||
|
.insert(compiled_value, retval);
|
||||||
|
retval
|
||||||
|
}
|
||||||
fn compile_expr(
|
fn compile_expr(
|
||||||
&mut self,
|
&mut self,
|
||||||
instantiated_module: InstantiatedModule,
|
instantiated_module: InstantiatedModule,
|
||||||
|
@ -2626,6 +2709,125 @@ impl Compiler {
|
||||||
source_location,
|
source_location,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
fn compile_clock(
|
||||||
|
&mut self,
|
||||||
|
clk: CompiledValue<Clock>,
|
||||||
|
source_location: SourceLocation,
|
||||||
|
) -> ClockTrigger {
|
||||||
|
if let Some(&retval) = self.compiled_value_to_clock_trigger_map.get(&clk) {
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
let mut alloc_small_slot = |part_name: &str| {
|
||||||
|
self.insns
|
||||||
|
.state_layout
|
||||||
|
.ty
|
||||||
|
.small_slots
|
||||||
|
.allocate(&StatePartLayout::scalar(SlotDebugData {
|
||||||
|
name: Interned::default(),
|
||||||
|
ty: Bool.canonical(),
|
||||||
|
}))
|
||||||
|
.start
|
||||||
|
};
|
||||||
|
let last_clk_was_low = alloc_small_slot("last_clk_was_low");
|
||||||
|
let clk_triggered = alloc_small_slot("clk_triggered");
|
||||||
|
let retval = ClockTrigger {
|
||||||
|
last_clk_was_low,
|
||||||
|
clk: self.compiled_value_bool_dest_is_small(
|
||||||
|
clk.map_ty(CanonicalType::Clock),
|
||||||
|
source_location,
|
||||||
|
),
|
||||||
|
clk_triggered,
|
||||||
|
source_location,
|
||||||
|
};
|
||||||
|
self.add_assignment(
|
||||||
|
Interned::default(),
|
||||||
|
[Insn::AndSmall {
|
||||||
|
dest: clk_triggered,
|
||||||
|
lhs: retval.clk,
|
||||||
|
rhs: last_clk_was_low,
|
||||||
|
}],
|
||||||
|
source_location,
|
||||||
|
);
|
||||||
|
self.clock_triggers.push(retval);
|
||||||
|
self.compiled_value_to_clock_trigger_map.insert(clk, retval);
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
fn compile_stmt_reg<R: ResetType>(
|
||||||
|
&mut self,
|
||||||
|
stmt_reg: StmtReg<R>,
|
||||||
|
instantiated_module: InstantiatedModule,
|
||||||
|
value: CompiledValue<CanonicalType>,
|
||||||
|
) {
|
||||||
|
let StmtReg { annotations, reg } = stmt_reg;
|
||||||
|
let clk = self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().clk));
|
||||||
|
let clk = self
|
||||||
|
.compiled_expr_to_value(clk, reg.source_location())
|
||||||
|
.map_ty(Clock::from_canonical);
|
||||||
|
let clk = self.compile_clock(clk, reg.source_location());
|
||||||
|
struct Dispatch;
|
||||||
|
impl ResetTypeDispatch for Dispatch {
|
||||||
|
type Input<T: ResetType> = ();
|
||||||
|
|
||||||
|
type Output<T: ResetType> = bool;
|
||||||
|
|
||||||
|
fn reset(self, _input: Self::Input<Reset>) -> Self::Output<Reset> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_reset(self, _input: Self::Input<SyncReset>) -> Self::Output<SyncReset> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn async_reset(self, _input: Self::Input<AsyncReset>) -> Self::Output<AsyncReset> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let reset = if let Some(init) = reg.init() {
|
||||||
|
let init = self.compile_expr(instantiated_module, init);
|
||||||
|
let init = self.compiled_expr_to_value(init, reg.source_location());
|
||||||
|
let rst =
|
||||||
|
self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().rst));
|
||||||
|
let rst = self.compiled_expr_to_value(rst, reg.source_location());
|
||||||
|
let rst = self.compiled_value_bool_dest_is_small(rst, reg.source_location());
|
||||||
|
let is_async = R::dispatch((), Dispatch);
|
||||||
|
if is_async {
|
||||||
|
let cond = Expr::canonical(reg.clock_domain().rst.cast_to(Bool));
|
||||||
|
let cond = self.compile_expr(instantiated_module, cond);
|
||||||
|
let cond = self.compiled_expr_to_value(cond, reg.source_location());
|
||||||
|
let cond = cond.map_ty(Bool::from_canonical);
|
||||||
|
// write to the register's current value since asynchronous reset is combinational
|
||||||
|
let lhs = CompiledValue {
|
||||||
|
layout: value.layout,
|
||||||
|
range: value.range,
|
||||||
|
write: None,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
self.compile_simple_connect(
|
||||||
|
[Cond {
|
||||||
|
body: CondBody::IfTrue { cond },
|
||||||
|
source_location: reg.source_location(),
|
||||||
|
}][..]
|
||||||
|
.intern(),
|
||||||
|
lhs,
|
||||||
|
init,
|
||||||
|
reg.source_location(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(RegisterReset {
|
||||||
|
is_async,
|
||||||
|
init,
|
||||||
|
rst,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.registers.push(Register {
|
||||||
|
value,
|
||||||
|
clk_triggered: clk.clk_triggered,
|
||||||
|
reset,
|
||||||
|
source_location: reg.source_location(),
|
||||||
|
});
|
||||||
|
}
|
||||||
fn compile_declaration(
|
fn compile_declaration(
|
||||||
&mut self,
|
&mut self,
|
||||||
declaration: StmtDeclaration,
|
declaration: StmtDeclaration,
|
||||||
|
@ -2644,17 +2846,17 @@ impl Compiler {
|
||||||
target: target_base.into(),
|
target: target_base.into(),
|
||||||
};
|
};
|
||||||
self.decl_conditions.insert(target, conditions);
|
self.decl_conditions.insert(target, conditions);
|
||||||
self.compile_value(target);
|
let compiled_value = self.compile_value(target);
|
||||||
match declaration {
|
match declaration {
|
||||||
StmtDeclaration::Wire(StmtWire { annotations, wire }) => {}
|
StmtDeclaration::Wire(StmtWire { annotations, wire }) => {}
|
||||||
StmtDeclaration::Reg(StmtReg { annotations, reg }) => {
|
StmtDeclaration::Reg(_) => {
|
||||||
todo!();
|
unreachable!("Reset types were already replaced by SyncReset or AsyncReset");
|
||||||
}
|
}
|
||||||
StmtDeclaration::RegSync(StmtReg { annotations, reg }) => {
|
StmtDeclaration::RegSync(stmt_reg) => {
|
||||||
todo!();
|
self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value)
|
||||||
}
|
}
|
||||||
StmtDeclaration::RegAsync(StmtReg { annotations, reg }) => {
|
StmtDeclaration::RegAsync(stmt_reg) => {
|
||||||
todo!();
|
self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value)
|
||||||
}
|
}
|
||||||
StmtDeclaration::Instance(StmtInstance {
|
StmtDeclaration::Instance(StmtInstance {
|
||||||
annotations,
|
annotations,
|
||||||
|
@ -2938,10 +3140,109 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn process_clocks(&mut self) -> Interned<[StatePartIndex<StatePartKindSmallSlots>]> {
|
||||||
|
mem::take(&mut self.clock_triggers)
|
||||||
|
.into_iter()
|
||||||
|
.map(
|
||||||
|
|ClockTrigger {
|
||||||
|
last_clk_was_low,
|
||||||
|
clk,
|
||||||
|
clk_triggered,
|
||||||
|
source_location,
|
||||||
|
}| {
|
||||||
|
self.insns.push(
|
||||||
|
Insn::NotSmall {
|
||||||
|
dest: last_clk_was_low,
|
||||||
|
src: clk,
|
||||||
|
},
|
||||||
|
source_location,
|
||||||
|
);
|
||||||
|
clk_triggered
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
fn process_registers(&mut self) {
|
||||||
|
for Register {
|
||||||
|
value,
|
||||||
|
clk_triggered,
|
||||||
|
reset,
|
||||||
|
source_location,
|
||||||
|
} in mem::take(&mut self.registers)
|
||||||
|
{
|
||||||
|
let write_to_current_value = |value_to_write: CompiledValue<CanonicalType>| {
|
||||||
|
let TypeIndexRange {
|
||||||
|
small_slots,
|
||||||
|
big_slots,
|
||||||
|
} = value.range;
|
||||||
|
small_slots
|
||||||
|
.iter()
|
||||||
|
.zip(value_to_write.range.small_slots.iter())
|
||||||
|
.map(|(base, src)| Insn::CopySmall { dest: base, src })
|
||||||
|
.chain(
|
||||||
|
big_slots
|
||||||
|
.iter()
|
||||||
|
.zip(value_to_write.range.big_slots.iter())
|
||||||
|
.map(|(base, src)| Insn::Copy { dest: base, src }),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match reset {
|
||||||
|
Some(RegisterReset {
|
||||||
|
is_async,
|
||||||
|
init,
|
||||||
|
rst,
|
||||||
|
}) => {
|
||||||
|
let reg_end = self.insns.new_label();
|
||||||
|
let reg_reset = self.insns.new_label();
|
||||||
|
let branch_if_reset = Insn::BranchIfSmallNonZero {
|
||||||
|
target: reg_reset,
|
||||||
|
value: rst,
|
||||||
|
};
|
||||||
|
let branch_if_not_triggered = Insn::BranchIfSmallZero {
|
||||||
|
target: reg_end,
|
||||||
|
value: clk_triggered,
|
||||||
|
};
|
||||||
|
if is_async {
|
||||||
|
self.insns.push(branch_if_reset, source_location);
|
||||||
|
self.insns.push(branch_if_not_triggered, source_location);
|
||||||
|
} else {
|
||||||
|
self.insns.push(branch_if_not_triggered, source_location);
|
||||||
|
self.insns.push(branch_if_reset, source_location);
|
||||||
|
}
|
||||||
|
for insn in write_to_current_value(value.write_value()) {
|
||||||
|
self.insns.push(insn, source_location);
|
||||||
|
}
|
||||||
|
self.insns
|
||||||
|
.push(Insn::Branch { target: reg_end }, source_location);
|
||||||
|
self.insns.define_label_at_next_insn(reg_reset);
|
||||||
|
for insn in write_to_current_value(init) {
|
||||||
|
self.insns.push(insn, source_location);
|
||||||
|
}
|
||||||
|
self.insns.define_label_at_next_insn(reg_end);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let reg_end = self.insns.new_label();
|
||||||
|
self.insns.push(
|
||||||
|
Insn::BranchIfSmallZero {
|
||||||
|
target: reg_end,
|
||||||
|
value: clk_triggered,
|
||||||
|
},
|
||||||
|
source_location,
|
||||||
|
);
|
||||||
|
for insn in write_to_current_value(value.write_value()) {
|
||||||
|
self.insns.push(insn, source_location);
|
||||||
|
}
|
||||||
|
self.insns.define_label_at_next_insn(reg_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn compile(mut self) -> Compiled<Bundle> {
|
pub fn compile(mut self) -> Compiled<Bundle> {
|
||||||
let base_module =
|
let base_module =
|
||||||
*self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized());
|
*self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized());
|
||||||
self.process_assignments();
|
self.process_assignments();
|
||||||
|
self.process_registers();
|
||||||
|
let clocks_triggered = self.process_clocks();
|
||||||
self.insns
|
self.insns
|
||||||
.push(Insn::Return, self.base_module.source_location());
|
.push(Insn::Return, self.base_module.source_location());
|
||||||
Compiled {
|
Compiled {
|
||||||
|
@ -2956,6 +3257,7 @@ impl Compiler {
|
||||||
self.original_base_module.source_location(),
|
self.original_base_module.source_location(),
|
||||||
),
|
),
|
||||||
traces: Intern::intern_owned(self.traces),
|
traces: Intern::intern_owned(self.traces),
|
||||||
|
clocks_triggered,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2972,6 +3274,7 @@ pub struct Compiled<T: BundleType> {
|
||||||
base_module: CompiledModule,
|
base_module: CompiledModule,
|
||||||
io: Instance<T>,
|
io: Instance<T>,
|
||||||
traces: Interned<[SimTrace<()>]>,
|
traces: Interned<[SimTrace<()>]>,
|
||||||
|
clocks_triggered: Interned<[StatePartIndex<StatePartKindSmallSlots>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BundleType> Compiled<T> {
|
impl<T: BundleType> Compiled<T> {
|
||||||
|
@ -2984,12 +3287,14 @@ impl<T: BundleType> Compiled<T> {
|
||||||
base_module,
|
base_module,
|
||||||
io,
|
io,
|
||||||
traces,
|
traces,
|
||||||
|
clocks_triggered,
|
||||||
} = self;
|
} = self;
|
||||||
Compiled {
|
Compiled {
|
||||||
insns,
|
insns,
|
||||||
base_module,
|
base_module,
|
||||||
io: Instance::from_canonical(io.canonical()),
|
io: Instance::from_canonical(io.canonical()),
|
||||||
traces,
|
traces,
|
||||||
|
clocks_triggered,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_canonical(canonical: Compiled<Bundle>) -> Self {
|
pub fn from_canonical(canonical: Compiled<Bundle>) -> Self {
|
||||||
|
@ -2998,12 +3303,14 @@ impl<T: BundleType> Compiled<T> {
|
||||||
base_module,
|
base_module,
|
||||||
io,
|
io,
|
||||||
traces,
|
traces,
|
||||||
|
clocks_triggered,
|
||||||
} = canonical;
|
} = canonical;
|
||||||
Self {
|
Self {
|
||||||
insns,
|
insns,
|
||||||
base_module,
|
base_module,
|
||||||
io: Instance::from_canonical(io.canonical()),
|
io: Instance::from_canonical(io.canonical()),
|
||||||
traces,
|
traces,
|
||||||
|
clocks_triggered,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3621,6 +3928,7 @@ struct SimulationImpl {
|
||||||
traces: Box<[SimTrace<BitVec>]>,
|
traces: Box<[SimTrace<BitVec>]>,
|
||||||
trace_writers: Vec<TraceWriterState<DynTraceWriterDecls>>,
|
trace_writers: Vec<TraceWriterState<DynTraceWriterDecls>>,
|
||||||
instant: SimInstant,
|
instant: SimInstant,
|
||||||
|
clocks_triggered: Interned<[StatePartIndex<StatePartKindSmallSlots>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimulationImpl {
|
impl SimulationImpl {
|
||||||
|
@ -3683,6 +3991,7 @@ impl SimulationImpl {
|
||||||
)),
|
)),
|
||||||
trace_writers: vec![],
|
trace_writers: vec![],
|
||||||
instant: SimInstant::START,
|
instant: SimInstant::START,
|
||||||
|
clocks_triggered: compiled.clocks_triggered,
|
||||||
};
|
};
|
||||||
let io_target = Target::from(compiled.io);
|
let io_target = Target::from(compiled.io);
|
||||||
for (BundleField { name, .. }, value) in compiled
|
for (BundleField { name, .. }, value) in compiled
|
||||||
|
@ -3802,7 +4111,7 @@ impl SimulationImpl {
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn advance_time(&mut self, duration: SimDuration) {
|
fn advance_time(&mut self, duration: SimDuration) {
|
||||||
self.settle_step();
|
self.settle();
|
||||||
self.instant += duration;
|
self.instant += duration;
|
||||||
self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
|
self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
|
||||||
match &mut trace_writer_state {
|
match &mut trace_writer_state {
|
||||||
|
@ -3816,37 +4125,43 @@ impl SimulationImpl {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn settle_step(&mut self) {
|
fn settle(&mut self) {
|
||||||
assert!(
|
assert!(
|
||||||
self.uninitialized_inputs.is_empty(),
|
self.uninitialized_inputs.is_empty(),
|
||||||
"didn't initialize all inputs",
|
"didn't initialize all inputs",
|
||||||
);
|
);
|
||||||
if !self.needs_settle {
|
for _ in 0..100000 {
|
||||||
return;
|
if !self.needs_settle {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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.needs_settle = self
|
||||||
|
.clocks_triggered
|
||||||
|
.iter()
|
||||||
|
.any(|i| self.state.small_slots[*i] != 0);
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
self.state.setup_call(0);
|
panic!("settle(): took too many steps");
|
||||||
self.state.run();
|
|
||||||
if self.made_initial_step {
|
|
||||||
self.read_traces::<false>();
|
|
||||||
} else {
|
|
||||||
self.read_traces::<true>();
|
|
||||||
}
|
|
||||||
self.made_initial_step = true;
|
|
||||||
self.needs_settle = false;
|
|
||||||
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]
|
#[track_caller]
|
||||||
fn get_io(&self, target: Target) -> CompiledValue<CanonicalType> {
|
fn get_io(&self, target: Target) -> CompiledValue<CanonicalType> {
|
||||||
|
@ -3861,13 +4176,13 @@ impl SimulationImpl {
|
||||||
panic!("simulator read/write expression must not have dynamic array indexes");
|
panic!("simulator read/write expression must not have dynamic array indexes");
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn read_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>) -> I::Value {
|
fn read_helper(&mut self, io: Expr<CanonicalType>) -> CompiledValue<CanonicalType> {
|
||||||
let Some(target) = io.target() else {
|
let Some(target) = io.target() else {
|
||||||
panic!("can't read from expression that's not a field/element of `Simulation::io()`");
|
panic!("can't read from expression that's not a field/element of `Simulation::io()`");
|
||||||
};
|
};
|
||||||
let compiled_value = self.get_io(*target);
|
let compiled_value = self.get_io(*target);
|
||||||
if self.made_initial_step {
|
if self.made_initial_step {
|
||||||
self.settle_step();
|
self.settle();
|
||||||
} else {
|
} else {
|
||||||
match target.flow() {
|
match target.flow() {
|
||||||
Flow::Source => {
|
Flow::Source => {
|
||||||
|
@ -3881,6 +4196,56 @@ impl SimulationImpl {
|
||||||
Flow::Duplex => unreachable!(),
|
Flow::Duplex => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
compiled_value
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn write_helper(&mut self, io: Expr<CanonicalType>) -> CompiledValue<CanonicalType> {
|
||||||
|
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!(),
|
||||||
|
}
|
||||||
|
if !self.made_initial_step {
|
||||||
|
self.uninitialized_inputs.remove(&*target);
|
||||||
|
}
|
||||||
|
self.needs_settle = true;
|
||||||
|
compiled_value
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn read_bit(&mut self, io: Expr<CanonicalType>) -> bool {
|
||||||
|
let compiled_value = self.read_helper(Expr::canonical(io));
|
||||||
|
match compiled_value.range.len() {
|
||||||
|
TypeLen::A_SMALL_SLOT => {
|
||||||
|
self.state.small_slots[compiled_value.range.small_slots.start] != 0
|
||||||
|
}
|
||||||
|
TypeLen::A_BIG_SLOT => !self.state.big_slots[compiled_value.range.big_slots.start]
|
||||||
|
.clone()
|
||||||
|
.is_zero(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn write_bit(&mut self, io: Expr<CanonicalType>, value: bool) {
|
||||||
|
let compiled_value = self.write_helper(io);
|
||||||
|
match compiled_value.range.len() {
|
||||||
|
TypeLen::A_SMALL_SLOT => {
|
||||||
|
self.state.small_slots[compiled_value.range.small_slots.start] = value as _;
|
||||||
|
}
|
||||||
|
TypeLen::A_BIG_SLOT => {
|
||||||
|
self.state.big_slots[compiled_value.range.big_slots.start] = value.into()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
fn read_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>) -> I::Value {
|
||||||
|
let compiled_value = self.read_helper(Expr::canonical(io));
|
||||||
match compiled_value.range.len() {
|
match compiled_value.range.len() {
|
||||||
TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping(
|
TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping(
|
||||||
self.state.small_slots[compiled_value.range.small_slots.start],
|
self.state.small_slots[compiled_value.range.small_slots.start],
|
||||||
|
@ -3893,22 +4258,8 @@ impl SimulationImpl {
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn write_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>, value: I::Value) {
|
fn write_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>, value: I::Value) {
|
||||||
let Some(target) = io.target() else {
|
let compiled_value = self.write_helper(Expr::canonical(io));
|
||||||
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();
|
let value: BigInt = value.into();
|
||||||
if !self.made_initial_step {
|
|
||||||
self.uninitialized_inputs.remove(&*target);
|
|
||||||
}
|
|
||||||
self.needs_settle = true;
|
|
||||||
match compiled_value.range.len() {
|
match compiled_value.range.len() {
|
||||||
TypeLen::A_SMALL_SLOT => {
|
TypeLen::A_SMALL_SLOT => {
|
||||||
self.state.small_slots[compiled_value.range.small_slots.start] =
|
self.state.small_slots[compiled_value.range.small_slots.start] =
|
||||||
|
@ -3985,13 +4336,13 @@ impl SimulationImpl {
|
||||||
}
|
}
|
||||||
fn close(mut self) -> std::io::Result<()> {
|
fn close(mut self) -> std::io::Result<()> {
|
||||||
if self.made_initial_step {
|
if self.made_initial_step {
|
||||||
self.settle_step();
|
self.settle();
|
||||||
}
|
}
|
||||||
self.close_all_trace_writers()
|
self.close_all_trace_writers()
|
||||||
}
|
}
|
||||||
fn flush_traces(&mut self) -> std::io::Result<()> {
|
fn flush_traces(&mut self) -> std::io::Result<()> {
|
||||||
if self.made_initial_step {
|
if self.made_initial_step {
|
||||||
self.settle_step();
|
self.settle();
|
||||||
}
|
}
|
||||||
self.for_each_trace_writer_getting_error(
|
self.for_each_trace_writer_getting_error(
|
||||||
|this, trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
|
|this, trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
|
||||||
|
@ -4082,6 +4433,7 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
|
||||||
traces,
|
traces,
|
||||||
trace_writers,
|
trace_writers,
|
||||||
instant,
|
instant,
|
||||||
|
clocks_triggered,
|
||||||
},
|
},
|
||||||
io,
|
io,
|
||||||
} = self;
|
} = self;
|
||||||
|
@ -4099,6 +4451,7 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
|
||||||
.field("traces", traces)
|
.field("traces", traces)
|
||||||
.field("trace_writers", trace_writers)
|
.field("trace_writers", trace_writers)
|
||||||
.field("instant", instant)
|
.field("instant", instant)
|
||||||
|
.field("clocks_triggered", clocks_triggered)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4143,8 +4496,8 @@ impl<T: BundleType> Simulation<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn settle_step(&mut self) {
|
pub fn settle(&mut self) {
|
||||||
self.sim_impl.settle_step();
|
self.sim_impl.settle();
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn advance_time(&mut self, duration: SimDuration) {
|
pub fn advance_time(&mut self, duration: SimDuration) {
|
||||||
|
@ -4168,4 +4521,28 @@ impl<T: BundleType> Simulation<T> {
|
||||||
self.sim_impl
|
self.sim_impl
|
||||||
.write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value)));
|
.write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value)));
|
||||||
}
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn write_clock(&mut self, io: Expr<Clock>, value: bool) {
|
||||||
|
self.sim_impl.write_bit(Expr::canonical(io), value);
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn read_clock(&mut self, io: Expr<Clock>) -> bool {
|
||||||
|
self.sim_impl.read_bit(Expr::canonical(io))
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn write_bool(&mut self, io: Expr<Bool>, value: bool) {
|
||||||
|
self.sim_impl.write_bit(Expr::canonical(io), value);
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn read_bool(&mut self, io: Expr<Bool>) -> bool {
|
||||||
|
self.sim_impl.read_bit(Expr::canonical(io))
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn write_reset<R: ResetType>(&mut self, io: Expr<R>, value: bool) {
|
||||||
|
self.sim_impl.write_bit(Expr::canonical(io), value);
|
||||||
|
}
|
||||||
|
#[track_caller]
|
||||||
|
pub fn read_reset<R: ResetType>(&mut self, io: Expr<R>) -> bool {
|
||||||
|
self.sim_impl.read_bit(Expr::canonical(io))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2201,6 +2201,17 @@ impl_insns! {
|
||||||
state.small_slots[dest] = value;
|
state.small_slots[dest] = value;
|
||||||
next!();
|
next!();
|
||||||
}
|
}
|
||||||
|
IsNonZeroDestIsSmall {
|
||||||
|
#[kind = Output]
|
||||||
|
dest: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
#[kind = Input]
|
||||||
|
src: StatePartIndex<StatePartKindBigSlots>,
|
||||||
|
} => {
|
||||||
|
let src = &state.big_slots[src];
|
||||||
|
let value = !src.is_zero();
|
||||||
|
state.small_slots[dest] = value as SmallUInt;
|
||||||
|
next!();
|
||||||
|
}
|
||||||
CastToSInt {
|
CastToSInt {
|
||||||
#[kind = Output]
|
#[kind = Output]
|
||||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||||
|
@ -2237,6 +2248,18 @@ impl_insns! {
|
||||||
state.big_slots[dest] = value;
|
state.big_slots[dest] = value;
|
||||||
next!();
|
next!();
|
||||||
}
|
}
|
||||||
|
AndSmall {
|
||||||
|
#[kind = Output]
|
||||||
|
dest: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
#[kind = Input]
|
||||||
|
lhs: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
#[kind = Input]
|
||||||
|
rhs: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
} => {
|
||||||
|
let value = state.small_slots[lhs] & state.small_slots[rhs];
|
||||||
|
state.small_slots[dest] = value;
|
||||||
|
next!();
|
||||||
|
}
|
||||||
Or {
|
Or {
|
||||||
#[kind = Output]
|
#[kind = Output]
|
||||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||||
|
@ -2283,6 +2306,16 @@ impl_insns! {
|
||||||
state.big_slots[dest] = value;
|
state.big_slots[dest] = value;
|
||||||
next!();
|
next!();
|
||||||
}
|
}
|
||||||
|
NotSmall {
|
||||||
|
#[kind = Output]
|
||||||
|
dest: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
#[kind = Input]
|
||||||
|
src: StatePartIndex<StatePartKindSmallSlots>,
|
||||||
|
} => {
|
||||||
|
let value = !state.small_slots[src];
|
||||||
|
state.small_slots[dest] = value;
|
||||||
|
next!();
|
||||||
|
}
|
||||||
Neg {
|
Neg {
|
||||||
#[kind = Output]
|
#[kind = Output]
|
||||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue