Compare commits
	
		
			5 commits
		
	
	
		
			e661aeab11
			...
			57aa849615
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 57aa849615 | |||
| d477252bde | |||
| 37bc6eb639 | |||
| e2de8c3d99 | |||
| 42945b1f9c | 
					 11 changed files with 685 additions and 57 deletions
				
			
		
							
								
								
									
										74
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										74
									
								
								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",
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[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",
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[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",
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -249,8 +249,10 @@ dependencies = [
 | 
				
			||||||
 "hashbrown",
 | 
					 "hashbrown",
 | 
				
			||||||
 "num-bigint",
 | 
					 "num-bigint",
 | 
				
			||||||
 "num-traits",
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					 "os_pipe",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 | 
					 "tempfile",
 | 
				
			||||||
 "trybuild",
 | 
					 "trybuild",
 | 
				
			||||||
 "which",
 | 
					 "which",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -334,7 +336,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",
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -413,6 +415,16 @@ 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"
 | 
				
			||||||
| 
						 | 
					@ -457,7 +469,7 @@ dependencies = [
 | 
				
			||||||
 "errno",
 | 
					 "errno",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "linux-raw-sys",
 | 
					 "linux-raw-sys",
 | 
				
			||||||
 "windows-sys",
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -541,7 +553,7 @@ dependencies = [
 | 
				
			||||||
 "cfg-if",
 | 
					 "cfg-if",
 | 
				
			||||||
 "fastrand",
 | 
					 "fastrand",
 | 
				
			||||||
 "rustix",
 | 
					 "rustix",
 | 
				
			||||||
 "windows-sys",
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -665,14 +677,24 @@ dependencies = [
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows-targets"
 | 
					name = "windows-sys"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.59.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
 | 
					checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "windows-targets",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-targets"
 | 
				
			||||||
 | 
					version = "0.52.6"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 | 
				
			||||||
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",
 | 
				
			||||||
| 
						 | 
					@ -681,45 +703,51 @@ dependencies = [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows_aarch64_gnullvm"
 | 
					name = "windows_aarch64_gnullvm"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
 | 
					checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows_aarch64_msvc"
 | 
					name = "windows_aarch64_msvc"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
 | 
					checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows_i686_gnu"
 | 
					name = "windows_i686_gnu"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
 | 
					checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
 | 
					checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows_x86_64_gnu"
 | 
					name = "windows_x86_64_gnu"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
 | 
					checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows_x86_64_gnullvm"
 | 
					name = "windows_x86_64_gnullvm"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
 | 
					checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "windows_x86_64_msvc"
 | 
					name = "windows_x86_64_msvc"
 | 
				
			||||||
version = "0.52.4"
 | 
					version = "0.52.6"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
 | 
					checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "winsafe"
 | 
					name = "winsafe"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ 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"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,8 +21,10 @@ fayalite-proc-macros.workspace = true
 | 
				
			||||||
hashbrown.workspace = true
 | 
					hashbrown.workspace = true
 | 
				
			||||||
num-bigint.workspace = true
 | 
					num-bigint.workspace = true
 | 
				
			||||||
num-traits.workspace = true
 | 
					num-traits.workspace = true
 | 
				
			||||||
 | 
					os_pipe.workspace = true
 | 
				
			||||||
serde_json.workspace = true
 | 
					serde_json.workspace = true
 | 
				
			||||||
serde.workspace = true
 | 
					serde.workspace = true
 | 
				
			||||||
 | 
					tempfile.workspace = true
 | 
				
			||||||
which.workspace = true
 | 
					which.workspace = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,10 @@
 | 
				
			||||||
// 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,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use clap::{
 | 
					use clap::{
 | 
				
			||||||
    builder::{OsStringValueParser, TypedValueParser},
 | 
					    builder::{OsStringValueParser, TypedValueParser},
 | 
				
			||||||
| 
						 | 
					@ -12,6 +13,7 @@ use clap::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
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>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,18 +48,67 @@ pub trait RunPhase<Arg> {
 | 
				
			||||||
#[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)]
 | 
					    #[arg(short, long, value_hint = ValueHint::DirPath, required = true)]
 | 
				
			||||||
    pub output: PathBuf,
 | 
					    pub output: Option<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 {
 | 
				
			||||||
    pub fn to_firrtl_file_backend(&self) -> firrtl::FileBackend {
 | 
					    fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option<TempDir>)> {
 | 
				
			||||||
        firrtl::FileBackend {
 | 
					        let (dir_path, temp_dir) = match &self.output {
 | 
				
			||||||
            dir_path: self.output.clone(),
 | 
					            Some(output) => (output.clone(), None),
 | 
				
			||||||
            top_fir_file_stem: self.file_stem.clone(),
 | 
					            None => {
 | 
				
			||||||
 | 
					                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()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -67,31 +118,45 @@ 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)]
 | 
				
			||||||
#[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 firrtl_file(&self, args: &FirrtlArgs) -> PathBuf {
 | 
					    pub fn file_with_ext(&self, ext: &str) -> PathBuf {
 | 
				
			||||||
        let mut retval = args.base.output.join(&self.file_stem);
 | 
					        let mut retval = self.output_dir.join(&self.file_stem);
 | 
				
			||||||
        retval.set_extension("fir");
 | 
					        retval.set_extension(ext);
 | 
				
			||||||
        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,
 | 
				
			||||||
        } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?;
 | 
					            circuit_name,
 | 
				
			||||||
 | 
					            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,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -122,7 +187,22 @@ 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"],
 | 
				
			||||||
| 
						 | 
					@ -158,6 +238,8 @@ 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)]
 | 
				
			||||||
| 
						 | 
					@ -167,28 +249,37 @@ pub struct VerilogOutput {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl VerilogOutput {
 | 
					impl VerilogOutput {
 | 
				
			||||||
    pub fn verilog_file(&self, args: &VerilogArgs) -> PathBuf {
 | 
					    pub fn verilog_file(&self) -> PathBuf {
 | 
				
			||||||
        let mut retval = args.firrtl.base.output.join(&self.firrtl.file_stem);
 | 
					        self.firrtl.file_with_ext("v")
 | 
				
			||||||
        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(&self.firtool);
 | 
					        let mut cmd = process::Command::new(firtool);
 | 
				
			||||||
        cmd.arg(output.firrtl.firrtl_file(&self.firrtl));
 | 
					        cmd.arg(output.firrtl.firrtl_file());
 | 
				
			||||||
        cmd.arg("-o");
 | 
					        cmd.arg("-o");
 | 
				
			||||||
        cmd.arg(output.verilog_file(self));
 | 
					        cmd.arg(output.verilog_file());
 | 
				
			||||||
        if let Some(dialect) = self.verilog_dialect {
 | 
					        if *debug {
 | 
				
			||||||
 | 
					            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(&self.firtool_extra_args);
 | 
					        cmd.args(firtool_extra_args);
 | 
				
			||||||
        cmd.current_dir(&self.firrtl.base.output);
 | 
					        cmd.current_dir(&output.firrtl.output_dir);
 | 
				
			||||||
        let status = cmd.status()?;
 | 
					        let status = firrtl.base.run_external_command(cmd)?;
 | 
				
			||||||
        if status.success() {
 | 
					        if status.success() {
 | 
				
			||||||
            Ok(output)
 | 
					            Ok(output)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
| 
						 | 
					@ -211,12 +302,217 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -302,6 +598,9 @@ where
 | 
				
			||||||
            CliCommand::Verilog(c) => {
 | 
					            CliCommand::Verilog(c) => {
 | 
				
			||||||
                c.run(arg)?;
 | 
					                c.run(arg)?;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            CliCommand::Formal(c) => {
 | 
				
			||||||
 | 
					                c.run(arg)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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>;
 | 
				
			||||||
| 
						 | 
					@ -2300,6 +2311,7 @@ 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>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2307,6 +2319,7 @@ 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,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -2342,7 +2355,10 @@ 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.top_fir_file_stem.get_or_insert(circuit_name);
 | 
					        let top_fir_file_stem = self
 | 
				
			||||||
 | 
					            .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)?;
 | 
				
			||||||
| 
						 | 
					@ -2370,6 +2386,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 +2397,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 +2412,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 +2430,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 +2492,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 +2526,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-------"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,4 +50,5 @@ 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,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,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								crates/fayalite/src/testing.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								crates/fayalite/src/testing.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
				
			||||||
 | 
					// See Notices.txt for copyright information
 | 
				
			||||||
 | 
					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,6 +6,7 @@ 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};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -162,3 +162,106 @@ pub fn queue<T: Type>(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(todo)]
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use crate::{
 | 
				
			||||||
 | 
					        cli::FormalMode, firrtl::ExportOptions,
 | 
				
			||||||
 | 
					        module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_queue() {
 | 
				
			||||||
 | 
					        #[hdl_module]
 | 
				
			||||||
 | 
					        fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let clk: Clock = m.input();
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let rst: SyncReset = m.input();
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let inp_data: HdlOption<UInt<8>> = m.input();
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let out_ready: Bool = m.input();
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let cd = wire();
 | 
				
			||||||
 | 
					            connect(
 | 
				
			||||||
 | 
					                cd,
 | 
				
			||||||
 | 
					                #[hdl]
 | 
				
			||||||
 | 
					                ClockDomain {
 | 
				
			||||||
 | 
					                    clk,
 | 
				
			||||||
 | 
					                    rst: rst.to_reset(),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let dut = instance(queue(
 | 
				
			||||||
 | 
					                UInt[ConstUsize::<8>],
 | 
				
			||||||
 | 
					                capacity,
 | 
				
			||||||
 | 
					                inp_ready_is_comb,
 | 
				
			||||||
 | 
					                out_valid_is_comb,
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					            connect(dut.cd, cd);
 | 
				
			||||||
 | 
					            connect(dut.inp.data, inp_data);
 | 
				
			||||||
 | 
					            connect(dut.out.ready, out_ready);
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let count = reg_builder().clock_domain(cd).reset(0u32);
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let next_count = wire();
 | 
				
			||||||
 | 
					            connect(next_count, count);
 | 
				
			||||||
 | 
					            connect(count, next_count);
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            if ReadyValid::fire(dut.inp) & !ReadyValid::fire(dut.out) {
 | 
				
			||||||
 | 
					                connect_any(next_count, count + 1u8);
 | 
				
			||||||
 | 
					            } else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) {
 | 
				
			||||||
 | 
					                connect_any(next_count, count - 1u8);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            hdl_assert(clk, count.cmp_eq(dut.count), "");
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let index = reg_builder().clock_domain(cd).reset(HdlNone::<UInt<32>>());
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            let data = reg_builder().clock_domain(cd).reset(HdlNone());
 | 
				
			||||||
 | 
					            #[hdl]
 | 
				
			||||||
 | 
					            match index {
 | 
				
			||||||
 | 
					                HdlNone =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    #[hdl]
 | 
				
			||||||
 | 
					                    if ReadyValid::fire(dut.inp) {
 | 
				
			||||||
 | 
					                        connect(index, HdlSome(0u32));
 | 
				
			||||||
 | 
					                        connect(data, dut.inp.data);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                HdlSome(cur_index) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    #[hdl]
 | 
				
			||||||
 | 
					                    if cur_index.cmp_ge(next_count) {
 | 
				
			||||||
 | 
					                        connect(index, HdlNone());
 | 
				
			||||||
 | 
					                        #[hdl]
 | 
				
			||||||
 | 
					                        if let HdlSome(data) = data {
 | 
				
			||||||
 | 
					                            #[hdl]
 | 
				
			||||||
 | 
					                            if let HdlSome(out_data) = dut.out.data {
 | 
				
			||||||
 | 
					                                hdl_assert(clk, data.cmp_eq(out_data), "");
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                hdl_assert(clk, false.to_expr(), "");
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            hdl_assert(clk, false.to_expr(), "");
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        connect(index, HdlSome((cur_index + 1u8).cast_to_static()));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        assert_formal(
 | 
				
			||||||
 | 
					            queue_test(NonZeroUsize::new(2).unwrap(), false, false),
 | 
				
			||||||
 | 
					            FormalMode::BMC,
 | 
				
			||||||
 | 
					            20,
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            ExportOptions {
 | 
				
			||||||
 | 
					                simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
 | 
				
			||||||
 | 
					                ..ExportOptions::default()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										29
									
								
								crates/fayalite/src/util/streaming_read_utf8.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								crates/fayalite/src/util/streaming_read_utf8.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					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