add command line options for selecting which transforms to apply when generating firrtl
This commit is contained in:
parent
efc3a539ed
commit
bb860d54cc
|
@ -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",
|
||||
|
|
|
@ -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-------"
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue