This commit is contained in:
parent
0611044941
commit
f582013c1b
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…
Reference in a new issue