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,
|
||||
},
|
||||
prelude::*,
|
||||
reset::{ResetType, ResetTypeDispatch},
|
||||
sim::{
|
||||
interpreter::{
|
||||
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
||||
|
@ -208,6 +209,14 @@ impl<T: Type> CompiledValue<T> {
|
|||
fn write(self) -> (CompiledTypeLayout<T>, TypeIndexRange) {
|
||||
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>(
|
||||
self,
|
||||
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)]
|
||||
pub struct Compiler {
|
||||
insns: Insns<InsnsBuilding>,
|
||||
|
@ -1192,7 +1224,12 @@ pub struct Compiler {
|
|||
decl_conditions: HashMap<TargetInInstantiatedModule, Interned<[Cond]>>,
|
||||
compiled_values_to_dyn_array_indexes:
|
||||
HashMap<CompiledValue<UInt>, StatePartIndex<StatePartKindSmallSlots>>,
|
||||
compiled_value_bool_dest_is_small_map:
|
||||
HashMap<CompiledValue<CanonicalType>, StatePartIndex<StatePartKindSmallSlots>>,
|
||||
assignments: Assignments,
|
||||
clock_triggers: Vec<ClockTrigger>,
|
||||
compiled_value_to_clock_trigger_map: HashMap<CompiledValue<Clock>, ClockTrigger>,
|
||||
registers: Vec<Register>,
|
||||
traces: Vec<SimTrace<()>>,
|
||||
}
|
||||
|
||||
|
@ -1211,7 +1248,11 @@ impl Compiler {
|
|||
compiled_exprs_to_values: HashMap::new(),
|
||||
decl_conditions: HashMap::new(),
|
||||
compiled_values_to_dyn_array_indexes: HashMap::new(),
|
||||
compiled_value_bool_dest_is_small_map: HashMap::new(),
|
||||
assignments: Assignments::default(),
|
||||
clock_triggers: Vec::new(),
|
||||
compiled_value_to_clock_trigger_map: HashMap::new(),
|
||||
registers: Vec::new(),
|
||||
traces: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -1664,6 +1705,48 @@ impl Compiler {
|
|||
.insert(compiled_value, 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(
|
||||
&mut self,
|
||||
instantiated_module: InstantiatedModule,
|
||||
|
@ -2626,6 +2709,125 @@ impl Compiler {
|
|||
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(
|
||||
&mut self,
|
||||
declaration: StmtDeclaration,
|
||||
|
@ -2644,17 +2846,17 @@ impl Compiler {
|
|||
target: target_base.into(),
|
||||
};
|
||||
self.decl_conditions.insert(target, conditions);
|
||||
self.compile_value(target);
|
||||
let compiled_value = self.compile_value(target);
|
||||
match declaration {
|
||||
StmtDeclaration::Wire(StmtWire { annotations, wire }) => {}
|
||||
StmtDeclaration::Reg(StmtReg { annotations, reg }) => {
|
||||
todo!();
|
||||
StmtDeclaration::Reg(_) => {
|
||||
unreachable!("Reset types were already replaced by SyncReset or AsyncReset");
|
||||
}
|
||||
StmtDeclaration::RegSync(StmtReg { annotations, reg }) => {
|
||||
todo!();
|
||||
StmtDeclaration::RegSync(stmt_reg) => {
|
||||
self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value)
|
||||
}
|
||||
StmtDeclaration::RegAsync(StmtReg { annotations, reg }) => {
|
||||
todo!();
|
||||
StmtDeclaration::RegAsync(stmt_reg) => {
|
||||
self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value)
|
||||
}
|
||||
StmtDeclaration::Instance(StmtInstance {
|
||||
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> {
|
||||
let base_module =
|
||||
*self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized());
|
||||
self.process_assignments();
|
||||
self.process_registers();
|
||||
let clocks_triggered = self.process_clocks();
|
||||
self.insns
|
||||
.push(Insn::Return, self.base_module.source_location());
|
||||
Compiled {
|
||||
|
@ -2956,6 +3257,7 @@ impl Compiler {
|
|||
self.original_base_module.source_location(),
|
||||
),
|
||||
traces: Intern::intern_owned(self.traces),
|
||||
clocks_triggered,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2972,6 +3274,7 @@ pub struct Compiled<T: BundleType> {
|
|||
base_module: CompiledModule,
|
||||
io: Instance<T>,
|
||||
traces: Interned<[SimTrace<()>]>,
|
||||
clocks_triggered: Interned<[StatePartIndex<StatePartKindSmallSlots>]>,
|
||||
}
|
||||
|
||||
impl<T: BundleType> Compiled<T> {
|
||||
|
@ -2984,12 +3287,14 @@ impl<T: BundleType> Compiled<T> {
|
|||
base_module,
|
||||
io,
|
||||
traces,
|
||||
clocks_triggered,
|
||||
} = self;
|
||||
Compiled {
|
||||
insns,
|
||||
base_module,
|
||||
io: Instance::from_canonical(io.canonical()),
|
||||
traces,
|
||||
clocks_triggered,
|
||||
}
|
||||
}
|
||||
pub fn from_canonical(canonical: Compiled<Bundle>) -> Self {
|
||||
|
@ -2998,12 +3303,14 @@ impl<T: BundleType> Compiled<T> {
|
|||
base_module,
|
||||
io,
|
||||
traces,
|
||||
clocks_triggered,
|
||||
} = canonical;
|
||||
Self {
|
||||
insns,
|
||||
base_module,
|
||||
io: Instance::from_canonical(io.canonical()),
|
||||
traces,
|
||||
clocks_triggered,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3621,6 +3928,7 @@ struct SimulationImpl {
|
|||
traces: Box<[SimTrace<BitVec>]>,
|
||||
trace_writers: Vec<TraceWriterState<DynTraceWriterDecls>>,
|
||||
instant: SimInstant,
|
||||
clocks_triggered: Interned<[StatePartIndex<StatePartKindSmallSlots>]>,
|
||||
}
|
||||
|
||||
impl SimulationImpl {
|
||||
|
@ -3683,6 +3991,7 @@ impl SimulationImpl {
|
|||
)),
|
||||
trace_writers: vec![],
|
||||
instant: SimInstant::START,
|
||||
clocks_triggered: compiled.clocks_triggered,
|
||||
};
|
||||
let io_target = Target::from(compiled.io);
|
||||
for (BundleField { name, .. }, value) in compiled
|
||||
|
@ -3802,7 +4111,7 @@ impl SimulationImpl {
|
|||
}
|
||||
#[track_caller]
|
||||
fn advance_time(&mut self, duration: SimDuration) {
|
||||
self.settle_step();
|
||||
self.settle();
|
||||
self.instant += duration;
|
||||
self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
|
||||
match &mut trace_writer_state {
|
||||
|
@ -3816,37 +4125,43 @@ impl SimulationImpl {
|
|||
});
|
||||
}
|
||||
#[track_caller]
|
||||
fn settle_step(&mut self) {
|
||||
fn settle(&mut self) {
|
||||
assert!(
|
||||
self.uninitialized_inputs.is_empty(),
|
||||
"didn't initialize all inputs",
|
||||
);
|
||||
if !self.needs_settle {
|
||||
return;
|
||||
for _ in 0..100000 {
|
||||
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);
|
||||
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),
|
||||
})
|
||||
});
|
||||
panic!("settle(): took too many steps");
|
||||
}
|
||||
#[track_caller]
|
||||
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");
|
||||
}
|
||||
#[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 {
|
||||
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 {
|
||||
self.settle_step();
|
||||
self.settle();
|
||||
} else {
|
||||
match target.flow() {
|
||||
Flow::Source => {
|
||||
|
@ -3881,6 +4196,56 @@ impl SimulationImpl {
|
|||
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() {
|
||||
TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping(
|
||||
self.state.small_slots[compiled_value.range.small_slots.start],
|
||||
|
@ -3893,22 +4258,8 @@ impl SimulationImpl {
|
|||
}
|
||||
#[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 compiled_value = self.write_helper(Expr::canonical(io));
|
||||
let value: BigInt = value.into();
|
||||
if !self.made_initial_step {
|
||||
self.uninitialized_inputs.remove(&*target);
|
||||
}
|
||||
self.needs_settle = true;
|
||||
match compiled_value.range.len() {
|
||||
TypeLen::A_SMALL_SLOT => {
|
||||
self.state.small_slots[compiled_value.range.small_slots.start] =
|
||||
|
@ -3985,13 +4336,13 @@ impl SimulationImpl {
|
|||
}
|
||||
fn close(mut self) -> std::io::Result<()> {
|
||||
if self.made_initial_step {
|
||||
self.settle_step();
|
||||
self.settle();
|
||||
}
|
||||
self.close_all_trace_writers()
|
||||
}
|
||||
fn flush_traces(&mut self) -> std::io::Result<()> {
|
||||
if self.made_initial_step {
|
||||
self.settle_step();
|
||||
self.settle();
|
||||
}
|
||||
self.for_each_trace_writer_getting_error(
|
||||
|this, trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
|
||||
|
@ -4082,6 +4433,7 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
|
|||
traces,
|
||||
trace_writers,
|
||||
instant,
|
||||
clocks_triggered,
|
||||
},
|
||||
io,
|
||||
} = self;
|
||||
|
@ -4099,6 +4451,7 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
|
|||
.field("traces", traces)
|
||||
.field("trace_writers", trace_writers)
|
||||
.field("instant", instant)
|
||||
.field("clocks_triggered", clocks_triggered)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -4143,8 +4496,8 @@ impl<T: BundleType> Simulation<T> {
|
|||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn settle_step(&mut self) {
|
||||
self.sim_impl.settle_step();
|
||||
pub fn settle(&mut self) {
|
||||
self.sim_impl.settle();
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn advance_time(&mut self, duration: SimDuration) {
|
||||
|
@ -4168,4 +4521,28 @@ impl<T: BundleType> Simulation<T> {
|
|||
self.sim_impl
|
||||
.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;
|
||||
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 {
|
||||
#[kind = Output]
|
||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||
|
@ -2237,6 +2248,18 @@ impl_insns! {
|
|||
state.big_slots[dest] = value;
|
||||
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 {
|
||||
#[kind = Output]
|
||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||
|
@ -2283,6 +2306,16 @@ impl_insns! {
|
|||
state.big_slots[dest] = value;
|
||||
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 {
|
||||
#[kind = Output]
|
||||
dest: StatePartIndex<StatePartKindBigSlots>,
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue