This commit is contained in:
parent
37d03cec33
commit
0edf380c79
|
@ -1,5 +1,6 @@
|
|||
use clap::Parser;
|
||||
use fayalite::{
|
||||
cli,
|
||||
clock::{Clock, ClockDomain},
|
||||
hdl_module,
|
||||
int::{DynUInt, DynUIntType, IntCmp, IntTypeTrait, UInt},
|
||||
|
@ -41,10 +42,10 @@ struct Cli {
|
|||
#[arg(long, default_value = "1000000", value_parser = clap::value_parser!(u64).range(2..))]
|
||||
clock_frequency: u64,
|
||||
#[command(subcommand)]
|
||||
cli: fayalite::cli::Cli,
|
||||
cli: cli::Cli,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> cli::Result {
|
||||
let cli = Cli::parse();
|
||||
cli.cli.run(blinky(cli.clock_frequency))
|
||||
}
|
||||
|
|
|
@ -4,20 +4,97 @@ use crate::{
|
|||
intern::Interned,
|
||||
module::Module,
|
||||
};
|
||||
use clap::{Parser, Subcommand, ValueHint};
|
||||
use std::{path::PathBuf, process::exit};
|
||||
use clap::{Args, Parser, Subcommand, ValueHint};
|
||||
use std::{fmt, path::PathBuf};
|
||||
|
||||
enum CliErrorImpl {
|
||||
IoError(std::io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
||||
|
||||
pub struct CliError(CliErrorImpl);
|
||||
|
||||
impl fmt::Debug for CliError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 {
|
||||
CliErrorImpl::IoError(e) => e.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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CliError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match &self.0 {
|
||||
CliErrorImpl::IoError(e) => e.source(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for CliError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
CliError(CliErrorImpl::IoError(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RunPhase<Arg> {
|
||||
fn run(self, arg: Arg) -> Result;
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[non_exhaustive]
|
||||
pub struct FirrtlArgs {
|
||||
/// the directory to put the generated FIRRTL 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
|
||||
#[arg(long)]
|
||||
pub file_stem: Option<String>,
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BundleValue> RunPhase<Module<T>> for FirrtlArgs
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
fn run(self, top_module: Module<T>) -> Result {
|
||||
self.run_impl(top_module.canonical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BundleValue> RunPhase<Interned<Module<T>>> for FirrtlArgs
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
fn run(self, top_module: Interned<Module<T>>) -> Result {
|
||||
self.run(*top_module)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum CliCommand {
|
||||
/// Generate FIRRTL
|
||||
Firrtl {
|
||||
/// the directory to put the generated FIRRTL and associated files in
|
||||
#[arg(short, long, value_hint = ValueHint::DirPath)]
|
||||
output: PathBuf,
|
||||
/// the stem of the generated .fir file, e.g. to get foo.fir, pass --file-stem=foo
|
||||
#[arg(long)]
|
||||
file_stem: Option<String>,
|
||||
},
|
||||
Firrtl(FirrtlArgs),
|
||||
}
|
||||
|
||||
/// a simple CLI
|
||||
|
@ -25,26 +102,29 @@ enum CliCommand {
|
|||
/// Use like:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use fayalite::{hdl_module, cli::Cli};
|
||||
/// # use fayalite::hdl_module;
|
||||
/// # #[hdl_module]
|
||||
/// # fn my_module() {}
|
||||
/// fn main() {
|
||||
/// Cli::parse().run(my_module());
|
||||
/// use fayalite::cli;
|
||||
///
|
||||
/// fn main() -> cli::Result {
|
||||
/// cli::Cli::parse().run(my_module())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You can also use it with a larger [`clap`]-based CLI like so:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use fayalite::{hdl_module};
|
||||
/// # use fayalite::hdl_module;
|
||||
/// # #[hdl_module]
|
||||
/// # fn my_module() {}
|
||||
/// use clap::{Subcommand, Parser};
|
||||
/// use fayalite::cli;
|
||||
///
|
||||
/// #[derive(Subcommand)]
|
||||
/// pub enum Cmd {
|
||||
/// #[command(flatten)]
|
||||
/// Fayalite(fayalite::cli::Cli),
|
||||
/// Fayalite(cli::Cli),
|
||||
/// MySpecialCommand {
|
||||
/// #[arg(long)]
|
||||
/// foo: bool,
|
||||
|
@ -54,14 +134,15 @@ enum CliCommand {
|
|||
/// #[derive(Parser)]
|
||||
/// pub struct Cli {
|
||||
/// #[command(subcommand)]
|
||||
/// cmd: Cmd, // or just use fayalite::cli::Cli directly
|
||||
/// cmd: Cmd, // or just use cli::Cli directly if you don't need more subcommands
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// fn main() -> cli::Result {
|
||||
/// match Cli::parse().cmd {
|
||||
/// Cmd::Fayalite(v) => v.run(my_module()),
|
||||
/// Cmd::Fayalite(v) => v.run(my_module())?,
|
||||
/// Cmd::MySpecialCommand { foo } => println!("special: foo={foo}"),
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Parser)]
|
||||
|
@ -86,33 +167,27 @@ impl clap::Subcommand for Cli {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> RunPhase<T> for Cli
|
||||
where
|
||||
FirrtlArgs: RunPhase<T>,
|
||||
{
|
||||
fn run(self, arg: T) -> Result {
|
||||
match self.subcommand {
|
||||
CliCommand::Firrtl(firrtl_args) => firrtl_args.run(arg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
/// forwards to [`clap::Parser::parse()`] so you don't have to import [`clap::Parser`]
|
||||
pub fn parse() -> Self {
|
||||
clap::Parser::parse()
|
||||
}
|
||||
fn run_impl(self, top_module: Module<DynBundle>) -> ! {
|
||||
match self.subcommand {
|
||||
CliCommand::Firrtl { output, file_stem } => {
|
||||
let result = firrtl::export(
|
||||
firrtl::FileBackend {
|
||||
dir_path: output,
|
||||
top_fir_file_stem: file_stem,
|
||||
},
|
||||
&top_module,
|
||||
);
|
||||
if let Err(e) = result {
|
||||
eprintln!("Error: {:?}", eyre::Report::new(e));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
pub fn run<T: BundleValue>(self, top_module: Interned<Module<T>>) -> !
|
||||
/// forwards to [`RunPhase::run()`] so you don't have to import [`RunPhase`]
|
||||
pub fn run<T>(self, top_module: T) -> Result
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
Self: RunPhase<T>,
|
||||
{
|
||||
self.run_impl(top_module.canonical())
|
||||
RunPhase::run(self, top_module)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue