forked from libre-chip/fayalite
		
	add cli for compiling to verilog
This commit is contained in:
		
							parent
							
								
									0611044941
								
							
						
					
					
						commit
						f582013c1b
					
				
					 4 changed files with 183 additions and 53 deletions
				
			
		
							
								
								
									
										34
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										34
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -199,6 +199,12 @@ dependencies = [ | |||
|  "crypto-common", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "either" | ||||
| version = "1.13.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "equivalent" | ||||
| version = "1.0.1" | ||||
|  | @ -246,6 +252,7 @@ dependencies = [ | |||
|  "serde", | ||||
|  "serde_json", | ||||
|  "trybuild", | ||||
|  "which", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -321,6 +328,15 @@ version = "0.5.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "home" | ||||
| version = "0.5.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" | ||||
| dependencies = [ | ||||
|  "windows-sys", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "indenter" | ||||
| version = "0.3.3" | ||||
|  | @ -596,6 +612,18 @@ version = "0.9.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "which" | ||||
| version = "6.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" | ||||
| dependencies = [ | ||||
|  "either", | ||||
|  "home", | ||||
|  "rustix", | ||||
|  "winsafe", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winapi" | ||||
| version = "0.3.9" | ||||
|  | @ -693,6 +721,12 @@ version = "0.52.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winsafe" | ||||
| version = "0.0.19" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wyz" | ||||
| version = "0.5.1" | ||||
|  |  | |||
|  | @ -21,8 +21,9 @@ num-traits = { workspace = true } | |||
| fayalite-proc-macros = { workspace = true } | ||||
| serde = { workspace = true } | ||||
| serde_json = { workspace = true } | ||||
| clap = { version = "4.5.9", features = ["derive"] } | ||||
| clap = { version = "4.5.9", features = ["derive", "env"] } | ||||
| eyre = "0.6.12" | ||||
| which = "6.0.1" | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| trybuild = { workspace = true } | ||||
|  |  | |||
|  | @ -4,72 +4,93 @@ use crate::{ | |||
|     intern::Interned, | ||||
|     module::Module, | ||||
| }; | ||||
| use clap::{Args, Parser, Subcommand, ValueHint}; | ||||
| use std::{fmt, path::PathBuf}; | ||||
| 
 | ||||
| enum CliErrorImpl { | ||||
|     IoError(std::io::Error), | ||||
| } | ||||
| use clap::{ | ||||
|     builder::{OsStringValueParser, TypedValueParser}, | ||||
|     Args, Parser, Subcommand, ValueHint, | ||||
| }; | ||||
| use eyre::{eyre, Report}; | ||||
| use std::{error, ffi::OsString, fmt, io, path::PathBuf, process}; | ||||
| 
 | ||||
| pub type Result<T = (), E = CliError> = std::result::Result<T, E>; | ||||
| 
 | ||||
| pub struct CliError(CliErrorImpl); | ||||
| pub struct CliError(Report); | ||||
| 
 | ||||
| impl fmt::Debug for CliError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match &self.0 { | ||||
|             CliErrorImpl::IoError(e) => e.fmt(f), | ||||
|         } | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for CliError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match &self.0 { | ||||
|             CliErrorImpl::IoError(e) => e.fmt(f), | ||||
|         } | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::error::Error for CliError { | ||||
|     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||||
|         match &self.0 { | ||||
|             CliErrorImpl::IoError(e) => e.source(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl error::Error for CliError {} | ||||
| 
 | ||||
| impl From<std::io::Error> for CliError { | ||||
|     fn from(value: std::io::Error) -> Self { | ||||
|         CliError(CliErrorImpl::IoError(value)) | ||||
| impl From<io::Error> for CliError { | ||||
|     fn from(value: io::Error) -> Self { | ||||
|         CliError(Report::new(value)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait RunPhase<Arg> { | ||||
|     fn run(self, arg: Arg) -> Result; | ||||
|     type Output; | ||||
|     fn run(&self, arg: Arg) -> Result<Self::Output>; | ||||
| } | ||||
| 
 | ||||
| #[derive(Args)] | ||||
| #[derive(Args, Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct FirrtlArgs { | ||||
|     /// the directory to put the generated FIRRTL and associated files in
 | ||||
| pub struct BaseArgs { | ||||
|     /// the directory to put the generated main output file and associated files in
 | ||||
|     #[arg(short, long, value_hint = ValueHint::DirPath)] | ||||
|     pub output: PathBuf, | ||||
|     /// the stem of the generated .fir file, e.g. to get foo.fir, pass --file-stem=foo
 | ||||
|     /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
 | ||||
|     #[arg(long)] | ||||
|     pub file_stem: Option<String>, | ||||
| } | ||||
| 
 | ||||
| impl BaseArgs { | ||||
|     pub fn to_firrtl_file_backend(&self) -> firrtl::FileBackend { | ||||
|         firrtl::FileBackend { | ||||
|             dir_path: self.output.clone(), | ||||
|             top_fir_file_stem: self.file_stem.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Args, Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct FirrtlArgs { | ||||
|     #[command(flatten)] | ||||
|     pub base: BaseArgs, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct FirrtlOutput { | ||||
|     pub file_stem: String, | ||||
| } | ||||
| 
 | ||||
| impl FirrtlOutput { | ||||
|     pub fn firrtl_file(&self, args: &FirrtlArgs) -> PathBuf { | ||||
|         let mut retval = args.base.output.join(&self.file_stem); | ||||
|         retval.set_extension("fir"); | ||||
|         retval | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FirrtlArgs { | ||||
|     fn run_impl(self, top_module: Module<DynBundle>) -> Result { | ||||
|         firrtl::export( | ||||
|             firrtl::FileBackend { | ||||
|                 dir_path: self.output, | ||||
|                 top_fir_file_stem: self.file_stem, | ||||
|             }, | ||||
|             &top_module, | ||||
|         )?; | ||||
|         Ok(()) | ||||
|     fn run_impl(&self, top_module: Module<DynBundle>) -> Result<FirrtlOutput> { | ||||
|         let firrtl::FileBackend { | ||||
|             top_fir_file_stem, .. | ||||
|         } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?; | ||||
|         Ok(FirrtlOutput { | ||||
|             file_stem: top_fir_file_stem.expect( | ||||
|                 "export is known to set the file stem from the circuit name if not provided", | ||||
|             ), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -77,7 +98,8 @@ impl<T: BundleValue> RunPhase<Module<T>> for FirrtlArgs | |||
| where | ||||
|     T::Type: BundleType<Value = T>, | ||||
| { | ||||
|     fn run(self, top_module: Module<T>) -> Result { | ||||
|     type Output = FirrtlOutput; | ||||
|     fn run(&self, top_module: Module<T>) -> Result<Self::Output> { | ||||
|         self.run_impl(top_module.canonical()) | ||||
|     } | ||||
| } | ||||
|  | @ -86,15 +108,83 @@ impl<T: BundleValue> RunPhase<Interned<Module<T>>> for FirrtlArgs | |||
| where | ||||
|     T::Type: BundleType<Value = T>, | ||||
| { | ||||
|     fn run(self, top_module: Interned<Module<T>>) -> Result { | ||||
|     type Output = FirrtlOutput; | ||||
|     fn run(&self, top_module: Interned<Module<T>>) -> Result<Self::Output> { | ||||
|         self.run(*top_module) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Subcommand)] | ||||
| #[derive(Args, Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct VerilogArgs { | ||||
|     #[command(flatten)] | ||||
|     pub firrtl: FirrtlArgs, | ||||
|     #[arg(
 | ||||
|         long, | ||||
|         default_value = "firtool", | ||||
|         env = "FIRTOOL", | ||||
|         value_hint = ValueHint::CommandName, | ||||
|         value_parser = OsStringValueParser::new().try_map(which::which) | ||||
|     )] | ||||
|     pub firtool: PathBuf, | ||||
|     #[arg(long)] | ||||
|     pub firtool_extra_args: Vec<OsString>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct VerilogOutput { | ||||
|     pub firrtl: FirrtlOutput, | ||||
| } | ||||
| 
 | ||||
| impl VerilogOutput { | ||||
|     pub fn verilog_file(&self, args: &VerilogArgs) -> PathBuf { | ||||
|         let mut retval = args.firrtl.base.output.join(&self.firrtl.file_stem); | ||||
|         retval.set_extension("v"); | ||||
|         retval | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl VerilogArgs { | ||||
|     fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result<VerilogOutput> { | ||||
|         let output = VerilogOutput { | ||||
|             firrtl: firrtl_output, | ||||
|         }; | ||||
|         let mut cmd = process::Command::new(&self.firtool); | ||||
|         cmd.arg(output.firrtl.firrtl_file(&self.firrtl)) | ||||
|             .arg("-o") | ||||
|             .arg(output.verilog_file(self)) | ||||
|             .args(&self.firtool_extra_args) | ||||
|             .current_dir(&self.firrtl.base.output); | ||||
|         let status = cmd.status()?; | ||||
|         if status.success() { | ||||
|             Ok(output) | ||||
|         } else { | ||||
|             Err(CliError(eyre!( | ||||
|                 "running {} failed: {status}", | ||||
|                 self.firtool.display() | ||||
|             ))) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Arg> RunPhase<Arg> for VerilogArgs | ||||
| where | ||||
|     FirrtlArgs: RunPhase<Arg, Output = FirrtlOutput>, | ||||
| { | ||||
|     type Output = VerilogOutput; | ||||
|     fn run(&self, arg: Arg) -> Result<Self::Output> { | ||||
|         let firrtl_output = self.firrtl.run(arg)?; | ||||
|         self.run_impl(firrtl_output) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Subcommand, Debug)] | ||||
| enum CliCommand { | ||||
|     /// Generate FIRRTL
 | ||||
|     Firrtl(FirrtlArgs), | ||||
|     /// Generate Verilog
 | ||||
|     Verilog(VerilogArgs), | ||||
| } | ||||
| 
 | ||||
| /// a simple CLI
 | ||||
|  | @ -145,7 +235,7 @@ enum CliCommand { | |||
| ///     Ok(())
 | ||||
| /// }
 | ||||
| /// ```
 | ||||
| #[derive(Parser)] | ||||
| #[derive(Parser, Debug)] | ||||
| // clear things that would be crate-specific
 | ||||
| #[command(name = "Fayalite Simple CLI", about = None, long_about = None)] | ||||
| pub struct Cli { | ||||
|  | @ -169,12 +259,19 @@ impl clap::Subcommand for Cli { | |||
| 
 | ||||
| impl<T> RunPhase<T> for Cli | ||||
| where | ||||
|     FirrtlArgs: RunPhase<T>, | ||||
|     FirrtlArgs: RunPhase<T, Output = FirrtlOutput>, | ||||
| { | ||||
|     fn run(self, arg: T) -> Result { | ||||
|         match self.subcommand { | ||||
|             CliCommand::Firrtl(firrtl_args) => firrtl_args.run(arg), | ||||
|     type Output = (); | ||||
|     fn run(&self, arg: T) -> Result<Self::Output> { | ||||
|         match &self.subcommand { | ||||
|             CliCommand::Firrtl(c) => { | ||||
|                 c.run(arg)?; | ||||
|             } | ||||
|             CliCommand::Verilog(c) => { | ||||
|                 c.run(arg)?; | ||||
|             } | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -184,9 +281,9 @@ impl Cli { | |||
|         clap::Parser::parse() | ||||
|     } | ||||
|     /// forwards to [`RunPhase::run()`] so you don't have to import [`RunPhase`]
 | ||||
|     pub fn run<T>(self, top_module: T) -> Result | ||||
|     pub fn run<T>(&self, top_module: T) -> Result<()> | ||||
|     where | ||||
|         Self: RunPhase<T>, | ||||
|         Self: RunPhase<T, Output = ()>, | ||||
|     { | ||||
|         RunPhase::run(self, top_module) | ||||
|     } | ||||
|  |  | |||
|  | @ -2015,8 +2015,7 @@ impl<'a> Exporter<'a> { | |||
|                     let ty = self.type_state.ty(reg.ty()); | ||||
|                     let clk = self.expr(reg.clock_domain().clk.to_dyn(), &definitions, false); | ||||
|                     if let Some(init) = reg.init() { | ||||
|                         let rst = | ||||
|                             self.expr(reg.clock_domain().rst.to_dyn(), &definitions, false); | ||||
|                         let rst = self.expr(reg.clock_domain().rst.to_dyn(), &definitions, false); | ||||
|                         let init = self.expr(init.to_dyn(), &definitions, false); | ||||
|                         writeln!( | ||||
|                             body, | ||||
|  | @ -2267,9 +2266,8 @@ impl FileBackendTrait for FileBackend { | |||
|         circuit_name: String, | ||||
|         contents: String, | ||||
|     ) -> Result<(), Self::Error> { | ||||
|         let mut path = self | ||||
|             .dir_path | ||||
|             .join(self.top_fir_file_stem.as_deref().unwrap_or(&circuit_name)); | ||||
|         let top_fir_file_stem = self.top_fir_file_stem.get_or_insert(circuit_name); | ||||
|         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()) { | ||||
|             fs::create_dir_all(parent)?; | ||||
|         } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue