From bb860d54ccba824a26427b02cc5888dafd9745de Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:47:48 -0700 Subject: [PATCH] add command line options for selecting which transforms to apply when generating firrtl --- crates/fayalite/src/cli.rs | 6 +- crates/fayalite/src/firrtl.rs | 133 ++++++++++++++++-- .../src/module/transform/simplify_enums.rs | 10 +- 3 files changed, 137 insertions(+), 12 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 1dbf85f..c171c22 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -2,7 +2,7 @@ // See Notices.txt for copyright information use crate::{ bundle::{Bundle, BundleType}, - firrtl, + firrtl::{self, ExportOptions}, intern::Interned, module::Module, util::streaming_read_utf8::streaming_read_utf8, @@ -98,6 +98,8 @@ impl BaseArgs { pub struct FirrtlArgs { #[command(flatten)] pub base: BaseArgs, + #[command(flatten)] + pub export_options: ExportOptions, } #[derive(Debug)] @@ -118,7 +120,7 @@ impl FirrtlArgs { fn run_impl(&self, top_module: Module) -> Result { let firrtl::FileBackend { top_fir_file_stem, .. - } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?; + } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module, self.export_options)?; Ok(FirrtlOutput { file_stem: top_fir_file_stem.expect( "export is known to set the file stem from the circuit name if not provided", diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index b8ba89a..ba37bcc 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -18,10 +18,14 @@ use crate::{ intern::{Intern, Interned}, memory::{Mem, PortKind, PortName, ReadUnderWrite}, module::{ - transform::simplify_memories::simplify_memories, AnnotatedModuleIO, Block, - ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody, - NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, - StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + transform::{ + simplify_enums::{simplify_enums, SimplifyEnumsError, SimplifyEnumsKind}, + simplify_memories::simplify_memories, + }, + AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, + ExternModuleParameterValue, Module, ModuleBody, NameId, NormalModuleBody, Stmt, + StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, StmtIf, StmtInstance, StmtMatch, + StmtReg, StmtWire, }, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, @@ -32,6 +36,7 @@ use crate::{ }, }; use bitvec::slice::BitSlice; +use clap::value_parser; use hashbrown::{HashMap, HashSet}; use num_traits::Signed; use serde::Serialize; @@ -476,6 +481,7 @@ trait WrappedFileBackendTrait { circuit_name: String, contents: String, ) -> Result<(), WrappedError>; + fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError; } struct WrappedFileBackend { @@ -533,6 +539,11 @@ impl WrappedFileBackendTrait for WrappedFileBackend { WrappedError }) } + + fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError { + self.error = Err(error.into()); + WrappedError + } } #[derive(Clone)] @@ -2225,7 +2236,7 @@ impl<'a> Exporter<'a> { } pub trait FileBackendTrait { - type Error; + type Error: From; type Path: AsRef + fmt::Debug + ?Sized; type PathBuf: AsRef + fmt::Debug; fn path_to_string(&mut self, path: &Self::Path) -> Result; @@ -2370,6 +2381,7 @@ impl Default for TestBackendPrivate { pub struct TestBackend { pub files: BTreeMap, pub error_after: Option, + pub options: ExportOptions, #[doc(hidden)] /// `#[non_exhaustive]` except allowing struct update syntax pub __private: TestBackendPrivate, @@ -2380,6 +2392,7 @@ impl fmt::Debug for TestBackend { let Self { files, error_after, + options, __private: TestBackendPrivate { module_var_name }, } = self; writeln!( @@ -2394,6 +2407,9 @@ impl fmt::Debug for TestBackend { if *error_after != Option::default() { writeln!(f, " error_after: {error_after:?},")?; } + if *options != ExportOptions::default() { + writeln!(f, " options: {options:?},")?; + } write!(f, " }};") } } @@ -2409,6 +2425,12 @@ impl fmt::Display for TestBackendError { impl Error for TestBackendError {} +impl From for TestBackendError { + fn from(value: SimplifyEnumsError) -> Self { + TestBackendError(value.to_string()) + } +} + impl TestBackend { #[track_caller] pub fn step_error_after(&mut self, args: &dyn fmt::Debug) -> Result<(), TestBackendError> { @@ -2465,9 +2487,20 @@ impl FileBackendTrait for TestBackend { fn export_impl( file_backend: &mut dyn WrappedFileBackendTrait, - top_module: Interned>, + mut top_module: Interned>, + options: ExportOptions, ) -> Result<(), WrappedError> { - let top_module = simplify_memories(top_module); + let ExportOptions { + simplify_memories: do_simplify_memories, + simplify_enums: do_simplify_enums, + } = options; + if let Some(kind) = do_simplify_enums { + top_module = + simplify_enums(top_module, kind).map_err(|e| file_backend.simplify_enums_error(e))?; + } + if do_simplify_memories { + top_module = simplify_memories(top_module); + } let indent_depth = Cell::new(0); let mut global_ns = Namespace::default(); let circuit_name = global_ns.get(top_module.name_id()); @@ -2488,20 +2521,102 @@ fn export_impl( .run(top_module) } +#[derive(Clone)] +struct OptionSimplifyEnumsKindValueParser; + +impl OptionSimplifyEnumsKindValueParser { + const NONE_NAME: &'static str = "off"; +} + +impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser { + type Value = Option; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + if value == Self::NONE_NAME { + Ok(None) + } else { + Ok(Some( + value_parser!(SimplifyEnumsKind).parse_ref(cmd, arg, value)?, + )) + } + } + + fn possible_values( + &self, + ) -> Option + '_>> { + Some(Box::new( + [Self::NONE_NAME.into()] + .into_iter() + .chain(value_parser!(SimplifyEnumsKind).possible_values()?) + .collect::>() + .into_iter(), + )) + } +} + +#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub struct ExportOptions { + #[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)] + pub simplify_memories: bool, + #[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = OptionSimplifyEnumsKindValueParser::NONE_NAME)] + pub simplify_enums: std::option::Option, +} + +impl fmt::Debug for ExportOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ExportOptions {")?; + if f.alternate() { + f.write_str("\n")?; + } + let mut sep = if f.alternate() { "\n " } else { " " }; + let comma_sep = if f.alternate() { ",\n " } else { ", " }; + let default = ExportOptions::default(); + if self.simplify_memories != default.simplify_memories { + write!(f, "{sep}simplify_memories: {:?}", self.simplify_memories)?; + sep = comma_sep; + } + if self.simplify_enums != default.simplify_enums { + write!(f, "{sep}simplify_enums: {:?}", self.simplify_enums)?; + sep = comma_sep; + } + write!( + f, + "{sep}..ExportOptions::default(){}", + if f.alternate() { "\n}" } else { " }" } + ) + } +} + +impl Default for ExportOptions { + fn default() -> Self { + Self { + simplify_memories: true, + simplify_enums: None, + } + } +} + pub fn export( file_backend: B, top_module: &Module, + options: ExportOptions, ) -> Result { let top_module = Intern::intern_sized(top_module.canonical()); WrappedFileBackend::with(file_backend, |file_backend| { - export_impl(file_backend, top_module) + export_impl(file_backend, top_module, options) }) } #[doc(hidden)] #[track_caller] pub fn assert_export_firrtl_impl(top_module: &Module, expected: TestBackend) { - let result = export(TestBackend::default(), top_module).unwrap(); + let result = export(TestBackend::default(), top_module, expected.options).unwrap(); if result != expected { panic!( "assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------" diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index c817e45..cf85875 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -41,6 +41,12 @@ impl fmt::Display for SimplifyEnumsError { impl std::error::Error for SimplifyEnumsError {} +impl From for std::io::Error { + fn from(value: SimplifyEnumsError) -> Self { + std::io::Error::new(std::io::ErrorKind::Other, value) + } +} + #[hdl] struct TagAndBody { tag: T, @@ -595,10 +601,12 @@ impl Folder for State { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)] pub enum SimplifyEnumsKind { SimplifyToEnumsWithNoBody, + #[clap(name = "replace-with-bundle-of-uints")] ReplaceWithBundleOfUInts, + #[clap(name = "replace-with-uint")] ReplaceWithUInt, }