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
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
firrtl,
|
firrtl::{self, ExportOptions},
|
||||||
intern::Interned,
|
intern::Interned,
|
||||||
module::Module,
|
module::Module,
|
||||||
util::streaming_read_utf8::streaming_read_utf8,
|
util::streaming_read_utf8::streaming_read_utf8,
|
||||||
|
@ -98,6 +98,8 @@ impl BaseArgs {
|
||||||
pub struct FirrtlArgs {
|
pub struct FirrtlArgs {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub base: BaseArgs,
|
pub base: BaseArgs,
|
||||||
|
#[command(flatten)]
|
||||||
|
pub export_options: ExportOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -118,7 +120,7 @@ impl FirrtlArgs {
|
||||||
fn run_impl(&self, top_module: Module<Bundle>) -> Result<FirrtlOutput> {
|
fn run_impl(&self, top_module: Module<Bundle>) -> Result<FirrtlOutput> {
|
||||||
let firrtl::FileBackend {
|
let firrtl::FileBackend {
|
||||||
top_fir_file_stem, ..
|
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 {
|
Ok(FirrtlOutput {
|
||||||
file_stem: top_fir_file_stem.expect(
|
file_stem: top_fir_file_stem.expect(
|
||||||
"export is known to set the file stem from the circuit name if not provided",
|
"export is known to set the file stem from the circuit name if not provided",
|
||||||
|
|
|
@ -18,10 +18,14 @@ use crate::{
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
||||||
module::{
|
module::{
|
||||||
transform::simplify_memories::simplify_memories, AnnotatedModuleIO, Block,
|
transform::{
|
||||||
ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody,
|
simplify_enums::{simplify_enums, SimplifyEnumsError, SimplifyEnumsKind},
|
||||||
NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind,
|
simplify_memories::simplify_memories,
|
||||||
StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire,
|
},
|
||||||
|
AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter,
|
||||||
|
ExternModuleParameterValue, Module, ModuleBody, NameId, NormalModuleBody, Stmt,
|
||||||
|
StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, StmtIf, StmtInstance, StmtMatch,
|
||||||
|
StmtReg, StmtWire,
|
||||||
},
|
},
|
||||||
reset::{AsyncReset, Reset, SyncReset},
|
reset::{AsyncReset, Reset, SyncReset},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
@ -32,6 +36,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bitvec::slice::BitSlice;
|
use bitvec::slice::BitSlice;
|
||||||
|
use clap::value_parser;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use num_traits::Signed;
|
use num_traits::Signed;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -476,6 +481,7 @@ trait WrappedFileBackendTrait {
|
||||||
circuit_name: String,
|
circuit_name: String,
|
||||||
contents: String,
|
contents: String,
|
||||||
) -> Result<(), WrappedError>;
|
) -> Result<(), WrappedError>;
|
||||||
|
fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WrappedFileBackend<B: FileBackendTrait> {
|
struct WrappedFileBackend<B: FileBackendTrait> {
|
||||||
|
@ -533,6 +539,11 @@ impl<B: FileBackendTrait> WrappedFileBackendTrait for WrappedFileBackend<B> {
|
||||||
WrappedError
|
WrappedError
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError {
|
||||||
|
self.error = Err(error.into());
|
||||||
|
WrappedError
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -2225,7 +2236,7 @@ impl<'a> Exporter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FileBackendTrait {
|
pub trait FileBackendTrait {
|
||||||
type Error;
|
type Error: From<SimplifyEnumsError>;
|
||||||
type Path: AsRef<Self::Path> + fmt::Debug + ?Sized;
|
type Path: AsRef<Self::Path> + fmt::Debug + ?Sized;
|
||||||
type PathBuf: AsRef<Self::Path> + fmt::Debug;
|
type PathBuf: AsRef<Self::Path> + fmt::Debug;
|
||||||
fn path_to_string(&mut self, path: &Self::Path) -> Result<String, Self::Error>;
|
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 struct TestBackend {
|
||||||
pub files: BTreeMap<String, String>,
|
pub files: BTreeMap<String, String>,
|
||||||
pub error_after: Option<i64>,
|
pub error_after: Option<i64>,
|
||||||
|
pub options: ExportOptions,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// `#[non_exhaustive]` except allowing struct update syntax
|
/// `#[non_exhaustive]` except allowing struct update syntax
|
||||||
pub __private: TestBackendPrivate,
|
pub __private: TestBackendPrivate,
|
||||||
|
@ -2380,6 +2392,7 @@ impl fmt::Debug for TestBackend {
|
||||||
let Self {
|
let Self {
|
||||||
files,
|
files,
|
||||||
error_after,
|
error_after,
|
||||||
|
options,
|
||||||
__private: TestBackendPrivate { module_var_name },
|
__private: TestBackendPrivate { module_var_name },
|
||||||
} = self;
|
} = self;
|
||||||
writeln!(
|
writeln!(
|
||||||
|
@ -2394,6 +2407,9 @@ impl fmt::Debug for TestBackend {
|
||||||
if *error_after != Option::default() {
|
if *error_after != Option::default() {
|
||||||
writeln!(f, " error_after: {error_after:?},")?;
|
writeln!(f, " error_after: {error_after:?},")?;
|
||||||
}
|
}
|
||||||
|
if *options != ExportOptions::default() {
|
||||||
|
writeln!(f, " options: {options:?},")?;
|
||||||
|
}
|
||||||
write!(f, " }};")
|
write!(f, " }};")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2409,6 +2425,12 @@ impl fmt::Display for TestBackendError {
|
||||||
|
|
||||||
impl Error for TestBackendError {}
|
impl Error for TestBackendError {}
|
||||||
|
|
||||||
|
impl From<SimplifyEnumsError> for TestBackendError {
|
||||||
|
fn from(value: SimplifyEnumsError) -> Self {
|
||||||
|
TestBackendError(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TestBackend {
|
impl TestBackend {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn step_error_after(&mut self, args: &dyn fmt::Debug) -> Result<(), TestBackendError> {
|
pub fn step_error_after(&mut self, args: &dyn fmt::Debug) -> Result<(), TestBackendError> {
|
||||||
|
@ -2465,9 +2487,20 @@ impl FileBackendTrait for TestBackend {
|
||||||
|
|
||||||
fn export_impl(
|
fn export_impl(
|
||||||
file_backend: &mut dyn WrappedFileBackendTrait,
|
file_backend: &mut dyn WrappedFileBackendTrait,
|
||||||
top_module: Interned<Module<Bundle>>,
|
mut top_module: Interned<Module<Bundle>>,
|
||||||
|
options: ExportOptions,
|
||||||
) -> Result<(), WrappedError> {
|
) -> 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 indent_depth = Cell::new(0);
|
||||||
let mut global_ns = Namespace::default();
|
let mut global_ns = Namespace::default();
|
||||||
let circuit_name = global_ns.get(top_module.name_id());
|
let circuit_name = global_ns.get(top_module.name_id());
|
||||||
|
@ -2488,20 +2521,102 @@ fn export_impl(
|
||||||
.run(top_module)
|
.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>(
|
pub fn export<T: BundleType, B: FileBackendTrait>(
|
||||||
file_backend: B,
|
file_backend: B,
|
||||||
top_module: &Module<T>,
|
top_module: &Module<T>,
|
||||||
|
options: ExportOptions,
|
||||||
) -> Result<B, B::Error> {
|
) -> Result<B, B::Error> {
|
||||||
let top_module = Intern::intern_sized(top_module.canonical());
|
let top_module = Intern::intern_sized(top_module.canonical());
|
||||||
WrappedFileBackend::with(file_backend, |file_backend| {
|
WrappedFileBackend::with(file_backend, |file_backend| {
|
||||||
export_impl(file_backend, top_module)
|
export_impl(file_backend, top_module, options)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) {
|
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 {
|
if result != expected {
|
||||||
panic!(
|
panic!(
|
||||||
"assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------"
|
"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 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]
|
#[hdl]
|
||||||
struct TagAndBody<T, BodyWidth: Size> {
|
struct TagAndBody<T, BodyWidth: Size> {
|
||||||
tag: T,
|
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 {
|
pub enum SimplifyEnumsKind {
|
||||||
SimplifyToEnumsWithNoBody,
|
SimplifyToEnumsWithNoBody,
|
||||||
|
#[clap(name = "replace-with-bundle-of-uints")]
|
||||||
ReplaceWithBundleOfUInts,
|
ReplaceWithBundleOfUInts,
|
||||||
|
#[clap(name = "replace-with-uint")]
|
||||||
ReplaceWithUInt,
|
ReplaceWithUInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue