From c7a47b1d2edc0c6a5e6851fb6650863dfd9a1975 Mon Sep 17 00:00:00 2001 From: Kevin Laeufer Date: Wed, 9 Oct 2024 13:38:17 -0400 Subject: [PATCH] Add FST output for cider (#2299) This adds an option to generate a wavedump when executing a Calyx program with cider. FST generation is based on the highly experimental `fst-writer` library. Currently, the FSTs produced work on Surfer but crash GTKWave. --- .gitignore | 2 + Cargo.lock | 73 ++++-- interp/Cargo.toml | 1 + interp/src/debugger/debugger_core.rs | 11 +- interp/src/flatten/flat_ir/component.rs | 8 +- .../src/flatten/flat_ir/control/translator.rs | 12 +- interp/src/flatten/structures/context.rs | 10 +- .../src/flatten/structures/environment/env.rs | 30 ++- .../src/flatten/structures/environment/mod.rs | 1 + .../flatten/structures/environment/wave.rs | 219 ++++++++++++++++++ interp/src/flatten/structures/indexed_map.rs | 14 +- interp/src/main.rs | 13 +- 12 files changed, 344 insertions(+), 50 deletions(-) create mode 100644 interp/src/flatten/structures/environment/wave.rs diff --git a/.gitignore b/.gitignore index 76b8b9a9b4..29eaadf2e0 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,8 @@ __pycache__ !.vscode/launch.json !.vscode/tasks.json +.idea/ + # cocotb artifacts tests/xilinx/cocotb/**/hdl sim_build/ diff --git a/Cargo.lock b/Cargo.lock index 3f5171fb67..43f9e5f80d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1251,6 +1251,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "fst-writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17dac4299fb4b037b8ce0d68b9e0ce7c61e5fbb038251f8f28148528f925b69" +dependencies = [ + "lz4_flex", + "thiserror", + "twox-hash", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1570,9 +1581,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "967d6dd42f16dbf0eb8040cb9e477933562684d3918f7d253f2ff9087fb3e7a3" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1616,6 +1627,7 @@ dependencies = [ "calyx-utils", "ciborium", "fraction", + "fst-writer", "itertools 0.11.0", "lazy_static", "num-bigint", @@ -1768,6 +1780,15 @@ dependencies = [ "url", ] +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + [[package]] name = "manifest-dir-macros" version = "0.1.18" @@ -2107,7 +2128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.4", + "indexmap 2.5.0", ] [[package]] @@ -2215,9 +2236,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", @@ -2343,9 +2364,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -2616,9 +2637,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2643,7 +2664,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.4", + "indexmap 2.5.0", "serde", "serde_derive", "serde_json", @@ -2975,18 +2996,18 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -3124,20 +3145,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.4", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", @@ -3271,6 +3292,16 @@ dependencies = [ "regex", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typed-arena" version = "2.0.2" @@ -3660,9 +3691,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.3" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e19b97e00a4d3db3cdb9b53c8c5f87151b5689b82cc86c2848cbdcccb2689b" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/interp/Cargo.toml b/interp/Cargo.toml index db7d69353b..26636f60e9 100644 --- a/interp/Cargo.toml +++ b/interp/Cargo.toml @@ -47,6 +47,7 @@ btor2i = { path = "../tools/btor2/btor2i" } ciborium = "0.2.2" baa = { version = "0.6.0", features = ["bigint", "serde1", "fraction1"] } +fst-writer = "0.2.0" [dev-dependencies] proptest = "1.0.0" diff --git a/interp/src/debugger/debugger_core.rs b/interp/src/debugger/debugger_core.rs index 6b34dc8ef7..b9e4ffe81e 100644 --- a/interp/src/debugger/debugger_core.rs +++ b/interp/src/debugger/debugger_core.rs @@ -93,7 +93,8 @@ impl OwnedDebugger { false, )?; - let debugger: Debugger> = Self::new(Rc::new(ctx), &None)?; + let debugger: Debugger> = + Self::new(Rc::new(ctx), &None, &None)?; Ok((debugger, map)) } @@ -104,9 +105,13 @@ impl + Clone> Debugger { pub fn new( program_context: C, data_file: &Option, + wave_file: &Option, ) -> InterpreterResult { - let mut interpreter = - Simulator::build_simulator(program_context.clone(), data_file)?; + let mut interpreter = Simulator::build_simulator( + program_context.clone(), + data_file, + wave_file, + )?; interpreter.converge()?; Ok(Self { diff --git a/interp/src/flatten/flat_ir/component.rs b/interp/src/flatten/flat_ir/component.rs index 363e300bda..1625e618ed 100644 --- a/interp/src/flatten/flat_ir/component.rs +++ b/interp/src/flatten/flat_ir/component.rs @@ -173,7 +173,7 @@ impl ComponentCore { #[derive(Debug, Clone)] /// Other information about a component definition. This is not on the hot path /// and is instead needed primarily during setup and error reporting. -pub struct AuxillaryComponentInfo { +pub struct AuxiliaryComponentInfo { /// Name of the component. pub name: Identifier, /// The input/output signature of this component. This could probably be @@ -193,14 +193,14 @@ pub struct AuxillaryComponentInfo { SparseMap, } -impl Default for AuxillaryComponentInfo { +impl Default for AuxiliaryComponentInfo { fn default() -> Self { Self::new_with_name(Identifier::get_default_id()) } } -impl AuxillaryComponentInfo { - /// Creates a new [`AuxillaryComponentInfo`] with the given name. And +impl AuxiliaryComponentInfo { + /// Creates a new [`AuxiliaryComponentInfo`] with the given name. And /// default values elsewhere. pub fn new_with_name(id: Identifier) -> Self { Self { diff --git a/interp/src/flatten/flat_ir/control/translator.rs b/interp/src/flatten/flat_ir/control/translator.rs index 842c7e27c0..8e8a1a70e1 100644 --- a/interp/src/flatten/flat_ir/control/translator.rs +++ b/interp/src/flatten/flat_ir/control/translator.rs @@ -7,7 +7,7 @@ use crate::{ flatten::{ flat_ir::{ cell_prototype::{CellPrototype, ConstantType}, - component::{AuxillaryComponentInfo, ComponentCore}, + component::{AuxiliaryComponentInfo, ComponentCore}, flatten_trait::{flatten_tree, FlattenTree, SingleHandle}, prelude::{ Assignment, AssignmentIdx, CellRef, CombGroup, CombGroupIdx, @@ -125,7 +125,7 @@ fn translate_component( ctx: &mut Context, component_id_map: &mut ComponentMapper, ) -> ComponentIdx { - let mut auxillary_component_info = AuxillaryComponentInfo::new_with_name( + let mut auxillary_component_info = AuxiliaryComponentInfo::new_with_name( ctx.secondary.string_table.insert(comp.name), ); @@ -275,7 +275,7 @@ fn translate_component( fn insert_port( secondary_ctx: &mut SecondaryContext, - aux: &mut AuxillaryComponentInfo, + aux: &mut AuxiliaryComponentInfo, port: &RRC, port_type: ContainmentType, ) -> PortRef { @@ -297,7 +297,7 @@ fn insert_port( fn insert_cell( secondary_ctx: &mut SecondaryContext, - aux: &mut AuxillaryComponentInfo, + aux: &mut AuxiliaryComponentInfo, cell: &RRC, layout: &mut Layout, comp_id: ComponentIdx, @@ -357,7 +357,7 @@ pub struct Layout { fn compute_local_layout( comp: &cir::Component, ctx: &mut Context, - aux: &mut AuxillaryComponentInfo, + aux: &mut AuxiliaryComponentInfo, component_id_map: &ComponentMapper, ) -> Layout { let comp_id = ctx.primary.components.peek_next_idx(); @@ -541,7 +541,7 @@ impl FlattenTree for cir::Control { type IdxType = ControlIdx; - type AuxillaryData = (GroupMapper, Layout, Context, AuxillaryComponentInfo); + type AuxillaryData = (GroupMapper, Layout, Context, AuxiliaryComponentInfo); fn process_element<'data>( &'data self, diff --git a/interp/src/flatten/structures/context.rs b/interp/src/flatten/structures/context.rs index 4592b23c73..2a4aad88a5 100644 --- a/interp/src/flatten/structures/context.rs +++ b/interp/src/flatten/structures/context.rs @@ -3,7 +3,7 @@ use std::ops::Index; use crate::flatten::flat_ir::{ cell_prototype::CellPrototype, component::{ - AssignmentDefinitionLocation, AuxillaryComponentInfo, ComponentCore, + AssignmentDefinitionLocation, AuxiliaryComponentInfo, ComponentCore, ComponentMap, }, identifier::IdMap, @@ -23,7 +23,7 @@ use crate::flatten::flat_ir::{ use super::{ index_trait::{IndexRange, IndexRef}, - indexed_map::{AuxillaryMap, IndexedMap}, + indexed_map::{AuxiliaryMap, IndexedMap}, printer::Printer, }; @@ -116,8 +116,8 @@ pub struct SecondaryContext { pub local_cell_defs: IndexedMap, /// ref-cell definitions pub ref_cell_defs: IndexedMap, - /// auxillary information for components - pub comp_aux_info: AuxillaryMap, + /// auxiliary information for components + pub comp_aux_info: AuxiliaryMap, } impl Index for SecondaryContext { @@ -161,7 +161,7 @@ impl Index for SecondaryContext { } impl Index for SecondaryContext { - type Output = AuxillaryComponentInfo; + type Output = AuxiliaryComponentInfo; fn index(&self, index: ComponentIdx) -> &Self::Output { &self.comp_aux_info[index] diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index 619cc0f020..df29abaae5 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -6,6 +6,7 @@ use super::{ program_counter::{PcMaps, ProgramCounter, WithEntry}, traverser::{Path, TraversalError}, }; +use crate::flatten::structures::environment::wave::WaveWriter; use crate::{ errors::{BoxedInterpreterError, InterpreterError, InterpreterResult}, flatten::{ @@ -1179,11 +1180,23 @@ impl + Clone> Environment { /// the environment to avoid confusion pub struct Simulator + Clone> { env: Environment, + wave: Option, } impl + Clone> Simulator { - pub fn new(env: Environment) -> Self { - let mut output = Self { env }; + pub fn new( + env: Environment, + wave_file: &Option, + ) -> Self { + // open the wave form file and declare all signals + let wave = + wave_file.as_ref().map(|p| match WaveWriter::open(p, &env) { + Ok(w) => w, + Err(err) => { + todo!("deal more gracefully with error: {err:?}") + } + }); + let mut output = Self { env, wave }; output.set_root_go_high(); output } @@ -1208,6 +1221,7 @@ impl + Clone> Simulator { pub fn build_simulator( ctx: C, data_file: &Option, + wave_file: &Option, ) -> Result { let data_dump = data_file .as_ref() @@ -1218,7 +1232,7 @@ impl + Clone> Simulator { // flip to a result of an option .map_or(Ok(None), |res| res.map(Some))?; - Ok(Simulator::new(Environment::new(ctx, data_dump))) + Ok(Simulator::new(Environment::new(ctx, data_dump), wave_file)) } pub fn is_group_running(&self, group_idx: GroupIdx) -> bool { @@ -1872,9 +1886,17 @@ impl + Clone> Simulator { /// Evaluate the entire program pub fn run_program(&mut self) -> InterpreterResult<()> { + let mut time = 0; while !self.is_done() { + if let Some(wave) = self.wave.as_mut() { + wave.write_values(time, &self.env.ports)?; + } // self.print_pc(); - self.step()? + self.step()?; + time += 1; + } + if let Some(wave) = self.wave.as_mut() { + wave.write_values(time, &self.env.ports)?; } Ok(()) } diff --git a/interp/src/flatten/structures/environment/mod.rs b/interp/src/flatten/structures/environment/mod.rs index 706e8b8755..2f48b36d87 100644 --- a/interp/src/flatten/structures/environment/mod.rs +++ b/interp/src/flatten/structures/environment/mod.rs @@ -2,6 +2,7 @@ mod assignments; mod env; mod program_counter; mod traverser; +mod wave; pub use env::{Environment, PortMap, Simulator}; pub use traverser::{Path, PathError, PathResolution}; diff --git a/interp/src/flatten/structures/environment/wave.rs b/interp/src/flatten/structures/environment/wave.rs new file mode 100644 index 0000000000..1019433a09 --- /dev/null +++ b/interp/src/flatten/structures/environment/wave.rs @@ -0,0 +1,219 @@ +// Copyright 2024 Cornell University +// released under MIT License +// author: Kevin Laeufer + +use crate::flatten::flat_ir::cell_prototype::CellPrototype; +use crate::flatten::flat_ir::prelude::*; +use crate::flatten::structures::context::Context; +use crate::flatten::structures::environment::{Environment, PortMap}; +use crate::flatten::structures::indexed_map::AuxiliaryMap; +use baa::BitVecOps; +use fst_writer::*; + +#[derive(Debug, thiserror::Error)] +pub enum WaveError { + #[error("FST write operation failed.")] + Fst(#[from] fst_writer::FstWriteError), +} + +pub type Result = std::result::Result; + +impl From for crate::errors::InterpreterError { + fn from(value: WaveError) -> Self { + Self::GenericError(value.to_string()) + } +} + +pub struct WaveWriter { + // `writer` will be `None` after this struct is dropped. + writer: Option>>, + port_map: PortToSignalMap, +} + +impl WaveWriter { + pub fn open + Clone>( + file_path: &std::path::PathBuf, + env: &Environment, + ) -> Result { + let info = FstInfo { + start_time: 0, + timescale_exponent: 0, + version: "Cider 2".to_string(), + date: "today".to_string(), + file_type: FstFileType::Verilog, + }; + let mut writer = open_fst(file_path, &info)?; + let port_map = declare_signals(&mut writer, env)?; + let writer = writer.finish()?; + Ok(Self { + writer: Some(writer), + port_map, + }) + } + + pub fn write_values(&mut self, time: u64, values: &PortMap) -> Result<()> { + let writer = self.writer.as_mut().unwrap(); + writer.time_change(time)?; + for (port_id, maybe_signal_id) in self.port_map.iter() { + if let Some(signal_id) = maybe_signal_id { + match values[port_id].val() { + None => { + writer.signal_change(*signal_id, "x".as_bytes())?; + } + Some(value) => { + writer.signal_change( + *signal_id, + value.to_bit_str().as_bytes(), + )?; + } + } + } + } + Ok(()) + } +} + +impl Drop for WaveWriter { + fn drop(&mut self) { + let writer = std::mem::take(&mut self.writer); + if let Some(writer) = writer { + writer.finish().unwrap(); + } + } +} + +enum Todo { + /// instance name, instance id + OpenScope(String, GlobalCellIdx), + CloseScope, +} + +type PortToSignalMap = AuxiliaryMap>; + +fn declare_signals< + C: AsRef + Clone, + W: std::io::Write + std::io::Seek, +>( + out: &mut FstHeaderWriter, + env: &Environment, +) -> Result { + let ctx = env.ctx(); + let mut port_map: PortToSignalMap = PortToSignalMap::new(); + let root_idx = Environment::::get_root(); + let root_comp = &ctx.secondary[ctx.entry_point]; + let root_name = ctx.lookup_name(root_comp.name).clone(); + let mut todo = vec![Todo::OpenScope(root_name, root_idx)]; + while let Some(component) = todo.pop() { + match component { + Todo::OpenScope(name, id) => { + todo.push(Todo::CloseScope); + declare_component( + out, + env, + &mut todo, + &mut port_map, + id, + name, + )?; + } + Todo::CloseScope => { + out.up_scope()?; + } + } + } + Ok(port_map) +} + +fn declare_component< + C: AsRef + Clone, + W: std::io::Write + std::io::Seek, +>( + out: &mut FstHeaderWriter, + env: &Environment, + todo: &mut Vec, + port_map: &mut PortToSignalMap, + component_cell_idx: GlobalCellIdx, + instance_name: String, +) -> Result<()> { + let ctx = env.ctx(); + let instance = env.cells[component_cell_idx].as_comp().unwrap(); + let component = &ctx.secondary[instance.comp_id]; + + let component_name = ctx.lookup_name(component.name); + out.scope(instance_name, component_name, FstScopeType::Module)?; + + // component ports + declare_ports( + out, + env, + component.inputs().chain(component.outputs()).map(|local| { + ( + component.port_offset_map[local], + &instance.index_bases + local, + ) + }), + port_map, + )?; + + // child components + for (local_offset, cell) in component.cell_offset_map.iter() { + let cell = &ctx.secondary.local_cell_defs[*cell]; + let cell_idx = &instance.index_bases + local_offset; + if cell.prototype.is_component() { + let name = ctx.lookup_name(cell.name).clone(); + todo.push(Todo::OpenScope(name, cell_idx)); + } else { + if matches!(&cell.prototype, CellPrototype::Constant { .. }) { + // skip constants + continue; + } + let instance_name = ctx.lookup_name(cell.name); + let primitive_name = ""; // TODO + out.scope(instance_name, primitive_name, FstScopeType::Module)?; + declare_ports( + out, + env, + cell.ports.iter().map(|local| { + ( + component.port_offset_map[local], + &instance.index_bases + local, + ) + }), + port_map, + )?; + out.up_scope()?; // primitives do not have any children + } + } + Ok(()) +} + +fn declare_ports< + C: AsRef + Clone, + I: Iterator, + W: std::io::Write + std::io::Seek, +>( + out: &mut FstHeaderWriter, + env: &Environment, + ports: I, + port_map: &mut PortToSignalMap, +) -> Result<()> { + let ctx = env.ctx(); + for (port_id, global_idx) in ports { + let port = ctx.secondary.local_port_defs.get(port_id).unwrap(); + let name = ctx.lookup_name(port.name); + + let alias = *port_map.get(global_idx); + let signal_type = FstSignalType::bit_vec(port.width as u32); + let id = out.var( + name, + signal_type, + FstVarType::Logic, + FstVarDirection::Implicit, + alias, + )?; + if alias.is_none() { + port_map.insert(global_idx, Some(id)); + } + } + Ok(()) +} diff --git a/interp/src/flatten/structures/indexed_map.rs b/interp/src/flatten/structures/indexed_map.rs index f744799345..137788b09b 100644 --- a/interp/src/flatten/structures/indexed_map.rs +++ b/interp/src/flatten/structures/indexed_map.rs @@ -180,7 +180,7 @@ where } #[derive(Debug)] -pub struct AuxillaryMap +pub struct AuxiliaryMap where K: IndexRef, D: Clone, @@ -192,7 +192,7 @@ where // NOTE TO SELF: do not implement IndexMut -impl Index for AuxillaryMap +impl Index for AuxiliaryMap where K: IndexRef, D: Clone, @@ -208,7 +208,7 @@ where } } -impl AuxillaryMap +impl AuxiliaryMap where K: IndexRef, D: Clone, @@ -250,9 +250,13 @@ where self.data[index.index()] = item; } } + + pub fn iter(&self) -> impl Iterator { + self.data.iter().enumerate().map(|(k, v)| (K::new(k), v)) + } } -impl AuxillaryMap +impl AuxiliaryMap where K: IndexRef, D: Clone + Default, @@ -274,7 +278,7 @@ where } } -impl Default for AuxillaryMap +impl Default for AuxiliaryMap where K: IndexRef, D: Clone + Default, diff --git a/interp/src/main.rs b/interp/src/main.rs index ce6a0dbf29..3776615b5f 100644 --- a/interp/src/main.rs +++ b/interp/src/main.rs @@ -68,6 +68,10 @@ pub struct Opts { #[argh(switch, long = "all-memories")] dump_all_memories: bool, + /// optional wave file output path + #[argh(option, long = "wave-file")] + pub wave_file: Option, + #[argh(subcommand)] mode: Option, } @@ -119,7 +123,11 @@ fn main() -> InterpreterResult<()> { match &command { Command::Interpret(_) => { - let mut sim = Simulator::build_simulator(&i_ctx, &opts.data_file)?; + let mut sim = Simulator::build_simulator( + &i_ctx, + &opts.data_file, + &opts.wave_file, + )?; sim.run_program()?; @@ -132,7 +140,8 @@ fn main() -> InterpreterResult<()> { Command::Debug(_) => { let mut info: Option = None; loop { - let debugger = Debugger::new(&i_ctx, &opts.data_file)?; + let debugger = + Debugger::new(&i_ctx, &opts.data_file, &opts.wave_file)?; let result = debugger.main_loop(info)?; match result {