Compare commits
	
		
			4 commits
		
	
	
		
			3abba7f9eb
			...
			fd45465d35
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fd45465d35 | |||
| 5e0548db26 | |||
| 12b3ba57f1 | |||
| 965fe53077 | 
					 6 changed files with 2967 additions and 64 deletions
				
			
		| 
						 | 
				
			
			@ -770,7 +770,10 @@ impl<'a, P: Pass> PassArgs<'a, P> {
 | 
			
		|||
        fallback_error_source_location: Option<SourceLocation>,
 | 
			
		||||
    ) -> Result<(), DeduceResetsError> {
 | 
			
		||||
        assert_eq!(a.layout, b.layout);
 | 
			
		||||
        assert_eq!(a.ty, b.ty);
 | 
			
		||||
        assert!(
 | 
			
		||||
            a.ty.can_connect(b.ty),
 | 
			
		||||
            "can't connect types! a:\n{a:?}\nb:\n{b:?}"
 | 
			
		||||
        );
 | 
			
		||||
        for (a_node_index, b_node_index) in a.node_indexes.into_iter().zip(b.node_indexes) {
 | 
			
		||||
            self.state.reset_graph.union(
 | 
			
		||||
                a_node_index,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    expr::{Expr, ToExpr},
 | 
			
		||||
    int::Bool,
 | 
			
		||||
    clock::Clock,
 | 
			
		||||
    expr::{ops, Expr, ToExpr},
 | 
			
		||||
    int::{Bool, SInt, UInt},
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +12,19 @@ mod sealed {
 | 
			
		|||
    pub trait ResetTypeSealed {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {
 | 
			
		||||
pub trait ResetType:
 | 
			
		||||
    StaticType<MaskType = Bool>
 | 
			
		||||
    + sealed::ResetTypeSealed
 | 
			
		||||
    + ops::ExprCastTo<Bool>
 | 
			
		||||
    + ops::ExprCastTo<Reset>
 | 
			
		||||
    + ops::ExprCastTo<SyncReset>
 | 
			
		||||
    + ops::ExprCastTo<AsyncReset>
 | 
			
		||||
    + ops::ExprCastTo<Clock>
 | 
			
		||||
    + ops::ExprCastTo<UInt<1>>
 | 
			
		||||
    + ops::ExprCastTo<SInt<1>>
 | 
			
		||||
    + ops::ExprCastTo<UInt>
 | 
			
		||||
    + ops::ExprCastTo<SInt>
 | 
			
		||||
{
 | 
			
		||||
    fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -477,7 +477,7 @@ impl<W: io::Write> TraceWriter for VcdWriter<W> {
 | 
			
		|||
    fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
 | 
			
		||||
        match value.len() {
 | 
			
		||||
            0 => self.writer.write_all(b"s0 ")?,
 | 
			
		||||
            1 => write!(self.writer, "{} ", if value[0] { "1" } else { "0" })?,
 | 
			
		||||
            1 => self.writer.write_all(if value[0] { b"1" } else { b"0" })?,
 | 
			
		||||
            _ => {
 | 
			
		||||
                self.writer.write_all(b"b")?;
 | 
			
		||||
                let mut any_ones = false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue