WIP adding VCD output
This commit is contained in:
parent
414a2d74f1
commit
6eef3c23b5
|
@ -21,16 +21,21 @@ use crate::{
|
|||
StmtWire,
|
||||
},
|
||||
prelude::*,
|
||||
sim::interpreter::{
|
||||
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
||||
SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed,
|
||||
StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
|
||||
StatePartKindSmallSlots, StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex,
|
||||
TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts,
|
||||
sim::{
|
||||
interpreter::{
|
||||
Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone,
|
||||
SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed,
|
||||
StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
|
||||
StatePartKindSmallSlots, StatePartLayout, StatePartLen, StatePartsValue,
|
||||
TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeLen,
|
||||
TypeParts,
|
||||
},
|
||||
time::SimInstant,
|
||||
},
|
||||
ty::StaticType,
|
||||
util::DebugAsDisplay,
|
||||
};
|
||||
use bitvec::{bits, order::Lsb0, slice::BitSlice};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
|
@ -40,6 +45,7 @@ use petgraph::visit::{
|
|||
use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut};
|
||||
|
||||
mod interpreter;
|
||||
pub mod time;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
enum CondBody {
|
||||
|
@ -2756,6 +2762,11 @@ impl Compiler {
|
|||
self.base_module,
|
||||
self.base_module.source_location(),
|
||||
),
|
||||
trace_decls: TraceModule {
|
||||
name: self.base_module.name(),
|
||||
children: Interned::default(), // TODO: finish
|
||||
},
|
||||
traces: Interned::default(), // TODO: finish
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2770,6 +2781,8 @@ pub struct Compiled<T: BundleType> {
|
|||
insns: Interned<Insns<InsnsBuildingDone>>,
|
||||
base_module: CompiledModule,
|
||||
io: Instance<T>,
|
||||
trace_decls: TraceModule,
|
||||
traces: Interned<[SimTrace]>,
|
||||
}
|
||||
|
||||
impl<T: BundleType> Compiled<T> {
|
||||
|
@ -2781,11 +2794,15 @@ impl<T: BundleType> Compiled<T> {
|
|||
insns,
|
||||
base_module,
|
||||
io,
|
||||
trace_decls,
|
||||
traces,
|
||||
} = self;
|
||||
Compiled {
|
||||
insns,
|
||||
base_module,
|
||||
io: Instance::from_canonical(io.canonical()),
|
||||
trace_decls,
|
||||
traces,
|
||||
}
|
||||
}
|
||||
pub fn from_canonical(canonical: Compiled<Bundle>) -> Self {
|
||||
|
@ -2793,21 +2810,471 @@ impl<T: BundleType> Compiled<T> {
|
|||
insns,
|
||||
base_module,
|
||||
io,
|
||||
trace_decls,
|
||||
traces,
|
||||
} = canonical;
|
||||
Self {
|
||||
insns,
|
||||
base_module,
|
||||
io: Instance::from_canonical(io.canonical()),
|
||||
trace_decls,
|
||||
traces,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct TraceScalarId(usize);
|
||||
|
||||
impl TraceScalarId {
|
||||
pub fn as_usize(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_trace_decl {
|
||||
(
|
||||
$(
|
||||
#[kind = $category_kind:ident]
|
||||
$(#[$category_meta:meta])*
|
||||
$category_variant:ident($category_enum:ident {
|
||||
fn $category_property_fn:ident(self) -> $category_property_fn_ret_ty:ty;
|
||||
$(
|
||||
$(#[$meta:meta])*
|
||||
$variant:ident($struct:ident {
|
||||
fn $property_fn:ident($property_fn_self:ident) -> _ $property_fn_block:block
|
||||
$($(#[$field_meta:meta])*
|
||||
$field_name:ident: $field_ty:ty,)*
|
||||
}),
|
||||
)*
|
||||
}),
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
$(#[$category_meta])*
|
||||
pub enum $category_kind {
|
||||
$($(#[$meta])*
|
||||
$variant,)*
|
||||
}
|
||||
|
||||
impl From<$category_kind> for TraceKind {
|
||||
fn from(v: $category_kind) -> Self {
|
||||
TraceKind::$category_variant(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
$(#[$category_meta])*
|
||||
pub enum $category_enum {
|
||||
$($(#[$meta])*
|
||||
$variant($struct),)*
|
||||
}
|
||||
|
||||
impl $category_enum {
|
||||
pub fn kind(self) -> $category_kind {
|
||||
match self {
|
||||
$(Self::$variant(_) => $category_kind::$variant,)*
|
||||
}
|
||||
}
|
||||
pub fn name(self) -> Interned<str> {
|
||||
match self {
|
||||
$(Self::$variant(v) => v.name,)*
|
||||
}
|
||||
}
|
||||
pub fn $category_property_fn(self) -> $category_property_fn_ret_ty {
|
||||
match self {
|
||||
$(Self::$variant(v) => v.$property_fn(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$category_enum> for TraceDecl {
|
||||
fn from(v: $category_enum) -> Self {
|
||||
TraceDecl::$category_variant(v)
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
$(#[$meta])*
|
||||
pub struct $struct {
|
||||
$($(#[$field_meta])*
|
||||
pub $field_name: $field_ty,)*
|
||||
}
|
||||
|
||||
impl $struct {
|
||||
pub fn $property_fn($property_fn_self) -> $category_property_fn_ret_ty $property_fn_block
|
||||
}
|
||||
|
||||
impl From<$struct> for $category_enum {
|
||||
fn from(v: $struct) -> Self {
|
||||
$category_enum::$variant(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$struct> for TraceDecl {
|
||||
fn from(v: $struct) -> Self {
|
||||
TraceDecl::$category_variant($category_enum::$variant(v))
|
||||
}
|
||||
}
|
||||
)*
|
||||
)*
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum TraceKind {
|
||||
$($(#[$category_meta])*
|
||||
$category_variant($category_kind),)*
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TraceDecl {
|
||||
$($(#[$category_meta])*
|
||||
$category_variant($category_enum),)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_trace_decl! {
|
||||
#[kind = TraceScopeKind]
|
||||
Scope(TraceScope {
|
||||
fn children(self) -> Interned<[TraceDecl]>;
|
||||
Module(TraceModule {
|
||||
fn children(self) -> _ {
|
||||
self.children
|
||||
}
|
||||
name: Interned<str>,
|
||||
children: Interned<[TraceDecl]>,
|
||||
}),
|
||||
Instance(TraceInstance {
|
||||
fn children(self) -> _ {
|
||||
self.children
|
||||
}
|
||||
name: Interned<str>,
|
||||
children: Interned<[TraceDecl]>,
|
||||
ty: Bundle,
|
||||
flow: Flow,
|
||||
}),
|
||||
ModuleIO(TraceModuleIO {
|
||||
fn children(self) -> _ {
|
||||
self.children
|
||||
}
|
||||
name: Interned<str>,
|
||||
children: Interned<[TraceDecl]>,
|
||||
ty: Bundle,
|
||||
flow: Flow,
|
||||
}),
|
||||
Bundle(TraceBundle {
|
||||
fn children(self) -> _ {
|
||||
self.children
|
||||
}
|
||||
name: Interned<str>,
|
||||
children: Interned<[TraceDecl]>,
|
||||
ty: Bundle,
|
||||
flow: Flow,
|
||||
}),
|
||||
Array(TraceArray {
|
||||
fn children(self) -> _ {
|
||||
self.children
|
||||
}
|
||||
name: Interned<str>,
|
||||
children: Interned<[TraceDecl]>,
|
||||
ty: Array,
|
||||
flow: Flow,
|
||||
}),
|
||||
EnumWithFields(TraceEnumWithFields {
|
||||
fn children(self) -> _ {
|
||||
self.children
|
||||
}
|
||||
name: Interned<str>,
|
||||
children: Interned<[TraceDecl]>,
|
||||
ty: Enum,
|
||||
flow: Flow,
|
||||
}),
|
||||
}),
|
||||
#[kind = TraceScalarKind]
|
||||
Scalar(TraceScalar {
|
||||
fn id(self) -> TraceScalarId;
|
||||
UInt(TraceUInt {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: UInt,
|
||||
flow: Flow,
|
||||
}),
|
||||
SInt(TraceSInt {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: SInt,
|
||||
flow: Flow,
|
||||
}),
|
||||
Bool(TraceBool {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: Bool,
|
||||
flow: Flow,
|
||||
}),
|
||||
FieldlessEnum(TraceFieldlessEnum {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: Enum,
|
||||
flow: Flow,
|
||||
}),
|
||||
EnumDiscriminant(TraceEnumDiscriminant {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: Enum,
|
||||
flow: Flow,
|
||||
}),
|
||||
Clock(TraceClock {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: Clock,
|
||||
flow: Flow,
|
||||
}),
|
||||
SyncReset(TraceSyncReset {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: SyncReset,
|
||||
flow: Flow,
|
||||
}),
|
||||
AsyncReset(TraceAsyncReset {
|
||||
fn id(self) -> _ {
|
||||
self.id
|
||||
}
|
||||
id: TraceScalarId,
|
||||
name: Interned<str>,
|
||||
ty: AsyncReset,
|
||||
flow: Flow,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
|
||||
pub trait TraceWriterDecls: fmt::Debug + 'static + Sized {
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
type TraceWriter: TraceWriter<Error = Self::Error>;
|
||||
fn write_decls(self, module: TraceModule) -> Result<Self::TraceWriter, Self::Error>;
|
||||
}
|
||||
|
||||
trait TraceWriterDeclsDynTrait: fmt::Debug {
|
||||
fn write_decls_dyn(self: Box<Self>, module: TraceModule) -> std::io::Result<DynTraceWriter>;
|
||||
}
|
||||
|
||||
fn err_into_io<E: std::error::Error + Send + Sync + 'static>(e: E) -> std::io::Error {
|
||||
match <dyn std::error::Error + Send + Sync>::downcast::<std::io::Error>(Box::new(e)) {
|
||||
Ok(retval) => *retval,
|
||||
Err(e) => std::io::Error::other(e),
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TraceWriterDecls> TraceWriterDeclsDynTrait for T {
|
||||
fn write_decls_dyn(self: Box<Self>, module: TraceModule) -> std::io::Result<DynTraceWriter> {
|
||||
Ok(DynTraceWriter(Box::new(
|
||||
TraceWriterDecls::write_decls(*self, module).map_err(err_into_io)?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TraceWriter: fmt::Debug + 'static {
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
fn finish_init(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> {
|
||||
let _ = instant;
|
||||
Ok(())
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn close(self) -> Result<(), Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>;
|
||||
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>;
|
||||
fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
||||
if value {
|
||||
self.set_signal_uint(id, bits![1])
|
||||
} else {
|
||||
self.set_signal_uint(id, bits![0])
|
||||
}
|
||||
}
|
||||
fn set_signal_clock(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
||||
self.set_signal_bool(id, value)
|
||||
}
|
||||
fn set_signal_sync_reset(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
||||
self.set_signal_bool(id, value)
|
||||
}
|
||||
fn set_signal_async_reset(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
value: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.set_signal_bool(id, value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynTraceWriterDecls(Box<dyn TraceWriterDeclsDynTrait>);
|
||||
|
||||
impl fmt::Debug for DynTraceWriterDecls {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceWriterDecls for DynTraceWriterDecls {
|
||||
type Error = std::io::Error;
|
||||
type TraceWriter = DynTraceWriter;
|
||||
fn write_decls(self, module: TraceModule) -> Result<Self::TraceWriter, Self::Error> {
|
||||
self.0.write_decls_dyn(module)
|
||||
}
|
||||
}
|
||||
|
||||
trait TraceWriterDynTrait: fmt::Debug + 'static {
|
||||
fn finish_init_dyn(&mut self) -> std::io::Result<()>;
|
||||
fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()>;
|
||||
fn flush_dyn(&mut self) -> std::io::Result<()>;
|
||||
fn close_dyn(self: Box<Self>) -> std::io::Result<()>;
|
||||
fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>;
|
||||
fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>;
|
||||
fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
||||
fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
||||
fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>;
|
||||
fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool)
|
||||
-> std::io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: TraceWriter> TraceWriterDynTrait for T {
|
||||
fn finish_init_dyn(&mut self) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::finish_init(self).map_err(err_into_io)?)
|
||||
}
|
||||
fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::change_time_to(self, instant).map_err(err_into_io)?)
|
||||
}
|
||||
fn flush_dyn(&mut self) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::flush(self).map_err(err_into_io)?)
|
||||
}
|
||||
fn close_dyn(self: Box<Self>) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::close(*self).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_uint(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_sint(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_bool(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_clock(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_sync_reset(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
fn set_signal_async_reset_dyn(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
value: bool,
|
||||
) -> std::io::Result<()> {
|
||||
Ok(TraceWriter::set_signal_async_reset(self, id, value).map_err(err_into_io)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DynTraceWriter(Box<dyn TraceWriterDynTrait>);
|
||||
|
||||
impl fmt::Debug for DynTraceWriter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceWriter for DynTraceWriter {
|
||||
type Error = std::io::Error;
|
||||
fn finish_init(&mut self) -> Result<(), Self::Error> {
|
||||
self.0.finish_init_dyn()
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.0.flush_dyn()
|
||||
}
|
||||
fn close(self) -> Result<(), Self::Error> {
|
||||
self.0.close_dyn()
|
||||
}
|
||||
fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> {
|
||||
self.0.change_time_to_dyn(instant)
|
||||
}
|
||||
fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_uint_dyn(id, value)
|
||||
}
|
||||
fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_sint_dyn(id, value)
|
||||
}
|
||||
fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_bool_dyn(id, value)
|
||||
}
|
||||
fn set_signal_clock(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_clock_dyn(id, value)
|
||||
}
|
||||
fn set_signal_sync_reset(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_sync_reset_dyn(id, value)
|
||||
}
|
||||
fn set_signal_async_reset(
|
||||
&mut self,
|
||||
id: TraceScalarId,
|
||||
value: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.0.set_signal_async_reset_dyn(id, value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TraceWriterState<T: TraceWriterDecls> {
|
||||
Decls(T),
|
||||
Init(T::TraceWriter),
|
||||
Running(T::TraceWriter),
|
||||
Errored(Option<T::Error>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct SimTrace {
|
||||
id: TraceScalarId,
|
||||
}
|
||||
|
||||
struct SimulationImpl {
|
||||
state: interpreter::State,
|
||||
io: Expr<Bundle>,
|
||||
uninitialized_inputs: HashSet<Target>,
|
||||
io_targets: HashMap<Target, CompiledValue<CanonicalType>>,
|
||||
made_initial_step: bool,
|
||||
trace_decls: TraceModule,
|
||||
traces: Interned<[SimTrace]>,
|
||||
trace_writers: Vec<TraceWriterState<DynTraceWriterDecls>>,
|
||||
}
|
||||
|
||||
impl SimulationImpl {
|
||||
|
@ -2853,6 +3320,9 @@ impl SimulationImpl {
|
|||
uninitialized_inputs: HashSet::new(),
|
||||
io_targets: HashMap::new(),
|
||||
made_initial_step: false,
|
||||
trace_decls: compiled.trace_decls,
|
||||
traces: compiled.traces,
|
||||
trace_writers: vec![],
|
||||
};
|
||||
let io_target = Target::from(compiled.io);
|
||||
for (BundleField { name, .. }, value) in compiled
|
||||
|
@ -2991,6 +3461,72 @@ impl SimulationImpl {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn close_all_trace_writers(&mut self) -> std::io::Result<()> {
|
||||
let trace_writers = mem::take(&mut self.trace_writers);
|
||||
let mut retval = Ok(());
|
||||
let close_trace_writer =
|
||||
|trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
|
||||
TraceWriterState::Decls(v) => v.write_decls(self.trace_decls)?.close(),
|
||||
TraceWriterState::Init(v) => v.close(),
|
||||
TraceWriterState::Running(v) => v.close(),
|
||||
TraceWriterState::Errored(Some(e)) => Err(e),
|
||||
TraceWriterState::Errored(None) => Ok(()),
|
||||
};
|
||||
for trace_writer in trace_writers {
|
||||
retval = retval.and(close_trace_writer(trace_writer));
|
||||
}
|
||||
retval
|
||||
}
|
||||
fn close(mut self) -> std::io::Result<()> {
|
||||
self.close_all_trace_writers()
|
||||
}
|
||||
fn flush_traces(&mut self) -> std::io::Result<()> {
|
||||
let flush_trace_writer = |trace_writer: TraceWriterState<DynTraceWriterDecls>| {
|
||||
Ok(Some(match trace_writer {
|
||||
TraceWriterState::Decls(v) => {
|
||||
let mut v = v.write_decls(self.trace_decls)?;
|
||||
v.flush()?;
|
||||
TraceWriterState::Init(v)
|
||||
}
|
||||
TraceWriterState::Init(mut v) => {
|
||||
v.flush()?;
|
||||
TraceWriterState::Init(v)
|
||||
}
|
||||
TraceWriterState::Running(mut v) => {
|
||||
v.flush()?;
|
||||
TraceWriterState::Running(v)
|
||||
}
|
||||
TraceWriterState::Errored(Some(e)) => return Err(e),
|
||||
TraceWriterState::Errored(None) => return Ok(None),
|
||||
}))
|
||||
};
|
||||
let mut retval = Ok(());
|
||||
for trace_writer in &mut self.trace_writers {
|
||||
*trace_writer = match flush_trace_writer(mem::replace(
|
||||
trace_writer,
|
||||
TraceWriterState::Errored(None),
|
||||
)) {
|
||||
Ok(Some(v)) => v,
|
||||
Ok(None) => TraceWriterState::Errored(None),
|
||||
Err(e) => {
|
||||
if retval.is_ok() {
|
||||
retval = Err(e);
|
||||
TraceWriterState::Errored(None)
|
||||
} else {
|
||||
TraceWriterState::Errored(Some(e))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SimulationImpl {
|
||||
fn drop(&mut self) {
|
||||
self.close_all_trace_writers()
|
||||
.expect("error closing trace writers");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Simulation<T: BundleType> {
|
||||
|
@ -3048,6 +3584,9 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
|
|||
uninitialized_inputs,
|
||||
io_targets,
|
||||
made_initial_step,
|
||||
trace_decls,
|
||||
traces,
|
||||
trace_writers,
|
||||
},
|
||||
io,
|
||||
} = self;
|
||||
|
@ -3060,6 +3599,9 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
|
|||
)
|
||||
.field("io_targets", &SortedMapDebug(io_targets))
|
||||
.field("made_initial_step", made_initial_step)
|
||||
.field("trace_decls", trace_decls)
|
||||
.field("traces", traces)
|
||||
.field("trace_writers", trace_writers)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -3068,6 +3610,12 @@ impl<T: BundleType> Simulation<T> {
|
|||
pub fn new(module: Interned<Module<T>>) -> Self {
|
||||
Self::from_compiled(Compiled::new(module))
|
||||
}
|
||||
pub fn flush_traces(&mut self) -> std::io::Result<()> {
|
||||
self.sim_impl.flush_traces()
|
||||
}
|
||||
pub fn close(self) -> std::io::Result<()> {
|
||||
self.sim_impl.close()
|
||||
}
|
||||
pub fn canonical(self) -> Simulation<Bundle> {
|
||||
let Self { sim_impl, io } = self;
|
||||
Simulation {
|
||||
|
|
165
crates/fayalite/src/sim/time.rs
Normal file
165
crates/fayalite/src/sim/time.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use std::{fmt, time::Duration};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct SimInstant {
|
||||
time_since_start: SimDuration,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SimInstant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.time_since_start.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct SimDuration {
|
||||
attos: u128,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub struct SimDurationParts {
|
||||
pub attos: u16,
|
||||
pub femtos: u16,
|
||||
pub picos: u16,
|
||||
pub nanos: u16,
|
||||
pub micros: u16,
|
||||
pub millis: u16,
|
||||
pub secs: u128,
|
||||
}
|
||||
|
||||
macro_rules! impl_duration_units {
|
||||
(
|
||||
$(
|
||||
#[unit_const = $UNIT:ident, from_units = $from_units:ident, units = $units:ident, suffix = $suffix:literal]
|
||||
const $log10_units_per_sec:ident: u32 = $log10_units_per_sec_value:expr;
|
||||
)*
|
||||
) => {
|
||||
impl SimDuration {
|
||||
$(
|
||||
const $log10_units_per_sec: u32 = $log10_units_per_sec_value;
|
||||
pub const fn $from_units($units: u128) -> Self {
|
||||
Self::from_units_helper::<{ Self::$log10_units_per_sec }>($units)
|
||||
}
|
||||
)*
|
||||
pub const fn into_parts(mut self) -> SimDurationParts {
|
||||
$(
|
||||
let $units = self.attos / const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
|
||||
self.attos %= const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
|
||||
)*
|
||||
SimDurationParts {
|
||||
$($units: $units as _,)*
|
||||
}
|
||||
}
|
||||
pub const fn from_parts_checked(parts: SimDurationParts) -> Option<Self> {
|
||||
let attos = 0u128;
|
||||
$(
|
||||
let Some(product) = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) }.checked_mul(parts.$units as u128) else {
|
||||
return None;
|
||||
};
|
||||
let Some(attos) = attos.checked_add(product) else {
|
||||
return None;
|
||||
};
|
||||
)*
|
||||
Some(Self {
|
||||
attos,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SimDuration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let ilog10_attos = match self.attos.checked_ilog10() {
|
||||
Some(v) => v,
|
||||
None => Self::LOG10_ATTOS_PER_SEC,
|
||||
};
|
||||
let (suffix, int, fraction, fraction_digits) =
|
||||
match Self::LOG10_ATTOS_PER_SEC.saturating_sub(ilog10_attos) {
|
||||
$(
|
||||
..=Self::$log10_units_per_sec => {
|
||||
let divisor = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
|
||||
(
|
||||
$suffix,
|
||||
self.attos / divisor,
|
||||
self.attos % divisor,
|
||||
(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) as usize,
|
||||
)
|
||||
},
|
||||
)*
|
||||
_ => unreachable!(),
|
||||
};
|
||||
write!(f, "{int}")?;
|
||||
if fraction != 0 {
|
||||
write!(f, ".{fraction:0fraction_digits$}")?;
|
||||
}
|
||||
write!(f, " {suffix}")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_duration_debug() {
|
||||
$(
|
||||
assert_eq!(
|
||||
format!("{:?}", SimDuration::$from_units(123)),
|
||||
concat!("123 ", $suffix)
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{:?}", SimDuration::$from_units(1)),
|
||||
concat!("1 ", $suffix),
|
||||
);
|
||||
let mut v = SimDuration::$from_units(1);
|
||||
if v.attos < 1 << 53 {
|
||||
v.attos += 1;
|
||||
assert_eq!(
|
||||
format!("{v:?}"),
|
||||
format!("{} {}", v.attos as f64 / 10.0f64.powf((SimDuration::LOG10_ATTOS_PER_SEC - SimDuration::$log10_units_per_sec) as f64), $suffix),
|
||||
"1 {} + 1 as == {} as", $suffix, v.attos,
|
||||
);
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_duration_units! {
|
||||
#[unit_const = SECOND, from_units = from_secs, units = secs, suffix = "s"]
|
||||
const LOG10_SECS_PER_SEC: u32 = 0;
|
||||
#[unit_const = MILLISECOND, from_units = from_millis, units = millis, suffix = "ms"]
|
||||
const LOG10_MILLIS_PER_SEC: u32 = 3;
|
||||
#[unit_const = MICROSECOND, from_units = from_micros, units = micros, suffix = "μs"]
|
||||
const LOG10_MICROS_PER_SEC: u32 = 6;
|
||||
#[unit_const = NANOSECOND, from_units = from_nanos, units = nanos, suffix = "ns"]
|
||||
const LOG10_NANOS_PER_SEC: u32 = 9;
|
||||
#[unit_const = PICOSECOND, from_units = from_picos, units = picos, suffix = "ps"]
|
||||
const LOG10_PICOS_PER_SEC: u32 = 12;
|
||||
#[unit_const = FEMTOSECOND, from_units = from_femtos, units = femtos, suffix = "fs"]
|
||||
const LOG10_FEMTOS_PER_SEC: u32 = 15;
|
||||
#[unit_const = ATTOSECOND, from_units = from_attos, units = attos, suffix = "as"]
|
||||
const LOG10_ATTOS_PER_SEC: u32 = 18;
|
||||
}
|
||||
|
||||
impl SimDuration {
|
||||
const fn from_units_helper<const UNITS_PER_SEC: u32>(units: u128) -> Self {
|
||||
let Some(attos) =
|
||||
units.checked_mul(const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - UNITS_PER_SEC) })
|
||||
else {
|
||||
panic!("duration too big");
|
||||
};
|
||||
Self { attos }
|
||||
}
|
||||
pub const ZERO: SimDuration = SimDuration::from_secs(0);
|
||||
pub const fn from_parts(parts: SimDurationParts) -> Self {
|
||||
match Self::from_parts_checked(parts) {
|
||||
Some(v) => v,
|
||||
None => panic!("duration too big"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Duration> for SimDuration {
|
||||
fn from(duration: Duration) -> Self {
|
||||
Self::from_nanos(duration.as_nanos())
|
||||
}
|
||||
}
|
|
@ -115,6 +115,12 @@ fn test_connect_const() {
|
|||
},
|
||||
},
|
||||
made_initial_step: true,
|
||||
trace_decls: TraceModule {
|
||||
name: "connect_const",
|
||||
children: [],
|
||||
},
|
||||
traces: [],
|
||||
trace_writers: [],
|
||||
}"# {
|
||||
panic!();
|
||||
}
|
||||
|
@ -673,6 +679,12 @@ fn test_mod1() {
|
|||
},
|
||||
},
|
||||
made_initial_step: true,
|
||||
trace_decls: TraceModule {
|
||||
name: "mod1",
|
||||
children: [],
|
||||
},
|
||||
traces: [],
|
||||
trace_writers: [],
|
||||
}"# {
|
||||
panic!();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue