Compare commits
No commits in common. "8489bd06f069d7ee8f818c6472a596cbea2152d2" and "28aad19bf5c96f071a6f123ed934e3e2019f34cd" have entirely different histories.
8489bd06f0
...
28aad19bf5
13 changed files with 89 additions and 615 deletions
72
Cargo.lock
generated
72
Cargo.lock
generated
|
|
@ -56,7 +56,7 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -66,7 +66,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -218,7 +218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -249,10 +249,8 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"os_pipe",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
|
||||||
"trybuild",
|
"trybuild",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
@ -336,7 +334,7 @@ version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -415,16 +413,6 @@ version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "os_pipe"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
|
|
@ -469,7 +457,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -553,7 +541,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -676,25 +664,15 @@ dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.59.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
"windows_i686_gnullvm",
|
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm",
|
||||||
|
|
@ -703,51 +681,45 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnullvm"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winsafe"
|
name = "winsafe"
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,10 @@ fayalite-proc-macros-impl = { version = "=0.2.0", path = "crates/fayalite-proc-m
|
||||||
fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" }
|
fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" }
|
||||||
base16ct = "0.2.0"
|
base16ct = "0.2.0"
|
||||||
bitvec = { version = "1.0.1", features = ["serde"] }
|
bitvec = { version = "1.0.1", features = ["serde"] }
|
||||||
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
|
|
||||||
eyre = "0.6.12"
|
|
||||||
hashbrown = "0.14.3"
|
hashbrown = "0.14.3"
|
||||||
indexmap = { version = "2.2.6", features = ["serde"] }
|
indexmap = { version = "2.2.6", features = ["serde"] }
|
||||||
num-bigint = "0.4.4"
|
num-bigint = "0.4.4"
|
||||||
num-traits = "0.2.16"
|
num-traits = "0.2.16"
|
||||||
os_pipe = "1.2.1"
|
|
||||||
prettyplease = "0.2.20"
|
prettyplease = "0.2.20"
|
||||||
proc-macro2 = "1.0.83"
|
proc-macro2 = "1.0.83"
|
||||||
quote = "1.0.36"
|
quote = "1.0.36"
|
||||||
|
|
@ -36,4 +33,3 @@ syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"]
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
which = "6.0.1"
|
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ rust-version.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base16ct.workspace = true
|
base16ct = { workspace = true }
|
||||||
num-bigint.workspace = true
|
num-bigint = { workspace = true }
|
||||||
prettyplease.workspace = true
|
prettyplease = { workspace = true }
|
||||||
proc-macro2.workspace = true
|
proc-macro2 = { workspace = true }
|
||||||
quote.workspace = true
|
quote = { workspace = true }
|
||||||
sha2.workspace = true
|
sha2 = { workspace = true }
|
||||||
syn.workspace = true
|
syn = { workspace = true }
|
||||||
tempfile.workspace = true
|
tempfile = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,4 @@ version.workspace = true
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fayalite-proc-macros-impl.workspace = true
|
fayalite-proc-macros-impl = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ rust-version.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
indexmap.workspace = true
|
indexmap = { workspace = true }
|
||||||
prettyplease.workspace = true
|
prettyplease = { workspace = true }
|
||||||
proc-macro2.workspace = true
|
proc-macro2 = { workspace = true }
|
||||||
quote.workspace = true
|
quote = { workspace = true }
|
||||||
serde.workspace = true
|
serde = { workspace = true }
|
||||||
serde_json.workspace = true
|
serde_json = { workspace = true }
|
||||||
syn.workspace = true
|
syn = { workspace = true }
|
||||||
thiserror.workspace = true
|
thiserror = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -14,24 +14,22 @@ rust-version.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitvec.workspace = true
|
bitvec = { workspace = true }
|
||||||
clap.workspace = true
|
hashbrown = { workspace = true }
|
||||||
eyre.workspace = true
|
num-bigint = { workspace = true }
|
||||||
fayalite-proc-macros.workspace = true
|
num-traits = { workspace = true }
|
||||||
hashbrown.workspace = true
|
fayalite-proc-macros = { workspace = true }
|
||||||
num-bigint.workspace = true
|
serde = { workspace = true }
|
||||||
num-traits.workspace = true
|
serde_json = { workspace = true }
|
||||||
os_pipe.workspace = true
|
clap = { version = "4.5.9", features = ["derive", "env"] }
|
||||||
serde_json.workspace = true
|
eyre = "0.6.12"
|
||||||
serde.workspace = true
|
which = "6.0.1"
|
||||||
tempfile.workspace = true
|
|
||||||
which.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
trybuild.workspace = true
|
trybuild = { workspace = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
fayalite-visit-gen.workspace = true
|
fayalite-visit-gen = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
unstable-doc = []
|
unstable-doc = []
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,16 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{Bundle, BundleType},
|
bundle::{Bundle, BundleType},
|
||||||
firrtl::{self, ExportOptions},
|
firrtl,
|
||||||
intern::Interned,
|
intern::Interned,
|
||||||
module::Module,
|
module::Module,
|
||||||
util::streaming_read_utf8::streaming_read_utf8,
|
|
||||||
};
|
};
|
||||||
use clap::{
|
use clap::{
|
||||||
builder::{OsStringValueParser, TypedValueParser},
|
builder::{OsStringValueParser, TypedValueParser},
|
||||||
Parser, Subcommand, ValueEnum, ValueHint,
|
Args, Parser, Subcommand, ValueEnum, ValueHint,
|
||||||
};
|
};
|
||||||
use eyre::{eyre, Report};
|
use eyre::{eyre, Report};
|
||||||
use std::{error, ffi::OsString, fmt, io, path::PathBuf, process};
|
use std::{error, ffi::OsString, fmt, io, path::PathBuf, process};
|
||||||
use tempfile::TempDir;
|
|
||||||
|
|
||||||
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
|
@ -44,119 +42,56 @@ pub trait RunPhase<Arg> {
|
||||||
fn run(&self, arg: Arg) -> Result<Self::Output>;
|
fn run(&self, arg: Arg) -> Result<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Args, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct BaseArgs {
|
pub struct BaseArgs {
|
||||||
/// the directory to put the generated main output file and associated files in
|
/// the directory to put the generated main output file and associated files in
|
||||||
#[arg(short, long, value_hint = ValueHint::DirPath, required = true)]
|
#[arg(short, long, value_hint = ValueHint::DirPath)]
|
||||||
pub output: Option<PathBuf>,
|
pub output: PathBuf,
|
||||||
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
|
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub file_stem: Option<String>,
|
pub file_stem: Option<String>,
|
||||||
#[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")]
|
|
||||||
pub keep_temp_dir: bool,
|
|
||||||
#[arg(skip = false)]
|
|
||||||
pub redirect_output_for_rust_test: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseArgs {
|
impl BaseArgs {
|
||||||
fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option<TempDir>)> {
|
pub fn to_firrtl_file_backend(&self) -> firrtl::FileBackend {
|
||||||
let (dir_path, temp_dir) = match &self.output {
|
firrtl::FileBackend {
|
||||||
Some(output) => (output.clone(), None),
|
dir_path: self.output.clone(),
|
||||||
None => {
|
top_fir_file_stem: self.file_stem.clone(),
|
||||||
let temp_dir = TempDir::new()?;
|
|
||||||
if self.keep_temp_dir {
|
|
||||||
let temp_dir = temp_dir.into_path();
|
|
||||||
println!("created temporary directory: {}", temp_dir.display());
|
|
||||||
(temp_dir, None)
|
|
||||||
} else {
|
|
||||||
(temp_dir.path().to_path_buf(), Some(temp_dir))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok((
|
|
||||||
firrtl::FileBackend {
|
|
||||||
dir_path,
|
|
||||||
top_fir_file_stem: self.file_stem.clone(),
|
|
||||||
circuit_name: None,
|
|
||||||
},
|
|
||||||
temp_dir,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
/// handles possibly redirecting the command's output for Rust tests
|
|
||||||
pub fn run_external_command(
|
|
||||||
&self,
|
|
||||||
mut command: process::Command,
|
|
||||||
) -> io::Result<process::ExitStatus> {
|
|
||||||
if self.redirect_output_for_rust_test {
|
|
||||||
let (reader, writer) = os_pipe::pipe()?;
|
|
||||||
let mut reader = io::BufReader::new(reader);
|
|
||||||
command.stderr(writer.try_clone()?);
|
|
||||||
command.stdout(writer); // must not leave writer around after spawning child
|
|
||||||
command.stdin(process::Stdio::null());
|
|
||||||
let mut child = command.spawn()?;
|
|
||||||
drop(command); // close writers
|
|
||||||
Ok(loop {
|
|
||||||
let status = child.try_wait()?;
|
|
||||||
streaming_read_utf8(&mut reader, |s| {
|
|
||||||
// use print! so output goes to Rust test output capture
|
|
||||||
print!("{s}");
|
|
||||||
io::Result::Ok(())
|
|
||||||
})?;
|
|
||||||
if let Some(status) = status {
|
|
||||||
break status;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
command.status()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Args, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
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)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct FirrtlOutput {
|
pub struct FirrtlOutput {
|
||||||
pub file_stem: String,
|
pub file_stem: String,
|
||||||
pub top_module: String,
|
|
||||||
pub output_dir: PathBuf,
|
|
||||||
pub temp_dir: Option<TempDir>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FirrtlOutput {
|
impl FirrtlOutput {
|
||||||
pub fn file_with_ext(&self, ext: &str) -> PathBuf {
|
pub fn firrtl_file(&self, args: &FirrtlArgs) -> PathBuf {
|
||||||
let mut retval = self.output_dir.join(&self.file_stem);
|
let mut retval = args.base.output.join(&self.file_stem);
|
||||||
retval.set_extension(ext);
|
retval.set_extension("fir");
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
pub fn firrtl_file(&self) -> PathBuf {
|
|
||||||
self.file_with_ext("fir")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FirrtlArgs {
|
impl FirrtlArgs {
|
||||||
fn run_impl(&self, top_module: Module<Bundle>) -> Result<FirrtlOutput> {
|
fn run_impl(&self, top_module: Module<Bundle>) -> Result<FirrtlOutput> {
|
||||||
let (file_backend, temp_dir) = self.base.make_firrtl_file_backend()?;
|
|
||||||
let firrtl::FileBackend {
|
let firrtl::FileBackend {
|
||||||
top_fir_file_stem,
|
top_fir_file_stem, ..
|
||||||
circuit_name,
|
} = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?;
|
||||||
dir_path,
|
|
||||||
} = firrtl::export(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",
|
||||||
),
|
),
|
||||||
top_module: circuit_name.expect("export is known to set the circuit name"),
|
|
||||||
output_dir: dir_path,
|
|
||||||
temp_dir,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -187,22 +122,7 @@ pub enum VerilogDialect {
|
||||||
Yosys,
|
Yosys,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for VerilogDialect {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VerilogDialect {
|
impl VerilogDialect {
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
VerilogDialect::Questa => "questa",
|
|
||||||
VerilogDialect::Spyglass => "spyglass",
|
|
||||||
VerilogDialect::Verilator => "verilator",
|
|
||||||
VerilogDialect::Vivado => "vivado",
|
|
||||||
VerilogDialect::Yosys => "yosys",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn firtool_extra_args(self) -> &'static [&'static str] {
|
pub fn firtool_extra_args(self) -> &'static [&'static str] {
|
||||||
match self {
|
match self {
|
||||||
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
|
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
|
||||||
|
|
@ -220,7 +140,7 @@ impl VerilogDialect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Args, Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct VerilogArgs {
|
pub struct VerilogArgs {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
|
|
@ -238,8 +158,6 @@ pub struct VerilogArgs {
|
||||||
/// adapt the generated Verilog for a particular toolchain
|
/// adapt the generated Verilog for a particular toolchain
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub verilog_dialect: Option<VerilogDialect>,
|
pub verilog_dialect: Option<VerilogDialect>,
|
||||||
#[arg(long, short = 'g')]
|
|
||||||
pub debug: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -249,37 +167,28 @@ pub struct VerilogOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerilogOutput {
|
impl VerilogOutput {
|
||||||
pub fn verilog_file(&self) -> PathBuf {
|
pub fn verilog_file(&self, args: &VerilogArgs) -> PathBuf {
|
||||||
self.firrtl.file_with_ext("v")
|
let mut retval = args.firrtl.base.output.join(&self.firrtl.file_stem);
|
||||||
|
retval.set_extension("v");
|
||||||
|
retval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerilogArgs {
|
impl VerilogArgs {
|
||||||
fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result<VerilogOutput> {
|
fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result<VerilogOutput> {
|
||||||
let Self {
|
|
||||||
firrtl,
|
|
||||||
firtool,
|
|
||||||
firtool_extra_args,
|
|
||||||
verilog_dialect,
|
|
||||||
debug,
|
|
||||||
} = self;
|
|
||||||
let output = VerilogOutput {
|
let output = VerilogOutput {
|
||||||
firrtl: firrtl_output,
|
firrtl: firrtl_output,
|
||||||
};
|
};
|
||||||
let mut cmd = process::Command::new(firtool);
|
let mut cmd = process::Command::new(&self.firtool);
|
||||||
cmd.arg(output.firrtl.firrtl_file());
|
cmd.arg(output.firrtl.firrtl_file(&self.firrtl));
|
||||||
cmd.arg("-o");
|
cmd.arg("-o");
|
||||||
cmd.arg(output.verilog_file());
|
cmd.arg(output.verilog_file(self));
|
||||||
if *debug {
|
if let Some(dialect) = self.verilog_dialect {
|
||||||
cmd.arg("-g");
|
|
||||||
cmd.arg("--preserve-values=named");
|
|
||||||
}
|
|
||||||
if let Some(dialect) = verilog_dialect {
|
|
||||||
cmd.args(dialect.firtool_extra_args());
|
cmd.args(dialect.firtool_extra_args());
|
||||||
}
|
}
|
||||||
cmd.args(firtool_extra_args);
|
cmd.args(&self.firtool_extra_args);
|
||||||
cmd.current_dir(&output.firrtl.output_dir);
|
cmd.current_dir(&self.firrtl.base.output);
|
||||||
let status = firrtl.base.run_external_command(cmd)?;
|
let status = cmd.status()?;
|
||||||
if status.success() {
|
if status.success() {
|
||||||
Ok(output)
|
Ok(output)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -302,217 +211,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum FormalMode {
|
|
||||||
#[default]
|
|
||||||
BMC,
|
|
||||||
Prove,
|
|
||||||
Live,
|
|
||||||
Cover,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalMode {
|
|
||||||
pub fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
FormalMode::BMC => "bmc",
|
|
||||||
FormalMode::Prove => "prove",
|
|
||||||
FormalMode::Live => "live",
|
|
||||||
FormalMode::Cover => "cover",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FormalMode {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct FormalAdjustArgs;
|
|
||||||
|
|
||||||
impl clap::FromArgMatches for FormalAdjustArgs {
|
|
||||||
fn from_arg_matches(_matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
|
|
||||||
Ok(Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl clap::Args for FormalAdjustArgs {
|
|
||||||
fn augment_args(cmd: clap::Command) -> clap::Command {
|
|
||||||
cmd.mut_arg("output", |arg| arg.required(false))
|
|
||||||
.mut_arg("verilog_dialect", |arg| {
|
|
||||||
arg.default_value(VerilogDialect::Yosys.to_string())
|
|
||||||
.hide(true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
|
||||||
Self::augment_args(cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Clone)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct FormalArgs {
|
|
||||||
#[command(flatten)]
|
|
||||||
pub verilog: VerilogArgs,
|
|
||||||
#[arg(
|
|
||||||
long,
|
|
||||||
default_value = "sby",
|
|
||||||
env = "SBY",
|
|
||||||
value_hint = ValueHint::CommandName,
|
|
||||||
value_parser = OsStringValueParser::new().try_map(which::which)
|
|
||||||
)]
|
|
||||||
pub sby: PathBuf,
|
|
||||||
#[arg(long)]
|
|
||||||
pub sby_extra_args: Vec<OsString>,
|
|
||||||
#[arg(long, default_value_t)]
|
|
||||||
pub mode: FormalMode,
|
|
||||||
#[arg(long, default_value_t = Self::DEFAULT_DEPTH)]
|
|
||||||
pub depth: u64,
|
|
||||||
#[arg(long)]
|
|
||||||
pub solver: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub smtbmc_extra_args: Vec<String>,
|
|
||||||
#[command(flatten)]
|
|
||||||
_formal_adjust_args: FormalAdjustArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for FormalArgs {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let Self {
|
|
||||||
verilog,
|
|
||||||
sby,
|
|
||||||
sby_extra_args,
|
|
||||||
mode,
|
|
||||||
depth,
|
|
||||||
solver,
|
|
||||||
smtbmc_extra_args,
|
|
||||||
_formal_adjust_args: _,
|
|
||||||
} = self;
|
|
||||||
f.debug_struct("FormalArgs")
|
|
||||||
.field("verilog", &verilog)
|
|
||||||
.field("sby", &sby)
|
|
||||||
.field("sby_extra_args", &sby_extra_args)
|
|
||||||
.field("mode", &mode)
|
|
||||||
.field("depth", &depth)
|
|
||||||
.field("solver", &solver)
|
|
||||||
.field("smtbmc_extra_args", &smtbmc_extra_args)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalArgs {
|
|
||||||
pub const DEFAULT_DEPTH: u64 = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct FormalOutput {
|
|
||||||
pub verilog: VerilogOutput,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalOutput {
|
|
||||||
pub fn sby_file(&self) -> PathBuf {
|
|
||||||
self.verilog.firrtl.file_with_ext("sby")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormalArgs {
|
|
||||||
fn sby_contents(&self, output: &FormalOutput) -> String {
|
|
||||||
let Self {
|
|
||||||
verilog: _,
|
|
||||||
sby: _,
|
|
||||||
sby_extra_args: _,
|
|
||||||
mode,
|
|
||||||
depth,
|
|
||||||
smtbmc_extra_args,
|
|
||||||
solver,
|
|
||||||
_formal_adjust_args: _,
|
|
||||||
} = self;
|
|
||||||
struct OptArg<T>(Option<T>);
|
|
||||||
impl<T: fmt::Display> fmt::Display for OptArg<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
if let Some(v) = &self.0 {
|
|
||||||
f.write_str(" ")?;
|
|
||||||
v.fmt(f)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let space_solver = OptArg(solver.as_ref());
|
|
||||||
let smtbmc_options = smtbmc_extra_args.join(" ");
|
|
||||||
let verilog_file = output
|
|
||||||
.verilog
|
|
||||||
.verilog_file()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.ok()
|
|
||||||
.expect("verilog file path is not UTF-8");
|
|
||||||
let top_module = &output.verilog.firrtl.top_module;
|
|
||||||
format!(
|
|
||||||
"[options]\n\
|
|
||||||
mode {mode}\n\
|
|
||||||
depth {depth}\n\
|
|
||||||
wait on\n\
|
|
||||||
\n\
|
|
||||||
[engines]\n\
|
|
||||||
smtbmc{space_solver} -- -- {smtbmc_options}\n\
|
|
||||||
\n\
|
|
||||||
[script]\n\
|
|
||||||
read_verilog -sv -formal {verilog_file}\n\
|
|
||||||
prep -top {top_module}\n
|
|
||||||
"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
fn run_impl(&self, verilog_output: VerilogOutput) -> Result<FormalOutput> {
|
|
||||||
let output = FormalOutput {
|
|
||||||
verilog: verilog_output,
|
|
||||||
};
|
|
||||||
let sby_file = output.sby_file();
|
|
||||||
std::fs::write(&sby_file, self.sby_contents(&output))?;
|
|
||||||
let mut cmd = process::Command::new(&self.sby);
|
|
||||||
cmd.arg("-f");
|
|
||||||
cmd.arg(sby_file);
|
|
||||||
cmd.args(&self.sby_extra_args);
|
|
||||||
cmd.current_dir(&output.verilog.firrtl.output_dir);
|
|
||||||
let status = self.verilog.firrtl.base.run_external_command(cmd)?;
|
|
||||||
if status.success() {
|
|
||||||
Ok(output)
|
|
||||||
} else {
|
|
||||||
Err(CliError(eyre!(
|
|
||||||
"running {} failed: {status}",
|
|
||||||
self.sby.display()
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Arg> RunPhase<Arg> for FormalArgs
|
|
||||||
where
|
|
||||||
VerilogArgs: RunPhase<Arg, Output = VerilogOutput>,
|
|
||||||
{
|
|
||||||
type Output = FormalOutput;
|
|
||||||
fn run(&self, arg: Arg) -> Result<Self::Output> {
|
|
||||||
let verilog_output = self.verilog.run(arg)?;
|
|
||||||
self.run_impl(verilog_output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum CliCommand {
|
enum CliCommand {
|
||||||
/// Generate FIRRTL
|
/// Generate FIRRTL
|
||||||
Firrtl(FirrtlArgs),
|
Firrtl(FirrtlArgs),
|
||||||
/// Generate Verilog
|
/// Generate Verilog
|
||||||
Verilog(VerilogArgs),
|
Verilog(VerilogArgs),
|
||||||
/// Run a formal proof
|
|
||||||
Formal(FormalArgs),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a simple CLI
|
/// a simple CLI
|
||||||
|
|
@ -598,9 +302,6 @@ where
|
||||||
CliCommand::Verilog(c) => {
|
CliCommand::Verilog(c) => {
|
||||||
c.run(arg)?;
|
c.run(arg)?;
|
||||||
}
|
}
|
||||||
CliCommand::Formal(c) => {
|
|
||||||
c.run(arg)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,10 @@ use crate::{
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
||||||
module::{
|
module::{
|
||||||
transform::{
|
transform::simplify_memories::simplify_memories, AnnotatedModuleIO, Block,
|
||||||
simplify_enums::{simplify_enums, SimplifyEnumsError, SimplifyEnumsKind},
|
ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody,
|
||||||
simplify_memories::simplify_memories,
|
NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind,
|
||||||
},
|
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,
|
||||||
|
|
@ -36,7 +32,6 @@ 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;
|
||||||
|
|
@ -481,7 +476,6 @@ 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> {
|
||||||
|
|
@ -539,11 +533,6 @@ 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)]
|
||||||
|
|
@ -2236,7 +2225,7 @@ impl<'a> Exporter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FileBackendTrait {
|
pub trait FileBackendTrait {
|
||||||
type Error: From<SimplifyEnumsError>;
|
type Error;
|
||||||
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>;
|
||||||
|
|
@ -2311,7 +2300,6 @@ impl<T: ?Sized + FileBackendTrait> FileBackendTrait for &'_ mut T {
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct FileBackend {
|
pub struct FileBackend {
|
||||||
pub dir_path: PathBuf,
|
pub dir_path: PathBuf,
|
||||||
pub circuit_name: Option<String>,
|
|
||||||
pub top_fir_file_stem: Option<String>,
|
pub top_fir_file_stem: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2319,7 +2307,6 @@ impl FileBackend {
|
||||||
pub fn new(dir_path: impl AsRef<Path>) -> Self {
|
pub fn new(dir_path: impl AsRef<Path>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dir_path: dir_path.as_ref().to_owned(),
|
dir_path: dir_path.as_ref().to_owned(),
|
||||||
circuit_name: None,
|
|
||||||
top_fir_file_stem: None,
|
top_fir_file_stem: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2355,10 +2342,7 @@ impl FileBackendTrait for FileBackend {
|
||||||
circuit_name: String,
|
circuit_name: String,
|
||||||
contents: String,
|
contents: String,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let top_fir_file_stem = self
|
let top_fir_file_stem = self.top_fir_file_stem.get_or_insert(circuit_name);
|
||||||
.top_fir_file_stem
|
|
||||||
.get_or_insert_with(|| circuit_name.clone());
|
|
||||||
self.circuit_name = Some(circuit_name);
|
|
||||||
let mut path = self.dir_path.join(top_fir_file_stem);
|
let mut path = self.dir_path.join(top_fir_file_stem);
|
||||||
if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) {
|
if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) {
|
||||||
fs::create_dir_all(parent)?;
|
fs::create_dir_all(parent)?;
|
||||||
|
|
@ -2386,7 +2370,6 @@ 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,
|
||||||
|
|
@ -2397,7 +2380,6 @@ 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!(
|
||||||
|
|
@ -2412,9 +2394,6 @@ 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, " }};")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2430,12 +2409,6 @@ 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> {
|
||||||
|
|
@ -2492,20 +2465,9 @@ impl FileBackendTrait for TestBackend {
|
||||||
|
|
||||||
fn export_impl(
|
fn export_impl(
|
||||||
file_backend: &mut dyn WrappedFileBackendTrait,
|
file_backend: &mut dyn WrappedFileBackendTrait,
|
||||||
mut top_module: Interned<Module<Bundle>>,
|
top_module: Interned<Module<Bundle>>,
|
||||||
options: ExportOptions,
|
|
||||||
) -> Result<(), WrappedError> {
|
) -> Result<(), WrappedError> {
|
||||||
let ExportOptions {
|
let top_module = simplify_memories(top_module);
|
||||||
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());
|
||||||
|
|
@ -2526,102 +2488,20 @@ 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, options)
|
export_impl(file_backend, top_module)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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, expected.options).unwrap();
|
let result = export(TestBackend::default(), top_module).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-------"
|
||||||
|
|
|
||||||
|
|
@ -50,5 +50,4 @@ pub mod ty;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
//pub mod valueless;
|
//pub mod valueless;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod testing;
|
|
||||||
pub mod wire;
|
pub mod wire;
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,6 @@ 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,
|
||||||
|
|
@ -601,12 +595,10 @@ impl Folder for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum SimplifyEnumsKind {
|
pub enum SimplifyEnumsKind {
|
||||||
SimplifyToEnumsWithNoBody,
|
SimplifyToEnumsWithNoBody,
|
||||||
#[clap(name = "replace-with-bundle-of-uints")]
|
|
||||||
ReplaceWithBundleOfUInts,
|
ReplaceWithBundleOfUInts,
|
||||||
#[clap(name = "replace-with-uint")]
|
|
||||||
ReplaceWithUInt,
|
ReplaceWithUInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
use crate::{
|
|
||||||
cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
|
|
||||||
firrtl::ExportOptions,
|
|
||||||
};
|
|
||||||
use clap::Parser;
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
fn assert_formal_helper() -> FormalArgs {
|
|
||||||
static FORMAL_ARGS: OnceLock<FormalArgs> = OnceLock::new();
|
|
||||||
// ensure we only run parsing once, so errors from env vars don't produce overlapping output if we're called on multiple threads
|
|
||||||
FORMAL_ARGS
|
|
||||||
.get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"]))
|
|
||||||
.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
pub fn assert_formal<M>(
|
|
||||||
module: M,
|
|
||||||
mode: FormalMode,
|
|
||||||
depth: u64,
|
|
||||||
solver: Option<&str>,
|
|
||||||
export_options: ExportOptions,
|
|
||||||
) where
|
|
||||||
FormalArgs: RunPhase<M, Output = FormalOutput>,
|
|
||||||
{
|
|
||||||
let mut args = assert_formal_helper();
|
|
||||||
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
|
|
||||||
args.verilog.firrtl.export_options = export_options;
|
|
||||||
args.verilog.debug = true;
|
|
||||||
args.mode = mode;
|
|
||||||
args.depth = depth;
|
|
||||||
args.solver = solver.map(String::from);
|
|
||||||
args.run(module).expect("testing::assert_formal() failed");
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,6 @@ mod const_cmp;
|
||||||
mod const_usize;
|
mod const_usize;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod scoped_ref;
|
mod scoped_ref;
|
||||||
pub(crate) mod streaming_read_utf8;
|
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
use std::{
|
|
||||||
io::{self, BufRead},
|
|
||||||
str,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn streaming_read_utf8<R: BufRead, E: From<io::Error>>(
|
|
||||||
reader: R,
|
|
||||||
mut callback: impl FnMut(&str) -> Result<(), E>,
|
|
||||||
) -> Result<(), E> {
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
let mut buf_len = 0;
|
|
||||||
for byte in reader.bytes() {
|
|
||||||
buf[buf_len] = byte?;
|
|
||||||
buf_len += 1;
|
|
||||||
match str::from_utf8(&buf[..buf_len]) {
|
|
||||||
Ok(buf) => {
|
|
||||||
callback(buf)?;
|
|
||||||
buf_len = 0;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if e.error_len().is_some() {
|
|
||||||
callback("\u{FFFD}")?; // replacement character
|
|
||||||
buf_len = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue