add command line options for selecting which transforms to apply when generating firrtl

This commit is contained in:
Jacob Lifshay 2024-09-25 01:47:48 -07:00
parent efc3a539ed
commit bb860d54cc
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
3 changed files with 137 additions and 12 deletions

View file

@ -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<Bundle>) -> Result<FirrtlOutput> {
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",

View file

@ -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<B: FileBackendTrait> {
@ -533,6 +539,11 @@ impl<B: FileBackendTrait> WrappedFileBackendTrait for WrappedFileBackend<B> {
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<SimplifyEnumsError>;
type Path: AsRef<Self::Path> + fmt::Debug + ?Sized;
type PathBuf: AsRef<Self::Path> + fmt::Debug;
fn path_to_string(&mut self, path: &Self::Path) -> Result<String, Self::Error>;
@ -2370,6 +2381,7 @@ impl Default for TestBackendPrivate {
pub struct TestBackend {
pub files: BTreeMap<String, String>,
pub error_after: Option<i64>,
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<SimplifyEnumsError> 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<Module<Bundle>>,
mut top_module: Interned<Module<Bundle>>,
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<SimplifyEnumsKind>;
fn parse_ref(
&self,
cmd: &clap::Command,
arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
if value == Self::NONE_NAME {
Ok(None)
} else {
Ok(Some(
value_parser!(SimplifyEnumsKind).parse_ref(cmd, arg, value)?,
))
}
}
fn possible_values(
&self,
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
Some(Box::new(
[Self::NONE_NAME.into()]
.into_iter()
.chain(value_parser!(SimplifyEnumsKind).possible_values()?)
.collect::<Vec<_>>()
.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<SimplifyEnumsKind>,
}
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<T: BundleType, B: FileBackendTrait>(
file_backend: B,
top_module: &Module<T>,
options: ExportOptions,
) -> Result<B, B::Error> {
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<T: BundleType>(top_module: &Module<T>, 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-------"

View file

@ -41,6 +41,12 @@ impl fmt::Display for SimplifyEnumsError {
impl std::error::Error for SimplifyEnumsError {}
impl From<SimplifyEnumsError> for std::io::Error {
fn from(value: SimplifyEnumsError) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, value)
}
}
#[hdl]
struct TagAndBody<T, BodyWidth: Size> {
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,
}