diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 001168f..1b9910e 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -21,5 +21,4 @@ jobs: - run: cargo test --doc --features=unstable-doc - run: cargo doc --features=unstable-doc - run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher - - run: cargo run --example blinky yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/blinky-out - - run: cargo run --example tx_only_uart yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/tx_only_uart-out + - run: cargo run --example blinky yosys-nextpnr-xray --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db --device xc7a100ticsg324-1L -o target/blinky-out --clock-frequency=$((1000*1000*100)) diff --git a/Cargo.lock b/Cargo.lock index be5f3bc..f4b564a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,6 @@ dependencies = [ "jobslot", "num-bigint", "num-traits", - "ordered-float", "petgraph", "serde", "serde_json", @@ -525,17 +524,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "ordered-float" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" -dependencies = [ - "num-traits", - "rand", - "serde", -] - [[package]] name = "petgraph" version = "0.8.1" @@ -588,25 +576,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", - "serde", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "serde", -] - [[package]] name = "rustix" version = "0.38.31" diff --git a/Cargo.toml b/Cargo.toml index 2380ea7..b905f73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ indexmap = { version = "2.5.0", features = ["serde"] } jobslot = "0.2.23" num-bigint = "0.4.6" num-traits = "0.2.16" -ordered-float = { version = "5.1.0", features = ["serde"] } petgraph = "0.8.1" prettyplease = "0.2.20" proc-macro2 = "1.0.83" diff --git a/README.md b/README.md index 8e7f275..0b91833 100644 --- a/README.md +++ b/README.md @@ -8,73 +8,6 @@ Fayalite is a library for designing digital hardware -- a hardware description l [FIRRTL]: https://github.com/chipsalliance/firrtl-spec -# Building the [Blinky example] for the Arty A7 100T on Linux - -[Blinky example]: crates/fayalite/examples/blinky.rs - -This uses the container image containing all the external programs and files that Fayalite needs to build for FPGAs, the sources for the container image are in https://git.libre-chip.org/libre-chip/fayalite-deps - -Steps: - -Install podman (or docker). - -Run: -```bash -podman run --rm --security-opt label=disable --volume="$(pwd):$(pwd)" -w="$(pwd)" -it git.libre-chip.org/libre-chip/fayalite-deps:latest cargo run --example blinky yosys-nextpnr-xray --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db --platform arty-a7-100t -o target/blinky-out -``` - -To actually program the FPGA, you'll need to install [openFPGALoader] on your host OS: - -[openFPGALoader]: https://github.com/trabucayre/openFPGALoader - -On Debian 12: -```bash -sudo apt update && sudo apt install openfpgaloader -``` - -Then program the FPGA: -```bash -sudo openFPGALoader --board arty_a7_100t target/blinky-out/blinky.bit -``` - -This will program the FPGA but leave the Flash chip unmodified, so the FPGA will revert when the board is power-cycled. - -To program the Flash also, so it stays programmed when power-cycling the board: - -```bash -sudo openFPGALoader --board arty_a7_100t -f target/blinky-out/blinky.bit -``` - -# Building the [Transmit-only UART example] for the Arty A7 100T on Linux - -[Transmit-only UART example]: crates/fayalite/examples/tx_only_uart.rs - -Follow the steps above of building the Blinky example, but replace `blinky` with `tx_only_uart`. - -View the output using [tio](https://github.com/tio/tio) which you can install in Debian using `apt`. - -Find the correct USB device: -```bash -sudo tio --list -``` - -You want the device with a name like (note the `if01`, `if00` is presumably the JTAG port): -`/dev/serial/by-id/usb-Digilent_Digilent_USB_Device_210319B4A51E-if01-port0` - -Connect to the serial port: -```bash -sudo tio -b115200 /dev/serial/by-id/put-your-device-id-here -``` - -You'll see (repeating endlessly): -```text -Hello World from Fayalite!!! -Hello World from Fayalite!!! -Hello World from Fayalite!!! -``` - -Press Ctrl+T then `q` to exit tio. - # Funding ## NLnet Grants diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index fdf1c87..2403ff5 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -26,7 +26,6 @@ hashbrown.workspace = true jobslot.workspace = true num-bigint.workspace = true num-traits.workspace = true -ordered-float.workspace = true petgraph.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/crates/fayalite/examples/blinky.rs b/crates/fayalite/examples/blinky.rs index 75799fd..8682a33 100644 --- a/crates/fayalite/examples/blinky.rs +++ b/crates/fayalite/examples/blinky.rs @@ -1,64 +1,55 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use fayalite::prelude::*; +use fayalite::{ + build::{ToArgs, WriteArgs}, + prelude::*, +}; #[hdl_module] -fn blinky(platform_io_builder: PlatformIOBuilder<'_>) { - let clk_input = - platform_io_builder.peripherals_with_type::()[0].use_peripheral(); - let rst = platform_io_builder.peripherals_with_type::()[0].use_peripheral(); +fn blinky(clock_frequency: u64) { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let rst: SyncReset = m.input(); let cd = #[hdl] ClockDomain { - clk: clk_input.clk, - rst, + clk, + rst: rst.to_reset(), }; - let max_value = (Expr::ty(clk_input).frequency() / 2.0).round_ties_even() as u64 - 1; + let max_value = clock_frequency / 2 - 1; let int_ty = UInt::range_inclusive(0..=max_value); #[hdl] let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); #[hdl] let output_reg: Bool = reg_builder().clock_domain(cd).reset(false); #[hdl] - let rgb_output_reg = reg_builder().clock_domain(cd).reset( - #[hdl] - peripherals::RgbLed { - r: false, - g: false, - b: false, - }, - ); - #[hdl] if counter_reg.cmp_eq(max_value) { connect_any(counter_reg, 0u8); connect(output_reg, !output_reg); - connect(rgb_output_reg.r, !rgb_output_reg.r); - #[hdl] - if rgb_output_reg.r { - connect(rgb_output_reg.g, !rgb_output_reg.g); - #[hdl] - if rgb_output_reg.g { - connect(rgb_output_reg.b, !rgb_output_reg.b); - } - } } else { connect_any(counter_reg, counter_reg + 1_hdl_u1); } - for led in platform_io_builder.peripherals_with_type::() { - if let Ok(led) = led.try_use_peripheral() { - connect(led.on, output_reg); - } - } - for rgb_led in platform_io_builder.peripherals_with_type::() { - if let Ok(rgb_led) = rgb_led.try_use_peripheral() { - connect(rgb_led, rgb_output_reg); - } - } #[hdl] - let io = m.add_platform_io(platform_io_builder); + let led: Bool = m.output(); + connect(led, output_reg); +} + +#[derive(clap::Args, Clone, PartialEq, Eq, Hash, Debug)] +struct ExtraArgs { + /// clock frequency in hertz + #[arg(long, default_value = "1000000", value_parser = clap::value_parser!(u64).range(2..))] + clock_frequency: u64, +} + +impl ToArgs for ExtraArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { clock_frequency } = self; + args.write_arg(format!("--clock-frequency={clock_frequency}")); + } } fn main() { - ::main("blinky", |_, platform, _| { - Ok(JobParams::new(platform.wrap_main_module(blinky))) + BuildCli::main(|_cli, ExtraArgs { clock_frequency }| { + Ok(JobParams::new(blinky(clock_frequency), "blinky")) }); } diff --git a/crates/fayalite/examples/tx_only_uart.rs b/crates/fayalite/examples/tx_only_uart.rs deleted file mode 100644 index 5c20b39..0000000 --- a/crates/fayalite/examples/tx_only_uart.rs +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information -use clap::builder::TypedValueParser; -use fayalite::{ - build::{ToArgs, WriteArgs}, - platform::PeripheralRef, - prelude::*, -}; -use ordered_float::NotNan; - -fn pick_clock<'a>( - platform_io_builder: &PlatformIOBuilder<'a>, -) -> PeripheralRef<'a, peripherals::ClockInput> { - let mut clks = platform_io_builder.peripherals_with_type::(); - clks.sort_by_key(|clk| { - // sort clocks by preference, smaller return values means higher preference - let mut frequency = clk.ty().frequency(); - let priority; - if frequency < 10e6 { - frequency = -frequency; // prefer bigger frequencies - priority = 1; - } else if frequency > 50e6 { - // prefer smaller frequencies - priority = 2; // least preferred - } else { - priority = 0; // most preferred - frequency = (frequency - 25e6).abs(); // prefer closer to 25MHz - } - (priority, NotNan::new(frequency).expect("should be valid")) - }); - clks[0] -} - -#[hdl_module] -fn tx_only_uart( - platform_io_builder: PlatformIOBuilder<'_>, - divisor: f64, - message: impl AsRef<[u8]>, -) { - let message = message.as_ref(); - let clk_input = pick_clock(&platform_io_builder).use_peripheral(); - let rst = platform_io_builder.peripherals_with_type::()[0].use_peripheral(); - let cd = #[hdl] - ClockDomain { - clk: clk_input.clk, - rst, - }; - let numerator = 1u128 << 16; - let denominator = (divisor * numerator as f64).round() as u128; - - #[hdl] - let remainder_reg: UInt<128> = reg_builder().clock_domain(cd).reset(0u128); - - #[hdl] - let sum: UInt<128> = wire(); - connect_any(sum, remainder_reg + numerator); - - #[hdl] - let tick_reg = reg_builder().clock_domain(cd).reset(false); - connect(tick_reg, false); - - #[hdl] - let next_remainder: UInt<128> = wire(); - connect(remainder_reg, next_remainder); - - #[hdl] - if sum.cmp_ge(denominator) { - connect_any(next_remainder, sum - denominator); - connect(tick_reg, true); - } else { - connect(next_remainder, sum); - } - - #[hdl] - let uart_state_reg = reg_builder().clock_domain(cd).reset(0_hdl_u4); - #[hdl] - let next_uart_state: UInt<4> = wire(); - - connect_any(next_uart_state, uart_state_reg + 1u8); - - #[hdl] - let message_mem: Array> = wire(Array[UInt::new_static()][message.len()]); - for (message, message_mem) in message.iter().zip(message_mem) { - connect(message_mem, *message); - } - #[hdl] - let addr_reg: UInt<32> = reg_builder().clock_domain(cd).reset(0u32); - #[hdl] - let next_addr: UInt<32> = wire(); - connect(next_addr, addr_reg); - - #[hdl] - let tx = reg_builder().clock_domain(cd).reset(true); - - #[hdl] - let tx_bits: Array = wire(); - - connect(tx_bits[0], false); // start bit - connect(tx_bits[9], true); // stop bit - - for i in 0..8 { - connect(tx_bits[i + 1], message_mem[addr_reg][i]); // data bits - } - - connect(tx, tx_bits[uart_state_reg]); - - #[hdl] - if uart_state_reg.cmp_eq(Expr::ty(tx_bits).len() - 1) { - connect(next_uart_state, 0_hdl_u4); - let next_addr_val = addr_reg + 1u8; - #[hdl] - if next_addr_val.cmp_lt(message.len()) { - connect_any(next_addr, next_addr_val); - } else { - connect(next_addr, 0u32); - } - } - - #[hdl] - if tick_reg { - connect(uart_state_reg, next_uart_state); - connect(addr_reg, next_addr); - } - - for uart in platform_io_builder.peripherals_with_type::() { - connect(uart.use_peripheral().tx, tx); - } - - #[hdl] - let io = m.add_platform_io(platform_io_builder); -} - -fn parse_baud_rate( - v: impl AsRef, -) -> Result, Box> { - let retval: NotNan = v - .as_ref() - .parse() - .map_err(|_| "invalid baud rate, must be a finite positive floating-point value")?; - if *retval > 0.0 && retval.is_finite() { - Ok(retval) - } else { - Err("baud rate must be finite and positive".into()) - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] -pub struct ExtraArgs { - #[arg(long, value_parser = clap::builder::StringValueParser::new().try_map(parse_baud_rate), default_value = "115200")] - pub baud_rate: NotNan, - #[arg(long, default_value = "Hello World from Fayalite!!!\r\n", value_parser = clap::builder::NonEmptyStringValueParser::new())] - pub message: String, -} - -impl ToArgs for ExtraArgs { - fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { - let Self { baud_rate, message } = self; - args.write_display_arg(format_args!("--baud-rate={baud_rate}")); - args.write_long_option_eq("message", message); - } -} - -fn main() { - type Cli = BuildCli; - Cli::main( - "tx_only_uart", - |_, platform, ExtraArgs { baud_rate, message }| { - Ok(JobParams::new(platform.try_wrap_main_module(|io| { - let clk = pick_clock(&io).ty(); - let divisor = clk.frequency() / *baud_rate; - let baud_rate_error = |msg| { - ::command() - .error(clap::error::ErrorKind::ValueValidation, msg) - }; - const HUGE_DIVISOR: f64 = u64::MAX as f64; - match divisor { - divisor if !divisor.is_finite() => { - return Err(baud_rate_error("bad baud rate")); - } - HUGE_DIVISOR.. => return Err(baud_rate_error("baud rate is too small")), - 4.0.. => {} - _ => return Err(baud_rate_error("baud rate is too large")), - } - Ok(tx_only_uart(io, divisor, message)) - })?)) - }, - ); -} diff --git a/crates/fayalite/src/build.rs b/crates/fayalite/src/build.rs index a9e9635..ccf81d1 100644 --- a/crates/fayalite/src/build.rs +++ b/crates/fayalite/src/build.rs @@ -6,7 +6,6 @@ use crate::{ bundle::{Bundle, BundleType}, intern::{Intern, InternSlice, Interned}, module::Module, - platform::{DynPlatform, Platform}, util::{job_server::AcquiredJob, os_str_strip_prefix}, vendor, }; @@ -38,12 +37,15 @@ pub mod registry; pub mod verilog; pub(crate) fn built_in_job_kinds() -> impl IntoIterator { - [DynJobKind::new(BaseJobKind)] - .into_iter() - .chain(firrtl::built_in_job_kinds()) - .chain(formal::built_in_job_kinds()) - .chain(vendor::built_in_job_kinds()) - .chain(verilog::built_in_job_kinds()) + [ + DynJobKind::new(BaseJobKind), + DynJobKind::new(CreateOutputDirJobKind), + ] + .into_iter() + .chain(firrtl::built_in_job_kinds()) + .chain(formal::built_in_job_kinds()) + .chain(vendor::built_in_job_kinds()) + .chain(verilog::built_in_job_kinds()) } #[derive(Clone, Hash, PartialEq, Eq, Debug)] @@ -316,7 +318,6 @@ impl JobKindAndArgs { self, dependencies: ::KindsAndArgs, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { K::args_to_jobs( JobArgsAndDependencies { @@ -324,7 +325,6 @@ impl JobKindAndArgs { dependencies, }, params, - global_params, ) } } @@ -410,9 +410,6 @@ impl JobAndDependencies { { GetJob::get_job(self) } - pub fn base_job(&self) -> &BaseJob { - self.job.kind.base_job(&self.job.job, &self.dependencies) - } } impl Clone for JobAndDependencies @@ -449,17 +446,8 @@ where } impl JobArgsAndDependencies { - pub fn args_to_jobs( - self, - params: &JobParams, - global_params: &GlobalParams, - ) -> eyre::Result> { - K::args_to_jobs(self, params, global_params) - } - pub fn base_job_args(&self) -> &BaseJobArgs { - self.args - .kind - .base_job_args(&self.args.args, &self.dependencies) + pub fn args_to_jobs(self, params: &JobParams) -> eyre::Result> { + K::args_to_jobs(self, params) } } @@ -467,7 +455,6 @@ impl>, D: JobKind> JobArgsAn pub fn args_to_jobs_simple( self, params: &JobParams, - global_params: &GlobalParams, f: F, ) -> eyre::Result> where @@ -477,7 +464,7 @@ impl>, D: JobKind> JobArgsAn args: JobKindAndArgs { kind, args }, dependencies, } = self; - let mut dependencies = dependencies.args_to_jobs(params, global_params)?; + let mut dependencies = dependencies.args_to_jobs(params)?; let job = f(kind, args, &mut dependencies)?; Ok(JobAndDependencies { job: JobAndKind { kind, job }, @@ -492,7 +479,6 @@ impl>, D: pub fn args_to_jobs_external_simple( self, params: &JobParams, - global_params: &GlobalParams, f: F, ) -> eyre::Result<( C::AdditionalJobData, @@ -508,7 +494,7 @@ impl>, D: args: JobKindAndArgs { kind: _, args }, dependencies, } = self; - let mut dependencies = dependencies.args_to_jobs(params, global_params)?; + let mut dependencies = dependencies.args_to_jobs(params)?; let additional_job_data = f(args, &mut dependencies)?; Ok((additional_job_data, dependencies)) } @@ -544,15 +530,6 @@ pub trait JobDependencies: 'static + Send + Sync + Hash + Eq + fmt::Debug + Copy } } -pub trait JobDependenciesHasBase: JobDependencies { - fn base_job_args(args: &Self::KindsAndArgs) -> &BaseJobArgs; - fn base_job(jobs: &Self::JobsAndKinds) -> &BaseJob; - #[track_caller] - fn base_job_args_dyn(dependencies_args: &[DynJobArgs]) -> &BaseJobArgs; - #[track_caller] - fn base_job_dyn(dependencies: &[DynJob]) -> &BaseJob; -} - impl JobDependencies for JobKindAndDependencies { type KindsAndArgs = JobArgsAndDependencies; type JobsAndKinds = JobAndDependencies; @@ -592,44 +569,6 @@ impl JobDependencies for JobKindAndDependencies { } } -impl JobDependenciesHasBase for JobKindAndDependencies { - fn base_job_args(args: &Self::KindsAndArgs) -> &BaseJobArgs { - args.base_job_args() - } - - fn base_job(jobs: &Self::JobsAndKinds) -> &BaseJob { - jobs.base_job() - } - - #[track_caller] - fn base_job_args_dyn(dependencies_args: &[DynJobArgs]) -> &BaseJobArgs { - let [dependencies_args @ .., args] = dependencies_args else { - panic!("wrong number of dependencies"); - }; - let Some((kind, args)) = args.downcast_ref::() else { - panic!( - "wrong type of dependency, expected {} got:\n{args:?}", - std::any::type_name::() - ) - }; - kind.base_job_args_dyn(args, dependencies_args) - } - - #[track_caller] - fn base_job_dyn(dependencies: &[DynJob]) -> &BaseJob { - let [dependencies @ .., job] = dependencies else { - panic!("wrong number of dependencies"); - }; - let Some((kind, job)) = job.downcast_ref::() else { - panic!( - "wrong type of dependency, expected {} got:\n{job:?}", - std::any::type_name::() - ) - }; - kind.base_job_dyn(job, dependencies) - } -} - macro_rules! impl_job_dependencies { (@impl $(($v:ident: $T:ident),)*) => { impl<$($T: JobDependencies),*> JobDependencies for ($($T,)*) { @@ -685,6 +624,7 @@ impl_job_dependencies! { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct JobParams { main_module: Module, + application_name: Interned, } impl AsRef for JobParams { @@ -694,65 +634,24 @@ impl AsRef for JobParams { } impl JobParams { - pub fn new_canonical(main_module: Module) -> Self { - Self { main_module } + pub fn new_canonical(main_module: Module, application_name: Interned) -> Self { + Self { + main_module, + application_name, + } } - pub fn new(main_module: impl AsRef>) -> Self { - Self::new_canonical(main_module.as_ref().canonical()) + pub fn new( + main_module: impl AsRef>, + application_name: impl AsRef, + ) -> Self { + Self::new_canonical( + main_module.as_ref().canonical(), + application_name.as_ref().intern(), + ) } pub fn main_module(&self) -> &Module { &self.main_module } -} - -#[derive(Clone, Debug)] -pub struct GlobalParams { - top_level_cmd: Option, - application_name: Interned, -} - -impl AsRef for GlobalParams { - fn as_ref(&self) -> &Self { - self - } -} - -impl GlobalParams { - pub fn new(top_level_cmd: Option, application_name: impl AsRef) -> Self { - Self { - top_level_cmd, - application_name: application_name.as_ref().intern(), - } - } - pub fn top_level_cmd(&self) -> Option<&clap::Command> { - self.top_level_cmd.as_ref() - } - pub fn into_top_level_cmd(self) -> Option { - self.top_level_cmd - } - pub fn extract_clap_error(&self, e: eyre::Report) -> eyre::Result { - let e = e.downcast::()?; - Ok(match &self.top_level_cmd { - Some(cmd) => e.with_cmd(cmd), - None => e, - }) - } - pub fn exit_if_clap_error(&self, e: eyre::Report) -> eyre::Report { - match self.extract_clap_error(e) { - Ok(e) => e.exit(), - Err(e) => e, - } - } - pub fn clap_error( - &self, - kind: clap::error::ErrorKind, - message: impl fmt::Display, - ) -> clap::Error { - match self.top_level_cmd.clone() { - Some(top_level_cmd) => top_level_cmd.clone().error(kind, message), - None => clap::Error::raw(kind, message), - } - } pub fn application_name(&self) -> Interned { self.application_name } @@ -803,73 +702,7 @@ impl CommandParams { } } -pub trait JobKindHelper: 'static + Send + Sync + Hash + Eq + fmt::Debug + Copy { - fn base_job_args<'a>( - self, - args: &'a ::Args, - dependencies: &'a <::Dependencies as JobDependencies>::KindsAndArgs, - ) -> &'a BaseJobArgs - where - Self: JobKind; - fn base_job<'a>( - self, - job: &'a ::Job, - dependencies: &'a <::Dependencies as JobDependencies>::JobsAndKinds, - ) -> &'a BaseJob - where - Self: JobKind; - #[track_caller] - fn base_job_args_dyn<'a>( - self, - args: &'a ::Args, - dependencies_args: &'a [DynJobArgs], - ) -> &'a BaseJobArgs - where - Self: JobKind; - #[track_caller] - fn base_job_dyn<'a>( - self, - job: &'a ::Job, - dependencies: &'a [DynJob], - ) -> &'a BaseJob - where - Self: JobKind; -} - -impl> JobKindHelper for K { - fn base_job_args<'a>( - self, - _args: &'a ::Args, - dependencies: &'a <::Dependencies as JobDependencies>::KindsAndArgs, - ) -> &'a BaseJobArgs { - K::Dependencies::base_job_args(dependencies) - } - fn base_job<'a>( - self, - _job: &'a ::Job, - dependencies: &'a <::Dependencies as JobDependencies>::JobsAndKinds, - ) -> &'a BaseJob { - K::Dependencies::base_job(dependencies) - } - #[track_caller] - fn base_job_args_dyn<'a>( - self, - _args: &'a ::Args, - dependencies_args: &'a [DynJobArgs], - ) -> &'a BaseJobArgs { - K::Dependencies::base_job_args_dyn(dependencies_args) - } - #[track_caller] - fn base_job_dyn<'a>( - self, - _job: &'a ::Job, - dependencies: &'a [DynJob], - ) -> &'a BaseJob { - K::Dependencies::base_job_dyn(dependencies) - } -} - -pub trait JobKind: JobKindHelper { +pub trait JobKind: 'static + Send + Sync + Hash + Eq + fmt::Debug + Sized + Copy { type Args: ToArgs; type Job: 'static + Send + Sync + Hash + Eq + fmt::Debug + Serialize + DeserializeOwned; type Dependencies: JobDependencies; @@ -877,7 +710,6 @@ pub trait JobKind: JobKindHelper { fn args_to_jobs( args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result>; fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]>; fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]>; @@ -888,7 +720,6 @@ pub trait JobKind: JobKindHelper { job: &Self::Job, inputs: &[JobItem], params: &JobParams, - global_params: &GlobalParams, acquired_job: &mut AcquiredJob, ) -> eyre::Result>; fn subcommand_hidden(self) -> bool { @@ -921,7 +752,7 @@ trait DynJobKindTrait: 'static + Send + Sync + fmt::Debug { ) -> serde_json::Result; } -impl DynJobKindTrait for K { +impl DynJobKindTrait for T { fn as_any(&self) -> &dyn Any { self } @@ -946,15 +777,15 @@ impl DynJobKindTrait for K { } fn args_group_id_dyn(&self) -> Option { - ::group_id() + ::group_id() } fn augment_args_dyn(&self, cmd: clap::Command) -> clap::Command { - ::augment_args(cmd) + ::augment_args(cmd) } fn augment_args_for_update_dyn(&self, cmd: clap::Command) -> clap::Command { - ::augment_args_for_update(cmd) + ::augment_args_for_update(cmd) } fn from_arg_matches_dyn( @@ -963,7 +794,7 @@ impl DynJobKindTrait for K { ) -> clap::error::Result { Ok(DynJobArgs::new( *self, - ::from_arg_matches_mut(matches)?, + ::from_arg_matches_mut(matches)?, )) } @@ -991,21 +822,21 @@ impl DynJobKindTrait for K { pub struct DynJobKind(Arc); impl DynJobKind { - pub fn from_arc(job_kind: Arc) -> Self { + pub fn from_arc(job_kind: Arc) -> Self { Self(job_kind) } - pub fn new(job_kind: K) -> Self { + pub fn new(job_kind: T) -> Self { Self(Arc::new(job_kind)) } pub fn type_id(&self) -> TypeId { DynJobKindTrait::as_any(&*self.0).type_id() } - pub fn downcast(&self) -> Option { + pub fn downcast(&self) -> Option { DynJobKindTrait::as_any(&*self.0).downcast_ref().copied() } - pub fn downcast_arc(self) -> Result, Self> { - if self.downcast::().is_some() { - Ok(Arc::downcast::(self.0.as_arc_any()) + pub fn downcast_arc(self) -> Result, Self> { + if self.downcast::().is_some() { + Ok(Arc::downcast::(self.0.as_arc_any()) .ok() .expect("already checked type")) } else { @@ -1233,10 +1064,7 @@ trait DynJobArgsTrait: 'static + Send + Sync + fmt::Debug { self: Arc, dependencies_args: Vec, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<(DynJob, Vec)>; - #[track_caller] - fn base_job_args_dyn<'a>(&'a self, dependencies_args: &'a [DynJobArgs]) -> &'a BaseJobArgs; } impl DynJobArgsTrait for DynJobArgsInner { @@ -1289,22 +1117,14 @@ impl DynJobArgsTrait for DynJobArgsInner { self: Arc, dependencies_args: Vec, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<(DynJob, Vec)> { let JobAndDependencies { job, dependencies } = JobArgsAndDependencies { args: Arc::unwrap_or_clone(self).0, dependencies: K::Dependencies::from_dyn_args(dependencies_args), } - .args_to_jobs(params, global_params)?; + .args_to_jobs(params)?; Ok((job.into(), K::Dependencies::into_dyn_jobs(dependencies))) } - - #[track_caller] - fn base_job_args_dyn<'a>(&'a self, dependencies_args: &'a [DynJobArgs]) -> &'a BaseJobArgs { - self.0 - .kind - .base_job_args_dyn(&self.0.args, dependencies_args) - } } #[derive(Clone)] @@ -1359,13 +1179,8 @@ impl DynJobArgs { self, dependencies_args: Vec, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<(DynJob, Vec)> { - DynJobArgsTrait::args_to_jobs(self.0, dependencies_args, params, global_params) - } - #[track_caller] - pub fn base_job_args_dyn<'a>(&'a self, dependencies_args: &'a [DynJobArgs]) -> &'a BaseJobArgs { - DynJobArgsTrait::base_job_args_dyn(&*self.0, dependencies_args) + DynJobArgsTrait::args_to_jobs(self.0, dependencies_args, params) } } @@ -1391,15 +1206,15 @@ impl fmt::Debug for DynJobArgs { } #[derive(PartialEq, Eq, Hash)] -struct DynJobInner { - kind: Arc, - job: K::Job, +struct DynJobInner { + kind: Arc, + job: T::Job, inputs: Interned<[JobItemName]>, outputs: Interned<[JobItemName]>, external_command_params: Option, } -impl> Clone for DynJobInner { +impl> Clone for DynJobInner { fn clone(&self) -> Self { Self { kind: self.kind.clone(), @@ -1411,7 +1226,7 @@ impl> Clone for DynJobInner { } } -impl fmt::Debug for DynJobInner { +impl fmt::Debug for DynJobInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { kind, @@ -1446,14 +1261,11 @@ trait DynJobTrait: 'static + Send + Sync + fmt::Debug { &self, inputs: &[JobItem], params: &JobParams, - global_params: &GlobalParams, acquired_job: &mut AcquiredJob, ) -> eyre::Result>; - #[track_caller] - fn base_job_dyn<'a>(&'a self, dependencies: &'a [DynJob]) -> &'a BaseJob; } -impl DynJobTrait for DynJobInner { +impl DynJobTrait for DynJobInner { fn as_any(&self) -> &dyn Any { self } @@ -1474,7 +1286,7 @@ impl DynJobTrait for DynJobInner { } fn kind_type_id(&self) -> TypeId { - TypeId::of::() + TypeId::of::() } fn kind(&self) -> DynJobKind { @@ -1505,16 +1317,9 @@ impl DynJobTrait for DynJobInner { &self, inputs: &[JobItem], params: &JobParams, - global_params: &GlobalParams, acquired_job: &mut AcquiredJob, ) -> eyre::Result> { - self.kind - .run(&self.job, inputs, params, global_params, acquired_job) - } - - #[track_caller] - fn base_job_dyn<'a>(&'a self, dependencies: &'a [DynJob]) -> &'a BaseJob { - self.kind.base_job_dyn(&self.job, dependencies) + self.kind.run(&self.job, inputs, params, acquired_job) } } @@ -1522,7 +1327,7 @@ impl DynJobTrait for DynJobInner { pub struct DynJob(Arc); impl DynJob { - pub fn from_arc(job_kind: Arc, job: K::Job) -> Self { + pub fn from_arc(job_kind: Arc, job: T::Job) -> Self { let inputs = job_kind.inputs(&job); let outputs = job_kind.outputs(&job); let external_command_params = job_kind.external_command_params(&job); @@ -1534,7 +1339,7 @@ impl DynJob { external_command_params, })) } - pub fn new(job_kind: K, job: K::Job) -> Self { + pub fn new(job_kind: T, job: T::Job) -> Self { Self::from_arc(Arc::new(job_kind), job) } pub fn kind_type_id(&self) -> TypeId { @@ -1579,12 +1384,10 @@ impl DynJob { pub fn internal_command_params_with_program_prefix( &self, internal_program_prefix: &[Interned], - platform: Option<&DynPlatform>, extra_args: &[Interned], ) -> CommandParams { let mut command_line = internal_program_prefix.to_vec(); - let command_line = match RunSingleJob::try_add_subcommand(platform, self, &mut command_line) - { + let command_line = match RunSingleJob::try_add_subcommand(self, &mut command_line) { Ok(()) => { command_line.extend_from_slice(extra_args); Intern::intern_owned(command_line) @@ -1597,14 +1400,9 @@ impl DynJob { } } #[track_caller] - pub fn internal_command_params( - &self, - platform: Option<&DynPlatform>, - extra_args: &[Interned], - ) -> CommandParams { + pub fn internal_command_params(&self, extra_args: &[Interned]) -> CommandParams { self.internal_command_params_with_program_prefix( &[program_name_for_internal_jobs()], - platform, extra_args, ) } @@ -1612,27 +1410,18 @@ impl DynJob { pub fn command_params_with_internal_program_prefix( &self, internal_program_prefix: &[Interned], - platform: Option<&DynPlatform>, extra_args: &[Interned], ) -> CommandParams { match self.external_command_params() { Some(v) => v, - None => self.internal_command_params_with_program_prefix( - internal_program_prefix, - platform, - extra_args, - ), + None => self + .internal_command_params_with_program_prefix(internal_program_prefix, extra_args), } } #[track_caller] - pub fn command_params( - &self, - platform: Option<&DynPlatform>, - extra_args: &[Interned], - ) -> CommandParams { + pub fn command_params(&self, extra_args: &[Interned]) -> CommandParams { self.command_params_with_internal_program_prefix( &[program_name_for_internal_jobs()], - platform, extra_args, ) } @@ -1640,14 +1429,9 @@ impl DynJob { &self, inputs: &[JobItem], params: &JobParams, - global_params: &GlobalParams, acquired_job: &mut AcquiredJob, ) -> eyre::Result> { - DynJobTrait::run(&*self.0, inputs, params, global_params, acquired_job) - } - #[track_caller] - pub fn base_job_dyn<'a>(&'a self, dependencies: &'a [DynJob]) -> &'a BaseJob { - DynJobTrait::base_job_dyn(&*self.0, dependencies) + DynJobTrait::run(&*self.0, inputs, params, acquired_job) } } @@ -1697,172 +1481,61 @@ impl<'de> Deserialize<'de> for DynJob { } pub trait RunBuild: Sized { - fn main_without_platform(application_name: impl AsRef, make_params: F) + fn main(make_params: F) where Self: clap::Parser + Clone, F: FnOnce(Self, Extra) -> eyre::Result, { - let application_name = application_name.as_ref(); - match Self::try_main_without_platform(application_name, make_params) { + match Self::try_main(make_params) { Ok(()) => {} Err(e) => { - let e = GlobalParams::new(Some(Self::command()), application_name) - .exit_if_clap_error(e); eprintln!("{e:#}"); std::process::exit(1); } } } - fn try_main_without_platform( - application_name: impl AsRef, - make_params: F, - ) -> eyre::Result<()> + fn try_main(make_params: F) -> eyre::Result<()> where Self: clap::Parser + Clone, F: FnOnce(Self, Extra) -> eyre::Result, { let args = Self::parse(); - let global_params = GlobalParams::new(Some(Self::command()), application_name); args.clone() - .run_without_platform(|extra| make_params(args, extra), &global_params) - .map_err(|e| global_params.exit_if_clap_error(e)) + .run(|extra| make_params(args, extra), Self::command()) } - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(Extra) -> eyre::Result; - fn get_platform(&self) -> Option<&DynPlatform>; - fn main(application_name: impl AsRef, make_params: F) - where - Self: clap::Parser + Clone, - F: FnOnce(Self, DynPlatform, Extra) -> eyre::Result, - { - let application_name = application_name.as_ref(); - match Self::try_main(application_name, make_params) { - Ok(()) => {} - Err(e) => { - let e = GlobalParams::new(Some(Self::command()), application_name) - .exit_if_clap_error(e); - eprintln!("{e:#}"); - std::process::exit(1); - } - } - } - fn try_main(application_name: impl AsRef, make_params: F) -> eyre::Result<()> - where - Self: clap::Parser + Clone, - F: FnOnce(Self, DynPlatform, Extra) -> eyre::Result, - { - let args = Self::parse(); - let global_params = GlobalParams::new(Some(Self::command()), application_name); - let Some(platform) = args.get_platform().cloned() else { - return args.handle_missing_platform(&global_params); - }; - args.clone() - .run( - |platform, extra| make_params(args, platform, extra), - platform, - &global_params, - ) - .map_err(|e| global_params.exit_if_clap_error(e)) - } - fn handle_missing_platform(self, global_params: &GlobalParams) -> eyre::Result<()> { - global_params - .clap_error( - clap::error::ErrorKind::MissingRequiredArgument, - "--platform is required", - ) - .exit(); - } - fn run( - self, - make_params: F, - platform: DynPlatform, - global_params: &GlobalParams, - ) -> eyre::Result<()> - where - F: FnOnce(DynPlatform, Extra) -> eyre::Result, - { - self.run_without_platform(|extra| make_params(platform, extra), global_params) - } } impl RunBuild for JobArgsAndDependencies { - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(NoArgs) -> eyre::Result, { let params = make_params(NoArgs)?; - self.args_to_jobs(¶ms, global_params)? - .run_without_platform(|_| Ok(params), global_params) - } - fn get_platform(&self) -> Option<&DynPlatform> { - self.base_job_args().platform.as_ref() - } - fn run( - self, - make_params: F, - platform: DynPlatform, - global_params: &GlobalParams, - ) -> eyre::Result<()> - where - F: FnOnce(DynPlatform, NoArgs) -> eyre::Result, - { - let params = make_params(platform.clone(), NoArgs)?; - self.args_to_jobs(¶ms, global_params)? - .run(|_, _| Ok(params), platform, global_params) + self.args_to_jobs(¶ms)?.run(|_| Ok(params), cmd) } } impl RunBuild for JobAndDependencies { - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(NoArgs) -> eyre::Result, { + let _ = cmd; let params = make_params(NoArgs)?; let Self { job, dependencies } = self; let mut jobs = vec![DynJob::from(job)]; K::Dependencies::into_dyn_jobs_extend(dependencies, &mut jobs); let mut job_graph = JobGraph::new(); job_graph.add_jobs(jobs); // add all at once to avoid recomputing graph properties multiple times - job_graph.run(¶ms, global_params) - } - fn get_platform(&self) -> Option<&DynPlatform> { - self.base_job().platform() - } - fn run( - self, - make_params: F, - platform: DynPlatform, - global_params: &GlobalParams, - ) -> eyre::Result<()> - where - F: FnOnce(DynPlatform, NoArgs) -> eyre::Result, - { - let params = make_params(platform, NoArgs)?; - let Self { job, dependencies } = self; - let mut jobs = vec![DynJob::from(job)]; - K::Dependencies::into_dyn_jobs_extend(dependencies, &mut jobs); - let mut job_graph = JobGraph::new(); - job_graph.add_jobs(jobs); // add all at once to avoid recomputing graph properties multiple times - job_graph.run(¶ms, global_params) + job_graph.run(¶ms) } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct RunSingleJob { - pub platform: Option, pub job: DynJob, pub extra: Extra, } @@ -1870,26 +1543,18 @@ pub struct RunSingleJob { impl RunSingleJob { pub const SUBCOMMAND_NAME: &'static str = "run-single-job"; fn try_add_subcommand( - platform: Option<&DynPlatform>, job: &DynJob, subcommand_line: &mut Vec>, ) -> serde_json::Result<()> { let mut json = job.serialize_to_json_ascii()?; json.insert_str(0, "--json="); - subcommand_line.push(Self::SUBCOMMAND_NAME.intern().into()); - if let Some(platform) = platform { - subcommand_line.push( - format!("--platform={}", platform.name()) - .intern_deref() - .into(), - ); - } - subcommand_line.push( + subcommand_line.extend([ + Interned::::from(Self::SUBCOMMAND_NAME.intern()), format!("--name={}", job.kind().name()) .intern_deref() .into(), - ); - subcommand_line.push(json.intern_deref().into()); + json.intern_deref().into(), + ]); Ok(()) } } @@ -1899,7 +1564,6 @@ impl TryFrom> for RunSingleJob { fn try_from(value: RunSingleJobClap) -> Result { let RunSingleJobClap::RunSingleJob { - platform, name: job_kind, json, extra, @@ -1913,11 +1577,7 @@ impl TryFrom> for RunSingleJob { format_args!("failed to parse job {name} from JSON: {e}"), ) }) - .map(|job| Self { - platform, - job, - extra, - }) + .map(|job| Self { job, extra }) } } @@ -1925,8 +1585,6 @@ impl TryFrom> for RunSingleJob { enum RunSingleJobClap { #[command(name = RunSingleJob::SUBCOMMAND_NAME, hide = true)] RunSingleJob { - #[arg(long)] - platform: Option, #[arg(long)] name: DynJobKind, #[arg(long)] @@ -1971,21 +1629,15 @@ impl clap::FromArgMatches for RunSingleJob { } impl RunBuild for RunSingleJob { - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(Extra) -> eyre::Result, { + let _ = cmd; let params = make_params(self.extra)?; let mut job_graph = JobGraph::new(); job_graph.add_jobs([self.job]); - job_graph.run(¶ms, global_params) - } - fn get_platform(&self) -> Option<&DynPlatform> { - self.platform.as_ref() + job_graph.run(¶ms) } } @@ -2022,18 +1674,11 @@ impl Completions { } impl RunBuild for Completions { - fn run_without_platform( - self, - _make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, _make_params: F, mut cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(NoArgs) -> eyre::Result, { let Self::Completions { shell } = self; - let Some(cmd) = global_params.top_level_cmd() else { - eyre::bail!("completions command requires GlobalParams::top_level_cmd() to be Some"); - }; let bin_name = cmd.get_bin_name().map(str::intern).unwrap_or_else(|| { program_name_for_internal_jobs() .to_interned_str() @@ -2041,18 +1686,12 @@ impl RunBuild for Completions { }); clap_complete::aot::generate( shell, - &mut cmd.clone(), + &mut cmd, &*bin_name, &mut std::io::BufWriter::new(std::io::stdout().lock()), ); Ok(()) } - fn handle_missing_platform(self, global_params: &GlobalParams) -> eyre::Result<()> { - self.run_without_platform(|_| unreachable!(), global_params) - } - fn get_platform(&self) -> Option<&DynPlatform> { - None - } } #[derive( @@ -2091,61 +1730,16 @@ pub enum BuildCli { } impl RunBuild for BuildCli { - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(Extra) -> eyre::Result, { match self { - BuildCli::Job(v) => v.run_without_platform(make_params, global_params), - BuildCli::RunSingleJob(v) => v.run_without_platform(make_params, global_params), - BuildCli::Completions(v) => { - v.run_without_platform(|NoArgs {}| unreachable!(), global_params) - } + BuildCli::Job(v) => v.run(make_params, cmd), + BuildCli::RunSingleJob(v) => v.run(make_params, cmd), + BuildCli::Completions(v) => v.run(|NoArgs {}| unreachable!(), cmd), #[cfg(unix)] - BuildCli::CreateUnixShellScript(v) => { - v.run_without_platform(make_params, global_params) - } - } - } - fn handle_missing_platform(self, global_params: &GlobalParams) -> eyre::Result<()> { - match self { - BuildCli::Job(v) => v.handle_missing_platform(global_params), - BuildCli::RunSingleJob(v) => v.handle_missing_platform(global_params), - BuildCli::Completions(v) => v.handle_missing_platform(global_params), - #[cfg(unix)] - BuildCli::CreateUnixShellScript(v) => v.handle_missing_platform(global_params), - } - } - fn get_platform(&self) -> Option<&DynPlatform> { - match self { - BuildCli::Job(v) => v.get_platform(), - BuildCli::RunSingleJob(v) => v.get_platform(), - BuildCli::Completions(v) => v.get_platform(), - #[cfg(unix)] - BuildCli::CreateUnixShellScript(v) => v.get_platform(), - } - } - fn run( - self, - make_params: F, - platform: DynPlatform, - global_params: &GlobalParams, - ) -> eyre::Result<()> - where - F: FnOnce(DynPlatform, Extra) -> eyre::Result, - { - match self { - BuildCli::Job(v) => v.run(make_params, platform, global_params), - BuildCli::RunSingleJob(v) => v.run(make_params, platform, global_params), - BuildCli::Completions(v) => { - v.run(|_, NoArgs {}| unreachable!(), platform, global_params) - } - #[cfg(unix)] - BuildCli::CreateUnixShellScript(v) => v.run(make_params, platform, global_params), + BuildCli::CreateUnixShellScript(v) => v.run(make_params, cmd), } } } @@ -2165,15 +1759,10 @@ enum CreateUnixShellScriptInner { pub struct CreateUnixShellScript(CreateUnixShellScriptInner); impl RunBuild for CreateUnixShellScript { - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(Extra) -> eyre::Result, { - let platform = self.get_platform().cloned(); let CreateUnixShellScriptInner::CreateUnixShellScript { _incomplete: (), inner: @@ -2185,28 +1774,22 @@ impl RunBuild for CreateUnixShellScript { } = self.0; let extra_args = extra.to_interned_args_vec(); let params = make_params(extra)?; - let bin_name = global_params - .top_level_cmd() - .and_then(clap::Command::get_bin_name) - .map(|v| OsStr::new(v).intern()); - let (job, dependencies) = args.args_to_jobs(dependencies_args, ¶ms, global_params)?; + let (job, dependencies) = args.args_to_jobs(dependencies_args, ¶ms)?; let mut job_graph = JobGraph::new(); job_graph.add_jobs([job].into_iter().chain(dependencies)); std::io::stdout().write_all( job_graph .to_unix_shell_script_with_internal_program_prefix( - &[bin_name.unwrap_or_else(|| program_name_for_internal_jobs())], - platform.as_ref(), + &[cmd + .get_bin_name() + .map(|v| OsStr::new(v).intern()) + .unwrap_or_else(|| program_name_for_internal_jobs())], &extra_args, ) .as_bytes(), )?; Ok(()) } - fn get_platform(&self) -> Option<&DynPlatform> { - let CreateUnixShellScriptInner::CreateUnixShellScript { inner, .. } = &self.0; - inner.get_platform() - } } impl clap::FromArgMatches for CreateUnixShellScript { @@ -2373,30 +1956,21 @@ impl clap::FromArgMatches for AnyJobSubcommand { } impl RunBuild for AnyJobSubcommand { - fn run_without_platform( - self, - make_params: F, - global_params: &GlobalParams, - ) -> eyre::Result<()> + fn run(self, make_params: F, cmd: clap::Command) -> eyre::Result<()> where F: FnOnce(Extra) -> eyre::Result, { + let _ = cmd; let Self { args, dependencies_args, extra, } = self; let params = make_params(extra)?; - let (job, dependencies) = args.args_to_jobs(dependencies_args, ¶ms, global_params)?; + let (job, dependencies) = args.args_to_jobs(dependencies_args, ¶ms)?; let mut job_graph = JobGraph::new(); job_graph.add_jobs([job].into_iter().chain(dependencies)); // add all at once to avoid recomputing graph properties multiple times - job_graph.run(¶ms, global_params) - } - fn get_platform(&self) -> Option<&DynPlatform> { - self.args - .base_job_args_dyn(&self.dependencies_args) - .platform - .as_ref() + job_graph.run(¶ms) } } @@ -2410,47 +1984,21 @@ pub fn program_name_for_internal_jobs() -> Interned { }) } -#[derive(clap::Args, Debug, Clone, Hash, PartialEq, Eq)] -#[group(id = "BaseJob")] -#[non_exhaustive] -pub struct BaseJobArgs { +#[derive(clap::Args, PartialEq, Eq, Hash, Debug, Clone)] +#[group(id = "CreateOutputDir")] +pub struct CreateOutputDirArgs { /// the directory to put the generated main output file and associated files in #[arg(short, long, value_hint = clap::ValueHint::DirPath)] pub output: Option, #[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")] pub keep_temp_dir: bool, - /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo - #[arg(long)] - pub file_stem: Option, - /// run commands even if their results are already cached - #[arg(long, env = Self::RUN_EVEN_IF_CACHED_ENV_NAME)] - pub run_even_if_cached: bool, - /// platform - #[arg(long)] - pub platform: Option, } -impl BaseJobArgs { - pub const RUN_EVEN_IF_CACHED_ENV_NAME: &'static str = "FAYALITE_RUN_EVEN_IF_CACHED"; - pub fn from_output_dir_and_env(output: PathBuf, platform: Option) -> Self { - Self { - output: Some(output), - keep_temp_dir: false, - file_stem: None, - run_even_if_cached: std::env::var_os(Self::RUN_EVEN_IF_CACHED_ENV_NAME).is_some(), - platform, - } - } -} - -impl ToArgs for BaseJobArgs { +impl ToArgs for CreateOutputDirArgs { fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { let Self { output, keep_temp_dir, - file_stem, - run_even_if_cached, - platform, } = self; if let Some(output) = output { args.write_long_option_eq("output", output); @@ -2458,130 +2006,36 @@ impl ToArgs for BaseJobArgs { if *keep_temp_dir { args.write_arg("--keep-temp-dir"); } - if let Some(file_stem) = file_stem { - args.write_long_option_eq("file-stem", file_stem); - } - if *run_even_if_cached { - args.write_arg("--run-even-if-cached"); - } - if let Some(platform) = platform { - args.write_long_option_eq("platform", platform.name()); - } } } -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BaseJob { +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CreateOutputDir { output_dir: Interned, #[serde(skip)] temp_dir: Option>, - file_stem: Interned, - run_even_if_cached: bool, - platform: Option, } -impl Hash for BaseJob { - fn hash(&self, state: &mut H) { - let Self { - output_dir, - temp_dir: _, - file_stem, - run_even_if_cached, - platform, - } = self; - output_dir.hash(state); - file_stem.hash(state); - run_even_if_cached.hash(state); - platform.hash(state); - } -} +impl Eq for CreateOutputDir {} -impl Eq for BaseJob {} - -impl PartialEq for BaseJob { +impl PartialEq for CreateOutputDir { fn eq(&self, other: &Self) -> bool { - let Self { - output_dir, - temp_dir: _, - file_stem, - run_even_if_cached, - ref platform, - } = *self; - output_dir == other.output_dir - && file_stem == other.file_stem - && run_even_if_cached == other.run_even_if_cached - && *platform == other.platform + self.compare_key() == other.compare_key() } } -impl BaseJob { - pub fn output_dir(&self) -> Interned { - self.output_dir - } - pub fn temp_dir(&self) -> Option<&Arc> { - self.temp_dir.as_ref() - } - pub fn file_stem(&self) -> Interned { - self.file_stem - } - pub fn file_with_ext(&self, ext: impl AsRef) -> Interned { - let mut retval = self.output_dir().join(self.file_stem()); - retval.set_extension(ext); - retval.intern_deref() - } - pub fn run_even_if_cached(&self) -> bool { - self.run_even_if_cached - } - pub fn platform(&self) -> Option<&DynPlatform> { - self.platform.as_ref() +impl Hash for CreateOutputDir { + fn hash(&self, state: &mut H) { + self.compare_key().hash(state); } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] -pub struct BaseJobKind; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] +pub struct CreateOutputDirJobKind; -impl JobKindHelper for BaseJobKind { - fn base_job<'a>( - self, - job: &'a ::Job, - _dependencies: &'a <::Dependencies as JobDependencies>::JobsAndKinds, - ) -> &'a BaseJob { - job - } - fn base_job_args<'a>( - self, - args: &'a ::Args, - _dependencies: &'a <::Dependencies as JobDependencies>::KindsAndArgs, - ) -> &'a BaseJobArgs { - args - } - #[track_caller] - fn base_job_args_dyn<'a>( - self, - args: &'a ::Args, - dependencies_args: &'a [DynJobArgs], - ) -> &'a BaseJobArgs { - let [] = dependencies_args else { - panic!("wrong number of dependencies"); - }; - args - } - #[track_caller] - fn base_job_dyn<'a>( - self, - job: &'a ::Job, - dependencies: &'a [DynJob], - ) -> &'a BaseJob { - let [] = dependencies else { - panic!("wrong number of dependencies"); - }; - job - } -} - -impl JobKind for BaseJobKind { - type Args = BaseJobArgs; - type Job = BaseJob; +impl JobKind for CreateOutputDirJobKind { + type Args = CreateOutputDirArgs; + type Job = CreateOutputDir; type Dependencies = (); fn dependencies(self) -> Self::Dependencies { @@ -2590,16 +2044,20 @@ impl JobKind for BaseJobKind { fn args_to_jobs( args: JobArgsAndDependencies, - params: &JobParams, - _global_params: &GlobalParams, + _params: &JobParams, ) -> eyre::Result> { - let BaseJobArgs { - output, - keep_temp_dir, - file_stem, - run_even_if_cached, - platform, - } = args.args.args; + let JobArgsAndDependencies { + args: + JobKindAndArgs { + kind, + args: + CreateOutputDirArgs { + output, + keep_temp_dir, + }, + }, + dependencies: (), + } = args; let (output_dir, temp_dir) = if let Some(output) = output { (Intern::intern_owned(output), None) } else { @@ -2617,18 +2075,12 @@ impl JobKind for BaseJobKind { }; (output_dir, temp_dir) }; - let file_stem = file_stem - .map(Intern::intern_deref) - .unwrap_or(params.main_module().name().into()); Ok(JobAndDependencies { job: JobAndKind { - kind: BaseJobKind, - job: BaseJob { + kind, + job: CreateOutputDir { output_dir, temp_dir, - file_stem, - run_even_if_cached, - platform, }, }, dependencies: (), @@ -2647,7 +2099,7 @@ impl JobKind for BaseJobKind { } fn name(self) -> Interned { - "base-job".intern() + "create-output-dir".intern() } fn external_command_params(self, job: &Self::Job) -> Option { @@ -2668,11 +2120,10 @@ impl JobKind for BaseJobKind { job: &Self::Job, inputs: &[JobItem], _params: &JobParams, - _global_params: &GlobalParams, _acquired_job: &mut AcquiredJob, ) -> eyre::Result> { let [] = inputs else { - panic!("invalid inputs for BaseJob"); + panic!("invalid inputs for CreateOutputDir"); }; std::fs::create_dir_all(&*job.output_dir)?; Ok(vec![JobItem::Path { @@ -2685,6 +2136,164 @@ impl JobKind for BaseJobKind { } } +impl CreateOutputDir { + pub fn output_dir(&self) -> Interned { + self.output_dir + } + fn compare_key(&self) -> (&Path, bool) { + let Self { + output_dir, + temp_dir, + } = self; + (output_dir, temp_dir.is_some()) + } +} + +#[derive(clap::Args, Debug, Clone, Hash, PartialEq, Eq)] +#[group(id = "BaseJob")] +#[non_exhaustive] +pub struct BaseJobArgs { + /// rather than having CreateOutputDir be a normal dependency, it's nested in BaseJob to avoid a cyclic dependency + #[command(flatten)] + pub create_output_dir_args: CreateOutputDirArgs, + /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo + #[arg(long)] + pub file_stem: Option, + /// run commands even if their results are already cached + #[arg(long, env = Self::RUN_EVEN_IF_CACHED_ENV_NAME)] + pub run_even_if_cached: bool, +} + +impl BaseJobArgs { + pub const RUN_EVEN_IF_CACHED_ENV_NAME: &'static str = "FAYALITE_RUN_EVEN_IF_CACHED"; + pub fn from_output_dir_and_env(output: PathBuf) -> Self { + Self { + create_output_dir_args: CreateOutputDirArgs { + output: Some(output), + keep_temp_dir: false, + }, + file_stem: None, + run_even_if_cached: std::env::var_os(Self::RUN_EVEN_IF_CACHED_ENV_NAME).is_some(), + } + } +} + +impl ToArgs for BaseJobArgs { + fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { + let Self { + create_output_dir_args, + file_stem, + run_even_if_cached, + } = self; + create_output_dir_args.to_args(args); + if let Some(file_stem) = file_stem { + args.write_long_option_eq("file-stem", file_stem); + } + if *run_even_if_cached { + args.write_arg("--run-even-if-cached"); + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct BaseJob { + /// rather than having CreateOutputDir be a normal dependency, it's nested in BaseJob to avoid a cyclic dependency + #[serde(flatten)] + create_output_dir: CreateOutputDir, + file_stem: Interned, + run_even_if_cached: bool, +} + +impl BaseJob { + pub fn output_dir(&self) -> Interned { + self.create_output_dir.output_dir() + } + pub fn file_stem(&self) -> Interned { + self.file_stem + } + pub fn file_with_ext(&self, ext: impl AsRef) -> Interned { + let mut retval = self.output_dir().join(self.file_stem()); + retval.set_extension(ext); + retval.intern_deref() + } + pub fn run_even_if_cached(&self) -> bool { + self.run_even_if_cached + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] +pub struct BaseJobKind; + +impl JobKind for BaseJobKind { + type Args = BaseJobArgs; + type Job = BaseJob; + type Dependencies = (); + + fn dependencies(self) -> Self::Dependencies { + () + } + + fn args_to_jobs( + args: JobArgsAndDependencies, + params: &JobParams, + ) -> eyre::Result> { + let BaseJobArgs { + create_output_dir_args, + file_stem, + run_even_if_cached, + } = args.args.args; + let create_output_dir_args = JobKindAndArgs { + kind: CreateOutputDirJobKind, + args: create_output_dir_args, + }; + let create_output_dir = create_output_dir_args.args_to_jobs((), params)?.job.job; + let file_stem = file_stem + .map(Intern::intern_deref) + .unwrap_or(params.main_module().name().into()); + Ok(JobAndDependencies { + job: JobAndKind { + kind: BaseJobKind, + job: BaseJob { + create_output_dir, + file_stem, + run_even_if_cached, + }, + }, + dependencies: (), + }) + } + + fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + CreateOutputDirJobKind.inputs(&job.create_output_dir) + } + + fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { + CreateOutputDirJobKind.outputs(&job.create_output_dir) + } + + fn name(self) -> Interned { + "base-job".intern() + } + + fn external_command_params(self, job: &Self::Job) -> Option { + CreateOutputDirJobKind.external_command_params(&job.create_output_dir) + } + + fn run( + self, + job: &Self::Job, + inputs: &[JobItem], + params: &JobParams, + acquired_job: &mut AcquiredJob, + ) -> eyre::Result> { + CreateOutputDirJobKind.run(&job.create_output_dir, inputs, params, acquired_job) + } + + fn subcommand_hidden(self) -> bool { + true + } +} + pub trait GetJob { fn get_job(this: &Self) -> &J; } @@ -2773,31 +2382,3 @@ impl GetJob for JobAndDependencies { &this.job.job } } - -impl>>> - GetJob> for JobArgsAndDependencies -{ - fn get_job(this: &Self) -> &J { - GetJob::get_job(&this.dependencies) - } -} - -impl GetJob for JobArgsAndDependencies { - fn get_job(this: &Self) -> &K::Args { - &this.args.args - } -} - -impl>> - GetJob> for JobKindAndDependencies -{ - fn get_job(this: &Self) -> &J { - GetJob::get_job(&this.dependencies) - } -} - -impl GetJob for JobKindAndDependencies { - fn get_job(this: &Self) -> &K { - &this.kind - } -} diff --git a/crates/fayalite/src/build/external.rs b/crates/fayalite/src/build/external.rs index e4251a4..a6936e5 100644 --- a/crates/fayalite/src/build/external.rs +++ b/crates/fayalite/src/build/external.rs @@ -3,9 +3,9 @@ use crate::{ build::{ - ArgsWriter, CommandParams, GlobalParams, JobAndDependencies, JobAndKind, - JobArgsAndDependencies, JobDependencies, JobDependenciesHasBase, JobItem, JobItemName, - JobKind, JobKindAndArgs, JobParams, ToArgs, WriteArgs, + ArgsWriter, BaseJob, CommandParams, GetJob, JobAndDependencies, JobAndKind, + JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs, + JobParams, ToArgs, WriteArgs, }, intern::{Intern, Interned}, util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, @@ -990,13 +990,12 @@ pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Size + Serialize + DeserializeOwned; type BaseJobPosition; - type Dependencies: JobDependenciesHasBase; + type Dependencies: JobDependencies>; type ExternalProgram: ExternalProgramTrait; fn dependencies() -> Self::Dependencies; fn args_to_jobs( args: JobArgsAndDependencies>, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<( Self::AdditionalJobData, ::JobsAndKinds, @@ -1029,7 +1028,6 @@ impl JobKind for ExternalCommandJobKind { fn args_to_jobs( args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { let JobKindAndArgs { kind, @@ -1044,8 +1042,8 @@ impl JobKind for ExternalCommandJobKind { additional_args: _, }, } = args.args; - let (additional_job_data, dependencies) = T::args_to_jobs(args, params, global_params)?; - let base_job = T::Dependencies::base_job(&dependencies); + let (additional_job_data, dependencies) = T::args_to_jobs(args, params)?; + let base_job = GetJob::::get_job(&dependencies); let job = ExternalCommandJob { additional_job_data, program_path, @@ -1080,8 +1078,7 @@ impl JobKind for ExternalCommandJobKind { self, job: &Self::Job, inputs: &[JobItem], - _params: &JobParams, - global_params: &GlobalParams, + params: &JobParams, acquired_job: &mut AcquiredJob, ) -> eyre::Result> { assert!( @@ -1096,7 +1093,7 @@ impl JobKind for ExternalCommandJobKind { } = job.command_params(); ExternalJobCaching::new( &job.output_dir, - &global_params.application_name(), + ¶ms.application_name(), &T::job_kind_name(), job.run_even_if_cached, )? diff --git a/crates/fayalite/src/build/firrtl.rs b/crates/fayalite/src/build/firrtl.rs index b5574a9..a04739d 100644 --- a/crates/fayalite/src/build/firrtl.rs +++ b/crates/fayalite/src/build/firrtl.rs @@ -3,7 +3,7 @@ use crate::{ build::{ - BaseJob, BaseJobKind, CommandParams, DynJobKind, GlobalParams, JobAndDependencies, + BaseJob, BaseJobKind, CommandParams, DynJobKind, JobAndDependencies, JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs, }, @@ -64,11 +64,9 @@ impl JobKind for FirrtlJobKind { fn args_to_jobs( args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { args.args_to_jobs_simple( params, - global_params, |_kind, FirrtlArgs { export_options }, dependencies| { Ok(Firrtl { base: dependencies.get_job::().clone(), @@ -105,7 +103,6 @@ impl JobKind for FirrtlJobKind { job: &Self::Job, inputs: &[JobItem], params: &JobParams, - _global_params: &GlobalParams, _acquired_job: &mut AcquiredJob, ) -> eyre::Result> { let [JobItem::Path { path: input_path }] = *inputs else { diff --git a/crates/fayalite/src/build/formal.rs b/crates/fayalite/src/build/formal.rs index 0708ff0..02515f2 100644 --- a/crates/fayalite/src/build/formal.rs +++ b/crates/fayalite/src/build/formal.rs @@ -3,8 +3,8 @@ use crate::{ build::{ - BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams, - JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, + BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies, + JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs, external::{ ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, @@ -201,7 +201,6 @@ impl JobKind for WriteSbyFileJobKind { fn args_to_jobs( mut args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { args.dependencies .dependencies @@ -210,7 +209,7 @@ impl JobKind for WriteSbyFileJobKind { .additional_args .verilog_dialect .get_or_insert(VerilogDialect::Yosys); - args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + args.args_to_jobs_simple(params, |_kind, args, dependencies| { let FormalArgs { sby_extra_args, formal_mode, @@ -256,7 +255,6 @@ impl JobKind for WriteSbyFileJobKind { job: &Self::Job, inputs: &[JobItem], params: &JobParams, - _global_params: &GlobalParams, _acquired_job: &mut AcquiredJob, ) -> eyre::Result> { assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); @@ -353,12 +351,11 @@ impl ExternalCommand for Formal { fn args_to_jobs( args: JobArgsAndDependencies>, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<( Self::AdditionalJobData, ::JobsAndKinds, )> { - args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + args.args_to_jobs_external_simple(params, |args, dependencies| { let FormalAdditionalArgs {} = args.additional_args; let write_sby_file = dependencies.get_job::().clone(); Ok(Formal { diff --git a/crates/fayalite/src/build/graph.rs b/crates/fayalite/src/build/graph.rs index d81b282..b727715 100644 --- a/crates/fayalite/src/build/graph.rs +++ b/crates/fayalite/src/build/graph.rs @@ -2,11 +2,8 @@ // See Notices.txt for copyright information use crate::{ - build::{ - DynJob, GlobalParams, JobItem, JobItemName, JobParams, program_name_for_internal_jobs, - }, + build::{DynJob, JobItem, JobItemName, JobParams, program_name_for_internal_jobs}, intern::Interned, - platform::DynPlatform, util::{HashMap, HashSet, job_server::AcquiredJob}, }; use eyre::{ContextCompat, eyre}; @@ -492,21 +489,15 @@ impl JobGraph { Err(e) => panic!("error: {e}"), } } - pub fn to_unix_makefile( - &self, - platform: Option<&DynPlatform>, - extra_args: &[Interned], - ) -> Result { + pub fn to_unix_makefile(&self, extra_args: &[Interned]) -> Result { self.to_unix_makefile_with_internal_program_prefix( &[program_name_for_internal_jobs()], - platform, extra_args, ) } pub fn to_unix_makefile_with_internal_program_prefix( &self, internal_program_prefix: &[Interned], - platform: Option<&DynPlatform>, extra_args: &[Interned], ) -> Result { let mut retval = String::new(); @@ -581,23 +572,19 @@ impl JobGraph { } } retval.push_str("\n\t"); - job.command_params_with_internal_program_prefix( - internal_program_prefix, - platform, - extra_args, - ) - .to_unix_shell_line(&mut retval, |arg, output| { - write_str!( - output, - "{}", - EscapeForUnixMakefile::new( - arg, - UnixMakefileEscapeKind::RecipeWithShellEscaping, - &mut needed_variables - )? - ); - Ok(()) - })?; + job.command_params_with_internal_program_prefix(internal_program_prefix, extra_args) + .to_unix_shell_line(&mut retval, |arg, output| { + write_str!( + output, + "{}", + EscapeForUnixMakefile::new( + arg, + UnixMakefileEscapeKind::RecipeWithShellEscaping, + &mut needed_variables + )? + ); + Ok(()) + })?; retval.push_str("\n\n"); } if !phony_targets.is_empty() { @@ -623,21 +610,15 @@ impl JobGraph { } Ok(retval) } - pub fn to_unix_shell_script( - &self, - platform: Option<&DynPlatform>, - extra_args: &[Interned], - ) -> String { + pub fn to_unix_shell_script(&self, extra_args: &[Interned]) -> String { self.to_unix_shell_script_with_internal_program_prefix( &[program_name_for_internal_jobs()], - platform, extra_args, ) } pub fn to_unix_shell_script_with_internal_program_prefix( &self, internal_program_prefix: &[Interned], - platform: Option<&DynPlatform>, extra_args: &[Interned], ) -> String { let mut retval = String::from( @@ -649,11 +630,7 @@ impl JobGraph { continue; }; let Ok(()) = job - .command_params_with_internal_program_prefix( - internal_program_prefix, - platform, - extra_args, - ) + .command_params_with_internal_program_prefix(internal_program_prefix, extra_args) .to_unix_shell_line(&mut retval, |arg, output| -> Result<(), Infallible> { write_str!(output, "{}", EscapeForUnixShell::new(&arg)); Ok(()) @@ -662,7 +639,7 @@ impl JobGraph { } retval } - pub fn run(&self, params: &JobParams, global_params: &GlobalParams) -> eyre::Result<()> { + pub fn run(&self, params: &JobParams) -> eyre::Result<()> { // use scope to auto-join threads on errors thread::scope(|scope| { struct WaitingJobState { @@ -748,18 +725,13 @@ impl JobGraph { job: DynJob, inputs: Vec, params: &'a JobParams, - global_params: &'a GlobalParams, acquired_job: AcquiredJob, finished_jobs_sender: mpsc::Sender<::NodeId>, } impl RunningJobInThread<'_> { fn run(mut self) -> eyre::Result> { - self.job.run( - &self.inputs, - self.params, - self.global_params, - &mut self.acquired_job, - ) + self.job + .run(&self.inputs, self.params, &mut self.acquired_job) } } impl Drop for RunningJobInThread<'_> { @@ -777,7 +749,6 @@ impl JobGraph { }) }))?, params, - global_params, acquired_job: AcquiredJob::acquire()?, finished_jobs_sender: finished_jobs_sender.clone(), }; diff --git a/crates/fayalite/src/build/registry.rs b/crates/fayalite/src/build/registry.rs index bbd9f2c..ccb401f 100644 --- a/crates/fayalite/src/build/registry.rs +++ b/crates/fayalite/src/build/registry.rs @@ -4,9 +4,10 @@ use crate::{ build::{DynJobKind, JobKind, built_in_job_kinds}, intern::Interned, - util::InternedStrCompareAsStr, }; use std::{ + borrow::Borrow, + cmp::Ordering, collections::BTreeMap, fmt, sync::{Arc, OnceLock, RwLock, RwLockWriteGuard}, @@ -22,6 +23,33 @@ impl DynJobKind { } } +#[derive(Copy, Clone, PartialEq, Eq)] +struct InternedStrCompareAsStr(Interned); + +impl fmt::Debug for InternedStrCompareAsStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Ord for InternedStrCompareAsStr { + fn cmp(&self, other: &Self) -> Ordering { + str::cmp(&self.0, &other.0) + } +} + +impl PartialOrd for InternedStrCompareAsStr { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Borrow for InternedStrCompareAsStr { + fn borrow(&self) -> &str { + &self.0 + } +} + #[derive(Clone, Debug)] struct JobKindRegistry { job_kinds: BTreeMap, diff --git a/crates/fayalite/src/build/verilog.rs b/crates/fayalite/src/build/verilog.rs index 7ce77ec..6ecae2c 100644 --- a/crates/fayalite/src/build/verilog.rs +++ b/crates/fayalite/src/build/verilog.rs @@ -4,8 +4,8 @@ use crate::{ build::{ BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob, - GlobalParams, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, - JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs, + JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, + JobKindAndDependencies, JobParams, ToArgs, WriteArgs, external::{ ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, }, @@ -152,12 +152,11 @@ impl ExternalCommand for UnadjustedVerilog { fn args_to_jobs( args: JobArgsAndDependencies>, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<( Self::AdditionalJobData, ::JobsAndKinds, )> { - args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + args.args_to_jobs_external_simple(params, |args, dependencies| { let UnadjustedVerilogArgs { firtool_extra_args, verilog_dialect, @@ -317,9 +316,8 @@ impl JobKind for VerilogJobKind { fn args_to_jobs( args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { - args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + args.args_to_jobs_simple(params, |_kind, args, dependencies| { let VerilogJobArgs {} = args; let base_job = dependencies.get_job::(); Ok(VerilogJob { @@ -366,7 +364,6 @@ impl JobKind for VerilogJobKind { job: &Self::Job, inputs: &[JobItem], _params: &JobParams, - _global_params: &GlobalParams, _acquired_job: &mut AcquiredJob, ) -> eyre::Result> { assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index cca0d82..fa3bb36 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -1883,11 +1883,7 @@ impl<'a> Exporter<'a> { } fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) { let data = match annotation { - Annotation::DontTouch(DontTouchAnnotation {}) => { - // TODO: error if the annotated thing was renamed because of a naming conflict, - // unless Target::base() is one of the ports of the top-level module since that's handled by ScalarizedModuleABI - AnnotationData::DontTouch - } + Annotation::DontTouch(DontTouchAnnotation {}) => AnnotationData::DontTouch, Annotation::SVAttribute(SVAttributeAnnotation { text }) => { AnnotationData::AttributeAnnotation { description: *text } } @@ -1911,8 +1907,7 @@ impl<'a> Exporter<'a> { additional_fields: (*additional_fields).into(), }, Annotation::Xilinx(XilinxAnnotation::XdcLocation(_)) - | Annotation::Xilinx(XilinxAnnotation::XdcIOStandard(_)) - | Annotation::Xilinx(XilinxAnnotation::XdcCreateClock(_)) => return, + | Annotation::Xilinx(XilinxAnnotation::XdcIOStandard(_)) => return, }; self.annotations.push(FirrtlAnnotation { data, diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index 970a439..39f4051 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -304,15 +304,6 @@ macro_rules! define_uint_in_range_type { $SerdeRange { start, end }.intern_sized(), )) } - pub fn bit_width(self) -> usize { - self.value.width() - } - pub fn start(self) -> Start::SizeType { - self.range.get().start - } - pub fn end(self) -> End::SizeType { - self.range.get().end - } } impl fmt::Debug for $UIntInRangeType { @@ -486,22 +477,18 @@ macro_rules! define_uint_in_range_type { } } - impl ExprCastTo> - for $UIntInRangeType - { - fn cast_to(src: Expr, to_type: UIntType) -> Expr> { + impl ExprCastTo for $UIntInRangeType { + fn cast_to(src: Expr, to_type: UInt) -> Expr { src.cast_to_bits().cast_to(to_type) } } - impl ExprCastTo<$UIntInRangeType> - for UIntType - { + impl ExprCastTo<$UIntInRangeType> for UInt { fn cast_to( src: Expr, to_type: $UIntInRangeType, ) -> Expr<$UIntInRangeType> { - src.cast_to(to_type.value).cast_bits_to(to_type) + src.cast_bits_to(to_type) } } diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 2c33a76..0aac2e0 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -33,9 +33,7 @@ use crate::{ sim::{ExternModuleSimulation, value::DynSimOnly}, source_location::SourceLocation, ty::{CanonicalType, Type}, - vendor::xilinx::{ - XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation, - }, + vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation}, wire::Wire, }; use num_bigint::{BigInt, BigUint}; diff --git a/crates/fayalite/src/platform.rs b/crates/fayalite/src/platform.rs index 194aa6e..903f9cc 100644 --- a/crates/fayalite/src/platform.rs +++ b/crates/fayalite/src/platform.rs @@ -8,30 +8,24 @@ use crate::{ module::{Module, ModuleBuilder, ModuleIO, connect_with_loc, instance_with_loc, wire_with_loc}, source_location::SourceLocation, ty::{CanonicalType, Type}, - util::{HashMap, HashSet, InternedStrCompareAsStr}, + util::HashMap, }; -use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; use std::{ any::{Any, TypeId}, - borrow::Cow, cmp::Ordering, collections::{BTreeMap, BTreeSet}, convert::Infallible, fmt, hash::{Hash, Hasher}, - iter::FusedIterator, marker::PhantomData, mem, - sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock, RwLockWriteGuard}, + sync::{Arc, Mutex, MutexGuard, OnceLock}, }; -pub mod peripherals; - trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug { fn as_any(&self) -> &dyn Any; fn eq_dyn(&self, other: &dyn DynPlatformTrait) -> bool; fn hash_dyn(&self, state: &mut dyn Hasher); - fn name_dyn(&self) -> Interned; fn new_peripherals_dyn<'builder>( &self, builder_factory: PeripheralsBuilderFactory<'builder>, @@ -39,7 +33,6 @@ trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug { fn source_location_dyn(&self) -> SourceLocation; #[track_caller] fn add_peripherals_in_wrapper_module_dyn(&self, m: &ModuleBuilder, peripherals: DynPeripherals); - fn aspects_dyn(&self) -> PlatformAspectSet; } impl DynPlatformTrait for T { @@ -58,10 +51,6 @@ impl DynPlatformTrait for T { self.hash(&mut state); } - fn name_dyn(&self) -> Interned { - self.name() - } - fn new_peripherals_dyn<'builder>( &self, builder_factory: PeripheralsBuilderFactory<'builder>, @@ -91,10 +80,6 @@ impl DynPlatformTrait for T { }; self.add_peripherals_in_wrapper_module(m, *peripherals) } - - fn aspects_dyn(&self) -> PlatformAspectSet { - self.aspects() - } } #[derive(Clone)] @@ -170,9 +155,6 @@ impl Peripherals for DynPeripherals { impl Platform for DynPlatform { type Peripherals = DynPeripherals; - fn name(&self) -> Interned { - DynPlatformTrait::name_dyn(&*self.0) - } fn new_peripherals<'a>( &self, builder_factory: PeripheralsBuilderFactory<'a>, @@ -186,9 +168,6 @@ impl Platform for DynPlatform { fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { DynPlatformTrait::add_peripherals_in_wrapper_module_dyn(&*self.0, m, peripherals); } - fn aspects(&self) -> PlatformAspectSet { - DynPlatformTrait::aspects_dyn(&*self.0) - } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -469,7 +448,9 @@ impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> { id, Box::new(move |state, peripheral_ref, wire| { on_use( - ::downcast_mut::(PeripheralsOnUseSharedState::as_any(state)) + state + .as_any() + .downcast_mut() .expect("known to be correct type"), PeripheralRef::from_canonical(peripheral_ref), Expr::from_canonical(wire), @@ -537,7 +518,6 @@ impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> { } } -#[must_use] pub struct Peripheral { ty: T, common: PeripheralCommon, @@ -548,27 +528,6 @@ impl Peripheral { let Self { ty, ref common } = *self; PeripheralRef { ty, common } } - pub fn ty(&self) -> T { - self.as_ref().ty() - } - pub fn id(&self) -> PeripheralId { - self.as_ref().id() - } - pub fn name(&self) -> Interned { - self.as_ref().name() - } - pub fn is_input(&self) -> bool { - self.as_ref().is_input() - } - pub fn is_output(&self) -> bool { - self.as_ref().is_output() - } - pub fn conflicts_with(&self) -> Interned> { - self.as_ref().conflicts_with() - } - pub fn availability(&self) -> PeripheralAvailability { - self.as_ref().availability() - } pub fn is_available(&self) -> bool { self.as_ref().is_available() } @@ -629,7 +588,7 @@ impl Peripheral { impl fmt::Debug for Peripheral { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_ref().debug_common_fields("Peripheral", f).finish() + self.as_ref().debug_fmt("Peripheral", f) } } @@ -640,10 +599,7 @@ pub struct UsedPeripheral { impl fmt::Debug for UsedPeripheral { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_ref() - .debug_common_fields("UsedPeripheral", f) - .field("instance_io_field", &self.instance_io_field()) - .finish() + self.as_ref().debug_fmt("UsedPeripheral", f) } } @@ -661,24 +617,6 @@ impl UsedPeripheral { pub fn instance_io_field(&self) -> Expr { self.instance_io_field } - pub fn ty(&self) -> T { - self.as_ref().ty() - } - pub fn id(&self) -> PeripheralId { - self.as_ref().id() - } - pub fn name(&self) -> Interned { - self.as_ref().name() - } - pub fn is_input(&self) -> bool { - self.as_ref().is_input() - } - pub fn is_output(&self) -> bool { - self.as_ref().is_output() - } - pub fn conflicts_with(&self) -> Interned> { - self.as_ref().conflicts_with() - } } #[derive(Copy, Clone)] @@ -689,7 +627,7 @@ pub struct PeripheralRef<'a, T: Type> { impl<'a, T: Type> fmt::Debug for PeripheralRef<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.debug_common_fields("PeripheralRef", f).finish() + self.debug_fmt("PeripheralRef", f) } } @@ -724,11 +662,7 @@ impl fmt::Display for PeripheralUnavailableError { impl std::error::Error for PeripheralUnavailableError {} impl<'a, T: Type> PeripheralRef<'a, T> { - fn debug_common_fields<'f1, 'f2>( - &self, - struct_name: &str, - f: &'f1 mut fmt::Formatter<'f2>, - ) -> fmt::DebugStruct<'f1, 'f2> { + fn debug_fmt(&self, struct_name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { ty, common: @@ -739,13 +673,12 @@ impl<'a, T: Type> PeripheralRef<'a, T> { peripherals_state: _, }, } = self; - let mut retval = f.debug_struct(struct_name); - retval + f.debug_struct(struct_name) .field("ty", ty) .field("id", id) .field("is_input", is_input) - .field("availability", &self.availability()); - retval + .field("availability", &self.availability()) + .finish() } pub fn ty(&self) -> T { self.ty @@ -917,7 +850,7 @@ impl<'a, T: Type> PeripheralRef<'a, T> { flipped: self.is_input(), ty: Expr::ty(canonical_wire), }); - on_use_function(&mut **shared_state, self.canonical(), canonical_wire); + on_use_function(shared_state, self.canonical(), canonical_wire); drop(on_use_state); Ok(wire) } @@ -1098,327 +1031,8 @@ impl<'a> fmt::Debug for PlatformIOBuilder<'a> { } } -trait PlatformAspectTrait: 'static + Send + Sync + fmt::Debug { - fn any_ref(&self) -> &dyn Any; - fn any_arc(self: Arc) -> Arc; - fn eq_dyn(&self, other: &dyn PlatformAspectTrait) -> bool; - fn hash_dyn(&self, state: &mut dyn Hasher); -} - -impl PlatformAspectTrait for T { - fn any_ref(&self) -> &dyn Any { - self - } - - fn any_arc(self: Arc) -> Arc { - self - } - - fn eq_dyn(&self, other: &dyn PlatformAspectTrait) -> bool { - other - .any_ref() - .downcast_ref::() - .is_some_and(|other| self == other) - } - - fn hash_dyn(&self, mut state: &mut dyn Hasher) { - self.hash(&mut state); - } -} - -#[derive(Clone)] -pub struct PlatformAspect { - type_id: TypeId, - type_name: &'static str, - value: Arc, -} - -impl Hash for PlatformAspect { - fn hash(&self, state: &mut H) { - PlatformAspectTrait::hash_dyn(&*self.value, state); - } -} - -impl Eq for PlatformAspect {} - -impl PartialEq for PlatformAspect { - fn eq(&self, other: &Self) -> bool { - self.type_id == other.type_id && PlatformAspectTrait::eq_dyn(&*self.value, &*other.value) - } -} - -impl fmt::Debug for PlatformAspect { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - type_id: _, - type_name, - value, - } = self; - write!(f, "PlatformAspect<{type_name}>")?; - f.debug_tuple("").field(value).finish() - } -} - -impl PlatformAspect { - pub fn new_arc(value: Arc) -> Self { - Self { - type_id: TypeId::of::(), - type_name: std::any::type_name::(), - value, - } - } - pub fn new(value: T) -> Self { - Self::new_arc(Arc::new(value)) - } - pub fn type_id(&self) -> TypeId { - self.type_id - } - pub fn downcast_arc(self) -> Result, Self> { - if self.type_id == TypeId::of::() { - let Ok(retval) = self.value.any_arc().downcast() else { - unreachable!(); - }; - Ok(retval) - } else { - Err(self) - } - } - pub fn downcast_unwrap_or_clone( - self, - ) -> Result { - Ok(Arc::unwrap_or_clone(self.downcast_arc()?)) - } - pub fn downcast_ref(&self) -> Option<&T> { - PlatformAspectTrait::any_ref(&*self.value).downcast_ref() - } -} - -#[derive(Clone, Default)] -pub struct PlatformAspectSet { - aspects_by_type_id: Arc>>, - aspects: Arc>, -} - -impl PlatformAspectSet { - pub fn new() -> Self { - Self::default() - } - pub fn insert_new( - &mut self, - value: T, - ) -> bool { - self.insert(PlatformAspect::new(value)) - } - pub fn insert_new_arc( - &mut self, - value: Arc, - ) -> bool { - self.insert(PlatformAspect::new_arc(value)) - } - fn insert_inner( - aspects_by_type_id: &mut HashMap>, - aspects: &mut Vec, - value: PlatformAspect, - ) -> bool { - if aspects_by_type_id - .entry(value.type_id) - .or_default() - .insert(value.clone()) - { - aspects.push(value); - true - } else { - false - } - } - pub fn insert(&mut self, value: PlatformAspect) -> bool { - Self::insert_inner( - Arc::make_mut(&mut self.aspects_by_type_id), - Arc::make_mut(&mut self.aspects), - value, - ) - } - pub fn contains(&self, value: &PlatformAspect) -> bool { - self.aspects_by_type_id - .get(&value.type_id) - .is_some_and(|aspects| aspects.contains(value)) - } - pub fn get_aspects_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( - &'a self, - ) -> impl Clone + Iterator + FusedIterator + ExactSizeIterator + 'a - { - self.aspects_by_type_id - .get(&TypeId::of::()) - .map(|aspects| aspects.iter()) - .unwrap_or_default() - } - pub fn get_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( - &'a self, - ) -> impl Clone + Iterator + FusedIterator + ExactSizeIterator + 'a { - self.get_aspects_by_type::() - .map(|aspect| aspect.downcast_ref().expect("already checked type")) - } - pub fn get_single_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( - &'a self, - ) -> Option<&'a T> { - let mut aspects = self.get_by_type::(); - if aspects.len() == 1 { - aspects.next() - } else { - None - } - } - pub fn get_arcs_by_type<'a, T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( - &'a self, - ) -> impl Clone + Iterator> + FusedIterator + ExactSizeIterator + 'a { - self.get_aspects_by_type::().map(|aspect| { - aspect - .clone() - .downcast_arc() - .ok() - .expect("already checked type") - }) - } -} - -impl<'a> Extend<&'a PlatformAspect> for PlatformAspectSet { - fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().cloned()); - } -} - -impl Extend for PlatformAspectSet { - fn extend>(&mut self, iter: T) { - let Self { - aspects_by_type_id, - aspects, - } = self; - let aspects_by_type_id = Arc::make_mut(aspects_by_type_id); - let aspects = Arc::make_mut(aspects); - iter.into_iter().for_each(|value| { - Self::insert_inner(aspects_by_type_id, aspects, value); - }); - } -} - -impl<'a> FromIterator<&'a PlatformAspect> for PlatformAspectSet { - fn from_iter>(iter: T) -> Self { - let mut retval = Self::default(); - retval.extend(iter); - retval - } -} - -impl FromIterator for PlatformAspectSet { - fn from_iter>(iter: T) -> Self { - let mut retval = Self::default(); - retval.extend(iter); - retval - } -} - -impl std::ops::Deref for PlatformAspectSet { - type Target = [PlatformAspect]; - - fn deref(&self) -> &Self::Target { - &self.aspects - } -} - -impl fmt::Debug for PlatformAspectSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_set().entries(self).finish() - } -} - -impl IntoIterator for PlatformAspectSet { - type Item = PlatformAspect; - type IntoIter = PlatformAspectsIntoIter; - - fn into_iter(self) -> Self::IntoIter { - PlatformAspectsIntoIter { - indexes: 0..self.aspects.len(), - aspects: self.aspects, - } - } -} - -impl<'a> IntoIterator for &'a PlatformAspectSet { - type Item = &'a PlatformAspect; - type IntoIter = std::slice::Iter<'a, PlatformAspect>; - - fn into_iter(self) -> Self::IntoIter { - self.aspects.iter() - } -} - -#[derive(Clone, Debug)] -pub struct PlatformAspectsIntoIter { - aspects: Arc>, - indexes: std::ops::Range, -} - -impl Iterator for PlatformAspectsIntoIter { - type Item = PlatformAspect; - - fn next(&mut self) -> Option { - self.indexes.next().map(|index| self.aspects[index].clone()) - } - - fn size_hint(&self) -> (usize, Option) { - self.indexes.size_hint() - } - - fn count(self) -> usize { - self.indexes.len() - } - - fn last(mut self) -> Option { - self.next_back() - } - - fn nth(&mut self, n: usize) -> Option { - self.indexes.nth(n).map(|index| self.aspects[index].clone()) - } - - fn fold(self, init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.indexes - .fold(init, |v, index| f(v, self.aspects[index].clone())) - } -} - -impl FusedIterator for PlatformAspectsIntoIter {} - -impl ExactSizeIterator for PlatformAspectsIntoIter {} - -impl DoubleEndedIterator for PlatformAspectsIntoIter { - fn next_back(&mut self) -> Option { - self.indexes - .next_back() - .map(|index| self.aspects[index].clone()) - } - - fn nth_back(&mut self, n: usize) -> Option { - self.indexes - .nth_back(n) - .map(|index| self.aspects[index].clone()) - } - - fn rfold(self, init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.indexes - .rfold(init, |v, index| f(v, self.aspects[index].clone())) - } -} - pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { type Peripherals: Peripherals; - fn name(&self) -> Interned; fn new_peripherals<'builder>( &self, builder_factory: PeripheralsBuilderFactory<'builder>, @@ -1499,7 +1113,7 @@ pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { crate::module::ModuleKind::Normal, |m| { let instance = - instance_with_loc("main", main_module.intern(), self.source_location()); + instance_with_loc("main", main_module.intern(), SourceLocation::caller()); let output_expr = Expr::field(instance, &output_module_io.bundle_field().name); let mut state = peripherals_state.state.lock().expect("shouldn't be poison"); let PeripheralsStateEnum::BuildingWrapperModule( @@ -1529,395 +1143,4 @@ pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { self.try_wrap_main_module(|p| Ok(make_main_module(p))) .unwrap_or_else(|e: Infallible| match e {}) } - fn aspects(&self) -> PlatformAspectSet; -} - -impl DynPlatform { - pub fn registry() -> PlatformRegistrySnapshot { - PlatformRegistrySnapshot(PlatformRegistry::get()) - } - #[track_caller] - pub fn register(self) { - PlatformRegistry::register(PlatformRegistry::lock(), self); - } -} - -#[derive(Clone, Debug)] -struct PlatformRegistry { - platforms: BTreeMap, -} - -enum PlatformRegisterError { - SameName { - name: InternedStrCompareAsStr, - old_platform: DynPlatform, - new_platform: DynPlatform, - }, -} - -impl fmt::Display for PlatformRegisterError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::SameName { - name, - old_platform, - new_platform, - } => write!( - f, - "two different `Platform` can't share the same name:\n\ - {name:?}\n\ - old platform:\n\ - {old_platform:?}\n\ - new platform:\n\ - {new_platform:?}", - ), - } - } -} - -trait PlatformRegistryRegisterLock { - type Locked; - fn lock(self) -> Self::Locked; - fn make_mut(locked: &mut Self::Locked) -> &mut PlatformRegistry; -} - -impl PlatformRegistryRegisterLock for &'static RwLock> { - type Locked = RwLockWriteGuard<'static, Arc>; - fn lock(self) -> Self::Locked { - self.write().expect("shouldn't be poisoned") - } - fn make_mut(locked: &mut Self::Locked) -> &mut PlatformRegistry { - Arc::make_mut(locked) - } -} - -impl PlatformRegistryRegisterLock for &'_ mut PlatformRegistry { - type Locked = Self; - - fn lock(self) -> Self::Locked { - self - } - - fn make_mut(locked: &mut Self::Locked) -> &mut PlatformRegistry { - locked - } -} - -impl PlatformRegistry { - fn lock() -> &'static RwLock> { - static REGISTRY: OnceLock>> = OnceLock::new(); - REGISTRY.get_or_init(Default::default) - } - fn try_register( - lock: L, - platform: DynPlatform, - ) -> Result<(), PlatformRegisterError> { - use std::collections::btree_map::Entry; - let name = InternedStrCompareAsStr(platform.name()); - // run user code only outside of lock - let mut locked = lock.lock(); - let this = L::make_mut(&mut locked); - let result = match this.platforms.entry(name) { - Entry::Occupied(entry) => Err(PlatformRegisterError::SameName { - name, - old_platform: entry.get().clone(), - new_platform: platform, - }), - Entry::Vacant(entry) => { - entry.insert(platform); - Ok(()) - } - }; - drop(locked); - // outside of lock now, so we can test if it's the same DynPlatform - match result { - Err(PlatformRegisterError::SameName { - name: _, - old_platform, - new_platform, - }) if old_platform == new_platform => Ok(()), - result => result, - } - } - #[track_caller] - fn register(lock: L, platform: DynPlatform) { - match Self::try_register(lock, platform) { - Err(e) => panic!("{e}"), - Ok(()) => {} - } - } - fn get() -> Arc { - Self::lock().read().expect("shouldn't be poisoned").clone() - } -} - -impl Default for PlatformRegistry { - fn default() -> Self { - let mut retval = Self { - platforms: BTreeMap::new(), - }; - for platform in built_in_platforms() { - Self::register(&mut retval, platform); - } - retval - } -} - -#[derive(Clone, Debug)] -pub struct PlatformRegistrySnapshot(Arc); - -impl PlatformRegistrySnapshot { - pub fn get() -> Self { - PlatformRegistrySnapshot(PlatformRegistry::get()) - } - pub fn get_by_name<'a>(&'a self, name: &str) -> Option<&'a DynPlatform> { - self.0.platforms.get(name) - } - pub fn iter_with_names(&self) -> PlatformRegistryIterWithNames<'_> { - PlatformRegistryIterWithNames(self.0.platforms.iter()) - } - pub fn iter(&self) -> PlatformRegistryIter<'_> { - PlatformRegistryIter(self.0.platforms.values()) - } -} - -impl<'a> IntoIterator for &'a PlatformRegistrySnapshot { - type Item = &'a DynPlatform; - type IntoIter = PlatformRegistryIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl<'a> IntoIterator for &'a mut PlatformRegistrySnapshot { - type Item = &'a DynPlatform; - type IntoIter = PlatformRegistryIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[derive(Clone, Debug)] -pub struct PlatformRegistryIter<'a>( - std::collections::btree_map::Values<'a, InternedStrCompareAsStr, DynPlatform>, -); - -impl<'a> Iterator for PlatformRegistryIter<'a> { - type Item = &'a DynPlatform; - - fn next(&mut self) -> Option { - self.0.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - fn count(self) -> usize - where - Self: Sized, - { - self.0.count() - } - - fn last(self) -> Option { - self.0.last() - } - - fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) - } - - fn fold(self, init: B, f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.0.fold(init, f) - } -} - -impl<'a> std::iter::FusedIterator for PlatformRegistryIter<'a> {} - -impl<'a> ExactSizeIterator for PlatformRegistryIter<'a> {} - -impl<'a> DoubleEndedIterator for PlatformRegistryIter<'a> { - fn next_back(&mut self) -> Option { - self.0.next_back() - } - - fn nth_back(&mut self, n: usize) -> Option { - self.0.nth_back(n) - } - - fn rfold(self, init: B, f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.0.rfold(init, f) - } -} - -#[derive(Clone, Debug)] -pub struct PlatformRegistryIterWithNames<'a>( - std::collections::btree_map::Iter<'a, InternedStrCompareAsStr, DynPlatform>, -); - -impl<'a> Iterator for PlatformRegistryIterWithNames<'a> { - type Item = (Interned, &'a DynPlatform); - - fn next(&mut self) -> Option { - self.0.next().map(|(name, platform)| (name.0, platform)) - } - - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - fn count(self) -> usize - where - Self: Sized, - { - self.0.count() - } - - fn last(self) -> Option { - self.0.last().map(|(name, platform)| (name.0, platform)) - } - - fn nth(&mut self, n: usize) -> Option { - self.0.nth(n).map(|(name, platform)| (name.0, platform)) - } - - fn fold(self, init: B, f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.0 - .map(|(name, platform)| (name.0, platform)) - .fold(init, f) - } -} - -impl<'a> std::iter::FusedIterator for PlatformRegistryIterWithNames<'a> {} - -impl<'a> ExactSizeIterator for PlatformRegistryIterWithNames<'a> {} - -impl<'a> DoubleEndedIterator for PlatformRegistryIterWithNames<'a> { - fn next_back(&mut self) -> Option { - self.0 - .next_back() - .map(|(name, platform)| (name.0, platform)) - } - - fn nth_back(&mut self, n: usize) -> Option { - self.0 - .nth_back(n) - .map(|(name, platform)| (name.0, platform)) - } - - fn rfold(self, init: B, f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.0 - .map(|(name, platform)| (name.0, platform)) - .rfold(init, f) - } -} - -#[track_caller] -pub fn register_platform(kind: K) { - DynPlatform::new(kind).register(); -} - -impl Serialize for DynPlatform { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.name().serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for DynPlatform { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let name = Cow::::deserialize(deserializer)?; - match Self::registry().get_by_name(&name) { - Some(retval) => Ok(retval.clone()), - None => Err(D::Error::custom(format_args!( - "unknown platform: name not found in registry: {name:?}" - ))), - } - } -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct DynPlatformValueParser; - -#[derive(Clone, PartialEq, Eq, Hash)] -struct DynPlatformValueEnum { - name: Interned, - platform: DynPlatform, -} - -impl clap::ValueEnum for DynPlatformValueEnum { - fn value_variants<'a>() -> &'a [Self] { - Interned::into_inner( - PlatformRegistrySnapshot::get() - .iter_with_names() - .map(|(name, platform)| Self { - name, - platform: platform.clone(), - }) - .collect(), - ) - } - - fn to_possible_value(&self) -> Option { - Some(clap::builder::PossibleValue::new(Interned::into_inner( - self.name, - ))) - } -} - -impl clap::builder::TypedValueParser for DynPlatformValueParser { - type Value = DynPlatform; - - fn parse_ref( - &self, - cmd: &clap::Command, - arg: Option<&clap::Arg>, - value: &std::ffi::OsStr, - ) -> clap::error::Result { - clap::builder::EnumValueParser::::new() - .parse_ref(cmd, arg, value) - .map(|v| v.platform) - } - - fn possible_values( - &self, - ) -> Option + '_>> { - static ENUM_VALUE_PARSER: OnceLock> = - OnceLock::new(); - ENUM_VALUE_PARSER - .get_or_init(clap::builder::EnumValueParser::::new) - .possible_values() - } -} - -impl clap::builder::ValueParserFactory for DynPlatform { - type Parser = DynPlatformValueParser; - - fn value_parser() -> Self::Parser { - DynPlatformValueParser::default() - } -} - -pub(crate) fn built_in_platforms() -> impl IntoIterator { - crate::vendor::built_in_platforms() } diff --git a/crates/fayalite/src/platform/peripherals.rs b/crates/fayalite/src/platform/peripherals.rs deleted file mode 100644 index 90c6640..0000000 --- a/crates/fayalite/src/platform/peripherals.rs +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use crate::{intern::Intern, prelude::*}; -use ordered_float::NotNan; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] -#[non_exhaustive] -pub struct ClockInputProperties { - pub frequency: NotNan, -} - -#[hdl(no_runtime_generics, no_static)] -pub struct ClockInput { - pub clk: Clock, - pub properties: PhantomConst, -} - -impl ClockInput { - #[track_caller] - pub fn new(frequency: f64) -> Self { - assert!( - frequency > 0.0 && frequency.is_finite(), - "invalid clock frequency: {frequency}" - ); - Self { - clk: Clock, - properties: PhantomConst::new( - ClockInputProperties { - frequency: NotNan::new(frequency).expect("just checked"), - } - .intern_sized(), - ), - } - } - pub fn frequency(self) -> f64 { - self.properties.get().frequency.into_inner() - } -} - -#[hdl] -pub struct Led { - pub on: Bool, -} - -#[hdl] -pub struct RgbLed { - pub r: Bool, - pub g: Bool, - pub b: Bool, -} - -#[hdl] -/// UART, used as an output from the FPGA -pub struct Uart { - /// transmit from the FPGA's perspective - pub tx: Bool, - /// receive from the FPGA's perspective - #[hdl(flip)] - pub rx: Bool, -} diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 216f94e..5ff3a64 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -28,7 +28,6 @@ pub use crate::{ memory, memory_array, memory_with_init, reg_builder, wire, }, phantom_const::PhantomConst, - platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals}, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, sim::{ diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index c7feb5e..30a8243 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -2,8 +2,8 @@ // See Notices.txt for copyright information use crate::{ build::{ - BaseJobArgs, BaseJobKind, GlobalParams, JobArgsAndDependencies, JobKindAndArgs, JobParams, - NoArgs, RunBuild, + BaseJobArgs, BaseJobKind, JobArgsAndDependencies, JobKindAndArgs, JobParams, NoArgs, + RunBuild, external::{ExternalCommandArgs, ExternalCommandJobKind}, firrtl::{FirrtlArgs, FirrtlJobKind}, formal::{Formal, FormalAdditionalArgs, FormalArgs, FormalMode, WriteSbyFileJobKind}, @@ -106,7 +106,7 @@ fn make_assert_formal_args( ) -> eyre::Result>> { let args = JobKindAndArgs { kind: BaseJobKind, - args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name), None), + args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name)), }; let dependencies = JobArgsAndDependencies { args, @@ -168,9 +168,9 @@ pub fn try_assert_formal>, T: BundleType>( solver, export_options, )? - .run_without_platform( - |NoArgs {}| Ok(JobParams::new(module)), - &GlobalParams::new(None, APP_NAME), + .run( + |NoArgs {}| Ok(JobParams::new(module, APP_NAME)), + clap::Command::new(APP_NAME), // not actually used, so we can use an arbitrary value ) } diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 9796488..cc8f8b0 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -33,6 +33,7 @@ pub use const_cmp::{ #[doc(inline)] pub use scoped_ref::ScopedRef; +pub(crate) use misc::chain; #[doc(inline)] pub use misc::{ BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, @@ -41,7 +42,6 @@ pub use misc::{ os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty, serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range, }; -pub(crate) use misc::{InternedStrCompareAsStr, chain}; pub mod job_server; pub mod prefix_sum; diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index 165ab3a..bd5c53f 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -585,30 +585,3 @@ pub fn os_str_strip_suffix<'a>(os_str: &'a OsStr, suffix: impl AsRef) -> Op unsafe { OsStr::from_encoded_bytes_unchecked(bytes) } }) } - -#[derive(Copy, Clone, PartialEq, Eq)] -pub(crate) struct InternedStrCompareAsStr(pub(crate) Interned); - -impl fmt::Debug for InternedStrCompareAsStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Ord for InternedStrCompareAsStr { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - str::cmp(&self.0, &other.0) - } -} - -impl PartialOrd for InternedStrCompareAsStr { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl std::borrow::Borrow for InternedStrCompareAsStr { - fn borrow(&self) -> &str { - &self.0 - } -} diff --git a/crates/fayalite/src/vendor.rs b/crates/fayalite/src/vendor.rs index cdf302d..56297cf 100644 --- a/crates/fayalite/src/vendor.rs +++ b/crates/fayalite/src/vendor.rs @@ -6,7 +6,3 @@ pub mod xilinx; pub(crate) fn built_in_job_kinds() -> impl IntoIterator { xilinx::built_in_job_kinds() } - -pub(crate) fn built_in_platforms() -> impl IntoIterator { - xilinx::built_in_platforms() -} diff --git a/crates/fayalite/src/vendor/xilinx.rs b/crates/fayalite/src/vendor/xilinx.rs index d80f388..05e45c7 100644 --- a/crates/fayalite/src/vendor/xilinx.rs +++ b/crates/fayalite/src/vendor/xilinx.rs @@ -1,19 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{ - annotations::make_annotation_enum, - build::{GlobalParams, ToArgs, WriteArgs}, - intern::Interned, - prelude::{DynPlatform, Platform}, -}; -use clap::ValueEnum; -use ordered_float::NotNan; -use serde::{Deserialize, Serialize}; -use std::fmt; +use crate::{annotations::make_annotation_enum, intern::Interned}; -pub mod arty_a7; -pub mod primitives; pub mod yosys_nextpnr_prjxray; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -26,182 +15,14 @@ pub struct XdcLocationAnnotation { pub location: Interned, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct XdcCreateClockAnnotation { - /// clock period in nanoseconds - pub period: NotNan, -} - make_annotation_enum! { #[non_exhaustive] pub enum XilinxAnnotation { XdcIOStandard(XdcIOStandardAnnotation), XdcLocation(XdcLocationAnnotation), - XdcCreateClock(XdcCreateClockAnnotation), - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] -pub struct XilinxArgs { - #[arg(long)] - pub device: Option, -} - -impl XilinxArgs { - pub fn require_device( - &self, - platform: Option<&DynPlatform>, - global_params: &GlobalParams, - ) -> clap::error::Result { - if let Some(device) = self.device { - return Ok(device); - } - if let Some(device) = - platform.and_then(|platform| platform.aspects().get_single_by_type::().copied()) - { - return Ok(device); - } - Err(global_params.clap_error( - clap::error::ErrorKind::MissingRequiredArgument, - "missing --device option", - )) - } -} - -impl ToArgs for XilinxArgs { - fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { - if let Some(device) = self.device { - args.write_long_option_eq("device", device.as_str()); - } - } -} - -macro_rules! make_device_enum { - ($vis:vis enum $Device:ident { - $( - #[ - name = $name:literal, - xray_part = $xray_part:literal, - xray_device = $xray_device:literal, - xray_family = $xray_family:literal, - ] - $variant:ident, - )* - }) => { - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, ValueEnum)] - $vis enum $Device { - $( - #[value(name = $name, alias = $xray_part)] - $variant, - )* - } - - impl $Device { - $vis fn as_str(self) -> &'static str { - match self { - $(Self::$variant => $name,)* - } - } - $vis fn xray_part(self) -> &'static str { - match self { - $(Self::$variant => $xray_part,)* - } - } - $vis fn xray_device(self) -> &'static str { - match self { - $(Self::$variant => $xray_device,)* - } - } - $vis fn xray_family(self) -> &'static str { - match self { - $(Self::$variant => $xray_family,)* - } - } - } - - struct DeviceVisitor; - - impl<'de> serde::de::Visitor<'de> for DeviceVisitor { - type Value = $Device; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("a Xilinx device string") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - match $Device::from_str(v, false) { - Ok(v) => Ok(v), - Err(_) => Err(E::invalid_value(serde::de::Unexpected::Str(v), &self)), - } - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: serde::de::Error, - { - match str::from_utf8(v).ok().and_then(|v| $Device::from_str(v, false).ok()) { - Some(v) => Ok(v), - None => Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)), - } - } - } - - impl<'de> Deserialize<'de> for $Device { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_string(DeviceVisitor) - } - } - - impl Serialize for $Device { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.as_str().serialize(serializer) - } - } - }; -} - -make_device_enum! { - pub enum Device { - #[ - name = "xc7a35ticsg324-1L", - xray_part = "xc7a35tcsg324-1", - xray_device = "xc7a35t", - xray_family = "artix7", - ] - Xc7a35ticsg324_1l, - #[ - name = "xc7a100ticsg324-1L", - xray_part = "xc7a100tcsg324-1", - xray_device = "xc7a100t", - xray_family = "artix7", - ] - Xc7a100ticsg324_1l, - } -} - -impl fmt::Display for Device { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) } } pub(crate) fn built_in_job_kinds() -> impl IntoIterator { - arty_a7::built_in_job_kinds() - .into_iter() - .chain(yosys_nextpnr_prjxray::built_in_job_kinds()) -} - -pub(crate) fn built_in_platforms() -> impl IntoIterator { - arty_a7::built_in_platforms() - .into_iter() - .chain(yosys_nextpnr_prjxray::built_in_platforms()) + yosys_nextpnr_prjxray::built_in_job_kinds() } diff --git a/crates/fayalite/src/vendor/xilinx/arty_a7.rs b/crates/fayalite/src/vendor/xilinx/arty_a7.rs deleted file mode 100644 index 552eb4a..0000000 --- a/crates/fayalite/src/vendor/xilinx/arty_a7.rs +++ /dev/null @@ -1,404 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use crate::{ - intern::{Intern, Interned}, - module::{instance_with_loc, reg_builder_with_loc, wire_with_loc}, - platform::{ - DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory, - PeripheralsBuilderFinished, Platform, PlatformAspectSet, - peripherals::{ClockInput, Led, RgbLed, Uart}, - }, - prelude::*, - vendor::xilinx::{ - Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, - primitives, - }, -}; -use ordered_float::NotNan; -use std::sync::OnceLock; - -macro_rules! arty_a7_platform { - ( - $vis:vis enum $ArtyA7Platform:ident { - $(#[name = $name:literal, device = $device:ident] - $Variant:ident,)* - } - ) => { - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - #[non_exhaustive] - $vis enum $ArtyA7Platform { - $($Variant,)* - } - - impl $ArtyA7Platform { - $vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*]; - $vis fn device(self) -> Device { - match self { - $(Self::$Variant => Device::$device,)* - } - } - $vis const fn as_str(self) -> &'static str { - match self { - $(Self::$Variant => $name,)* - } - } - fn get_aspects(self) -> &'static PlatformAspectSet { - match self { - $(Self::$Variant => { - static ASPECTS_SET: OnceLock = OnceLock::new(); - ASPECTS_SET.get_or_init(|| self.make_aspects()) - })* - } - } - } - }; -} - -arty_a7_platform! { - pub enum ArtyA7Platform { - #[name = "arty-a7-35t", device = Xc7a35ticsg324_1l] - ArtyA7_35T, - #[name = "arty-a7-100t", device = Xc7a100ticsg324_1l] - ArtyA7_100T, - } -} - -#[derive(Debug)] -pub struct ArtyA7Peripherals { - clk100_div_pow2: [Peripheral; 4], - rst: Peripheral, - rst_sync: Peripheral, - ld0: Peripheral, - ld1: Peripheral, - ld2: Peripheral, - ld3: Peripheral, - ld4: Peripheral, - ld5: Peripheral, - ld6: Peripheral, - ld7: Peripheral, - uart: Peripheral, - // TODO: add rest of peripherals when we need them -} - -impl Peripherals for ArtyA7Peripherals { - fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { - let Self { - clk100_div_pow2, - rst, - rst_sync, - ld0, - ld1, - ld2, - ld3, - ld4, - ld5, - ld6, - ld7, - uart, - } = self; - clk100_div_pow2.append_peripherals(peripherals); - rst.append_peripherals(peripherals); - rst_sync.append_peripherals(peripherals); - ld0.append_peripherals(peripherals); - ld1.append_peripherals(peripherals); - ld2.append_peripherals(peripherals); - ld3.append_peripherals(peripherals); - ld4.append_peripherals(peripherals); - ld5.append_peripherals(peripherals); - ld6.append_peripherals(peripherals); - ld7.append_peripherals(peripherals); - uart.append_peripherals(peripherals); - } -} - -impl ArtyA7Platform { - fn make_aspects(self) -> PlatformAspectSet { - let mut retval = PlatformAspectSet::new(); - retval.insert_new(self.device()); - retval - } -} - -#[hdl_module(extern)] -fn reset_sync() { - #[hdl] - let clk: Clock = m.input(); - #[hdl] - let inp: Bool = m.input(); - #[hdl] - let out: SyncReset = m.output(); - m.annotate_module(BlackBoxInlineAnnotation { - path: "fayalite_arty_a7_reset_sync.v".intern(), - text: r#"module __fayalite_arty_a7_reset_sync(input clk, input inp, output out); - wire reset_0_out; - (* ASYNC_REG = "TRUE" *) - FDPE #( - .INIT(1'b1) - ) reset_0 ( - .Q(reset_0_out), - .C(clk), - .CE(1'b1), - .PRE(inp), - .D(1'b0) - ); - (* ASYNC_REG = "TRUE" *) - FDPE #( - .INIT(1'b1) - ) reset_1 ( - .Q(out), - .C(clk), - .CE(1'b1), - .PRE(inp), - .D(reset_0_out) - ); -endmodule -"# - .intern(), - }); - m.verilog_name("__fayalite_arty_a7_reset_sync"); -} - -impl Platform for ArtyA7Platform { - type Peripherals = ArtyA7Peripherals; - - fn name(&self) -> Interned { - self.as_str().intern() - } - - fn new_peripherals<'builder>( - &self, - builder_factory: PeripheralsBuilderFactory<'builder>, - ) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) { - let mut builder = builder_factory.builder(); - - let clk100_div_pow2 = std::array::from_fn(|log2_divisor| { - let divisor = 1u64 << log2_divisor; - let name = if divisor != 1 { - format!("clk100_div_{divisor}") - } else { - "clk100".into() - }; - builder.input_peripheral(name, ClockInput::new(100e6 / divisor as f64)) - }); - builder.add_conflicts(Vec::from_iter(clk100_div_pow2.iter().map(|v| v.id()))); - ( - ArtyA7Peripherals { - clk100_div_pow2, - rst: builder.input_peripheral("rst", Reset), - rst_sync: builder.input_peripheral("rst_sync", SyncReset), - ld0: builder.output_peripheral("ld0", RgbLed), - ld1: builder.output_peripheral("ld1", RgbLed), - ld2: builder.output_peripheral("ld2", RgbLed), - ld3: builder.output_peripheral("ld3", RgbLed), - ld4: builder.output_peripheral("ld4", Led), - ld5: builder.output_peripheral("ld5", Led), - ld6: builder.output_peripheral("ld6", Led), - ld7: builder.output_peripheral("ld7", Led), - uart: builder.output_peripheral("uart", Uart), - }, - builder.finish(), - ) - } - - fn source_location(&self) -> SourceLocation { - SourceLocation::builtin() - } - - fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { - let ArtyA7Peripherals { - clk100_div_pow2, - rst, - rst_sync, - ld0, - ld1, - ld2, - ld3, - ld4, - ld5, - ld6, - ld7, - uart, - } = peripherals; - let make_buffered_input = |name: &str, location: &str, io_standard: &str, invert: bool| { - let pin = m.input_with_loc(name, SourceLocation::builtin(), Bool); - annotate( - pin, - XdcLocationAnnotation { - location: location.intern(), - }, - ); - annotate( - pin, - XdcIOStandardAnnotation { - value: io_standard.intern(), - }, - ); - let buf = instance_with_loc( - &format!("{name}_buf"), - primitives::IBUF(), - SourceLocation::builtin(), - ); - connect(buf.I, pin); - if invert { !buf.O } else { buf.O } - }; - let make_buffered_output = |name: &str, location: &str, io_standard: &str| { - let pin = m.output_with_loc(name, SourceLocation::builtin(), Bool); - annotate( - pin, - XdcLocationAnnotation { - location: location.intern(), - }, - ); - annotate( - pin, - XdcIOStandardAnnotation { - value: io_standard.intern(), - }, - ); - let buf = instance_with_loc( - &format!("{name}_buf"), - primitives::OBUFT(), - SourceLocation::builtin(), - ); - connect(pin, buf.O); - connect(buf.T, false); - buf.I - }; - let mut frequency = clk100_div_pow2[0].ty().frequency(); - let mut log2_divisor = 0; - let mut clk = None; - for (cur_log2_divisor, p) in clk100_div_pow2.into_iter().enumerate() { - let Some(p) = p.into_used() else { - continue; - }; - debug_assert!( - clk.is_none(), - "conflict-handling logic should ensure at most one clock is used", - ); - frequency = p.ty().frequency(); - clk = Some(p); - log2_divisor = cur_log2_divisor; - } - let clk100_buf = make_buffered_input("clk100", "E3", "LVCMOS33", false); - let startup = instance_with_loc( - "startup", - primitives::STARTUPE2_default_inputs(), - SourceLocation::builtin(), - ); - let clk_global_buf = instance_with_loc( - "clk_global_buf", - primitives::BUFGCE(), - SourceLocation::builtin(), - ); - connect(clk_global_buf.CE, startup.EOS); - let mut clk_global_buf_in = clk100_buf.to_clock(); - for prev_log2_divisor in 0..log2_divisor { - let prev_divisor = 1u64 << prev_log2_divisor; - let clk_in = wire_with_loc( - &format!("clk_div_{prev_divisor}"), - SourceLocation::builtin(), - Clock, - ); - connect(clk_in, clk_global_buf_in); - annotate( - clk_in, - XdcCreateClockAnnotation { - period: NotNan::new(1e9 / (100e6 / prev_divisor as f64)) - .expect("known to be valid"), - }, - ); - annotate(clk_in, DontTouchAnnotation); - let cd = wire_with_loc( - &format!("clk_div_{prev_divisor}_in"), - SourceLocation::builtin(), - ClockDomain[AsyncReset], - ); - connect(cd.clk, clk_in); - connect(cd.rst, (!startup.EOS).to_async_reset()); - let divider = reg_builder_with_loc("divider", SourceLocation::builtin()) - .clock_domain(cd) - .reset(false) - .build(); - connect(divider, !divider); - clk_global_buf_in = divider.to_clock(); - } - connect(clk_global_buf.I, clk_global_buf_in); - let clk_out = wire_with_loc("clk_out", SourceLocation::builtin(), Clock); - connect(clk_out, clk_global_buf.O); - annotate( - clk_out, - XdcCreateClockAnnotation { - period: NotNan::new(1e9 / frequency).expect("known to be valid"), - }, - ); - annotate(clk_out, DontTouchAnnotation); - if let Some(clk) = clk { - connect(clk.instance_io_field().clk, clk_out); - } - let rst_value = { - let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true); - let rst_sync = instance_with_loc("rst_sync", reset_sync(), SourceLocation::builtin()); - connect(rst_sync.clk, clk_out); - connect(rst_sync.inp, rst_buf | !startup.EOS); - rst_sync.out - }; - if let Some(rst) = rst.into_used() { - connect(rst.instance_io_field(), rst_value.to_reset()); - } - if let Some(rst_sync) = rst_sync.into_used() { - connect(rst_sync.instance_io_field(), rst_value); - } - let rgb_leds = [ - (ld0, ("G6", "F6", "E1")), - (ld1, ("G3", "J4", "G4")), - (ld2, ("J3", "J2", "H4")), - (ld3, ("K1", "H6", "K2")), - ]; - for (rgb_led, (r_loc, g_loc, b_loc)) in rgb_leds { - let r = make_buffered_output(&format!("{}_r", rgb_led.name()), r_loc, "LVCMOS33"); - let g = make_buffered_output(&format!("{}_g", rgb_led.name()), g_loc, "LVCMOS33"); - let b = make_buffered_output(&format!("{}_b", rgb_led.name()), b_loc, "LVCMOS33"); - if let Some(rgb_led) = rgb_led.into_used() { - connect(r, rgb_led.instance_io_field().r); - connect(g, rgb_led.instance_io_field().g); - connect(b, rgb_led.instance_io_field().b); - } else { - connect(r, false); - connect(g, false); - connect(b, false); - } - } - let leds = [(ld4, "H5"), (ld5, "J5"), (ld6, "T9"), (ld7, "T10")]; - for (led, loc) in leds { - let o = make_buffered_output(&led.name(), loc, "LVCMOS33"); - if let Some(led) = led.into_used() { - connect(o, led.instance_io_field().on); - } else { - connect(o, false); - } - } - let uart_tx = make_buffered_output("uart_tx", "D10", "LVCMOS33"); - let uart_rx = make_buffered_input("uart_rx", "A9", "LVCMOS33", false); - if let Some(uart) = uart.into_used() { - connect(uart_tx, uart.instance_io_field().tx); - connect(uart.instance_io_field().rx, uart_rx); - } else { - connect(uart_tx, true); // idle - } - } - - fn aspects(&self) -> PlatformAspectSet { - self.get_aspects().clone() - } -} - -pub(crate) fn built_in_job_kinds() -> impl IntoIterator { - [] -} - -pub(crate) fn built_in_platforms() -> impl IntoIterator { - ArtyA7Platform::VARIANTS - .iter() - .map(|&v| DynPlatform::new(v)) -} diff --git a/crates/fayalite/src/vendor/xilinx/primitives.rs b/crates/fayalite/src/vendor/xilinx/primitives.rs deleted file mode 100644 index 9e22d26..0000000 --- a/crates/fayalite/src/vendor/xilinx/primitives.rs +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -#![allow(non_snake_case)] - -use crate::prelude::*; - -#[hdl_module(extern)] -pub fn IBUF() { - m.verilog_name("IBUF"); - #[hdl] - let O: Bool = m.output(); - #[hdl] - let I: Bool = m.input(); -} - -#[hdl_module(extern)] -pub fn OBUFT() { - m.verilog_name("OBUFT"); - #[hdl] - let O: Bool = m.output(); - #[hdl] - let I: Bool = m.input(); - #[hdl] - let T: Bool = m.input(); -} - -#[hdl_module(extern)] -pub fn BUFGCE() { - m.verilog_name("BUFGCE"); - #[hdl] - let O: Clock = m.output(); - #[hdl] - let CE: Bool = m.input(); - #[hdl] - let I: Clock = m.input(); -} - -#[hdl_module(extern)] -pub fn STARTUPE2_default_inputs() { - m.verilog_name("STARTUPE2"); - #[hdl] - let CFGCLK: Clock = m.output(); - #[hdl] - let CFGMCLK: Clock = m.output(); - #[hdl] - let EOS: Bool = m.output(); - #[hdl] - let PREQ: Bool = m.output(); -} diff --git a/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs b/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs index 3e1ac0c..c3b1245 100644 --- a/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs +++ b/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs @@ -2,36 +2,28 @@ // See Notices.txt for copyright information use crate::{ - annotations::{Annotation, TargetedAnnotation}, + annotations::Annotation, build::{ - BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams, - JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, + BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies, + JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, ToArgs, WriteArgs, external::{ ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, }, verilog::{UnadjustedVerilog, VerilogDialect, VerilogJob, VerilogJobKind}, }, - bundle::{Bundle, BundleType}, - expr::target::{Target, TargetBase}, + bundle::Bundle, firrtl::{ScalarizedModuleABI, ScalarizedModuleABIAnnotations, ScalarizedModuleABIPort}, intern::{Intern, InternSlice, Interned}, - module::{ - NameId, ScopedNameId, TargetName, - transform::visit::{Visit, Visitor}, - }, - prelude::*, - source_location::SourceLocation, - util::{HashSet, job_server::AcquiredJob}, - vendor::xilinx::{ - Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, - XilinxAnnotation, XilinxArgs, - }, + module::{Module, NameId}, + prelude::JobParams, + util::job_server::AcquiredJob, + vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation}, }; +use clap::ValueEnum; use eyre::Context; use serde::{Deserialize, Serialize}; use std::{ - convert::Infallible, ffi::{OsStr, OsString}, fmt::{self, Write}, ops::ControlFlow, @@ -113,7 +105,6 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { fn args_to_jobs( mut args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { args.dependencies .dependencies @@ -122,7 +113,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { .additional_args .verilog_dialect .get_or_insert(VerilogDialect::Yosys); - args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + args.args_to_jobs_simple(params, |_kind, args, dependencies| { let YosysNextpnrXrayWriteYsFileArgs {} = args; let base_job = dependencies.get_job::(); let verilog_job = dependencies.get_job::(); @@ -162,7 +153,6 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { job: &Self::Job, inputs: &[JobItem], params: &JobParams, - _global_params: &GlobalParams, _acquired_job: &mut AcquiredJob, ) -> eyre::Result> { assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); @@ -270,12 +260,11 @@ impl ExternalCommand for YosysNextpnrXraySynth { fn args_to_jobs( args: JobArgsAndDependencies>, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<( Self::AdditionalJobData, ::JobsAndKinds, )> { - args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + args.args_to_jobs_external_simple(params, |args, dependencies| { let YosysNextpnrXraySynthArgs {} = args.additional_args; Ok(Self { write_ys_file: dependencies.job.job.clone(), @@ -361,9 +350,6 @@ impl From for WriteXdcContentsError { fn tcl_escape(s: impl AsRef) -> String { let s = s.as_ref(); - if !s.contains(|ch: char| !ch.is_alphanumeric() && ch != '_') { - return s.into(); - } let mut retval = String::with_capacity(s.len().saturating_add(2)); retval.push('"'); for ch in s.chars() { @@ -376,228 +362,6 @@ fn tcl_escape(s: impl AsRef) -> String { retval } -#[derive(Copy, Clone, Debug)] -enum AnnotationTarget { - None, - Module(Module), - Mem(Mem), - Target(Interned), -} - -impl AnnotationTarget { - fn source_location(self) -> SourceLocation { - match self { - AnnotationTarget::None => unreachable!(), - AnnotationTarget::Module(module) => module.source_location(), - AnnotationTarget::Mem(mem) => mem.source_location(), - AnnotationTarget::Target(target) => target.base().source_location(), - } - } -} - -struct XdcFileWriter { - output: W, - module_depth: usize, - annotation_target: AnnotationTarget, - dont_touch_targets: HashSet>, - required_dont_touch_targets: HashSet>, -} - -impl XdcFileWriter { - fn run(output: W, top_module: Module) -> Result<(), WriteXdcContentsError> { - let mut this = Self { - output, - module_depth: 0, - annotation_target: AnnotationTarget::None, - dont_touch_targets: HashSet::default(), - required_dont_touch_targets: HashSet::default(), - }; - top_module.visit(&mut this)?; - let Self { - output: _, - module_depth: _, - annotation_target: _, - dont_touch_targets, - required_dont_touch_targets, - } = this; - for &target in required_dont_touch_targets.difference(&dont_touch_targets) { - return Err(eyre::eyre!( - "a DontTouchAnnotation is required since the target is also annotated with a XilinxAnnotation:\ntarget: {target:?}\nat: {}", - target.base().source_location(), - ).into()); - } - Ok(()) - } - fn default_visit_with>( - &mut self, - module_depth: usize, - annotation_target: AnnotationTarget, - v: &T, - ) -> Result<(), WriteXdcContentsError> { - let Self { - output: _, - module_depth: old_module_depth, - annotation_target: old_annotation_target, - dont_touch_targets: _, - required_dont_touch_targets: _, - } = *self; - self.module_depth = module_depth; - self.annotation_target = annotation_target; - let retval = v.default_visit(self); - self.module_depth = old_module_depth; - self.annotation_target = old_annotation_target; - retval - } -} - -impl Visitor for XdcFileWriter { - type Error = WriteXdcContentsError; - - fn visit_targeted_annotation(&mut self, v: &TargetedAnnotation) -> Result<(), Self::Error> { - self.default_visit_with(self.module_depth, AnnotationTarget::Target(v.target()), v) - } - - fn visit_module(&mut self, v: &Module) -> Result<(), Self::Error> { - self.default_visit_with( - self.module_depth + 1, - AnnotationTarget::Module(v.canonical()), - v, - ) - } - - fn visit_mem( - &mut self, - v: &Mem, - ) -> Result<(), Self::Error> - where - Element: Visit, - { - self.default_visit_with( - self.module_depth + 1, - AnnotationTarget::Mem(v.canonical()), - v, - ) - } - - fn visit_dont_touch_annotation(&mut self, _v: &DontTouchAnnotation) -> Result<(), Self::Error> { - if let AnnotationTarget::Target(target) = self.annotation_target { - self.dont_touch_targets.insert(target); - } - Ok(()) - } - - fn visit_xilinx_annotation(&mut self, v: &XilinxAnnotation) -> Result<(), Self::Error> { - fn todo( - msg: &str, - annotation: &XilinxAnnotation, - source_location: SourceLocation, - ) -> Result { - Err(WriteXdcContentsError(eyre::eyre!( - "{msg}\nannotation: {annotation:?}\nat: {source_location}" - ))) - } - if self.module_depth != 1 { - match todo( - "annotations are not yet supported outside of the top module since the logic to figure out the correct name isn't implemented", - v, - self.annotation_target.source_location(), - )? {} - } - match self.annotation_target { - AnnotationTarget::None => unreachable!(), - AnnotationTarget::Module(module) => match v { - XilinxAnnotation::XdcIOStandard(_) - | XilinxAnnotation::XdcLocation(_) - | XilinxAnnotation::XdcCreateClock(_) => { - return Err(WriteXdcContentsError(eyre::eyre!( - "annotation not allowed on a module: {v:?}\nat: {}", - module.source_location(), - ))); - } - }, - AnnotationTarget::Mem(mem) => match todo( - "xilinx annotations are not yet supported on memories since the logic to figure out the correct name isn't implemented", - v, - mem.source_location(), - )? {}, - AnnotationTarget::Target(target) => { - let base = target.base(); - match *base { - TargetBase::ModuleIO(_) => { - // already handled by write_xdc_contents handling the main module's ScalarizedModuleABI - Ok(()) - } - TargetBase::MemPort(mem_port) => { - match todo( - "xilinx annotations are not yet supported on memory ports since the logic to figure out the correct name isn't implemented", - v, - mem_port.source_location(), - )? {} - } - TargetBase::Reg(_) - | TargetBase::RegSync(_) - | TargetBase::RegAsync(_) - | TargetBase::Wire(_) => { - match *target { - Target::Base(_) => {} - Target::Child(_) => match todo( - "xilinx annotations are not yet supported on parts of registers/wires since the logic to figure out the correct name isn't implemented", - v, - base.source_location(), - )? {}, - } - match base.canonical_ty() { - CanonicalType::UInt(_) - | CanonicalType::SInt(_) - | CanonicalType::Bool(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => {} - CanonicalType::Enum(_) - | CanonicalType::Array(_) - | CanonicalType::Bundle(_) - | CanonicalType::PhantomConst(_) - | CanonicalType::DynSimOnly(_) => match todo( - "xilinx annotations are not yet supported on types other than integers, Bool, resets, or Clock since the logic to figure out the correct name isn't implemented", - v, - base.source_location(), - )? {}, - } - self.required_dont_touch_targets.insert(target); - match v { - XilinxAnnotation::XdcIOStandard(_) - | XilinxAnnotation::XdcLocation(_) => { - return Err(WriteXdcContentsError(eyre::eyre!( - "annotation must be on a ModuleIO: {v:?}\nat: {}", - base.source_location(), - ))); - } - XilinxAnnotation::XdcCreateClock(XdcCreateClockAnnotation { - period, - }) => { - let TargetName(ScopedNameId(_, NameId(name, _)), _) = - base.target_name(); - writeln!( - self.output, - "create_clock -period {period} [get_nets {}]", - tcl_escape(name), - )?; - Ok(()) - } - } - } - TargetBase::Instance(instance) => match todo( - "xilinx annotations are not yet supported on instances' IO since the logic to figure out the correct name isn't implemented", - v, - instance.source_location(), - )? {}, - } - } - } - } -} - impl YosysNextpnrXrayWriteXdcFile { fn write_xdc_contents_for_port_and_annotations( &self, @@ -619,7 +383,7 @@ impl YosysNextpnrXrayWriteXdcFile { output, "set_property LOC {} [get_ports {}]", tcl_escape(location), - tcl_escape(port.scalarized_name()), + tcl_escape(port.scalarized_name()) )?, Annotation::Xilinx(XilinxAnnotation::XdcIOStandard(XdcIOStandardAnnotation { value, @@ -627,14 +391,7 @@ impl YosysNextpnrXrayWriteXdcFile { output, "set_property IOSTANDARD {} [get_ports {}]", tcl_escape(value), - tcl_escape(port.scalarized_name()), - )?, - Annotation::Xilinx(XilinxAnnotation::XdcCreateClock( - XdcCreateClockAnnotation { period }, - )) => writeln!( - output, - "create_clock -period {period} [get_ports {}]", - tcl_escape(port.scalarized_name()), + tcl_escape(port.scalarized_name()) )?, } } @@ -654,10 +411,9 @@ impl YosysNextpnrXrayWriteXdcFile { Err(e) => ControlFlow::Break(e), } }) { - ControlFlow::Continue(()) => {} - ControlFlow::Break(e) => return Err(e.0), + ControlFlow::Continue(()) => Ok(()), + ControlFlow::Break(e) => Err(e.0), } - XdcFileWriter::run(output, *top_module).map_err(|e| e.0) } } @@ -673,7 +429,6 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { fn args_to_jobs( args: JobArgsAndDependencies, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result> { let firrtl_export_options = args .dependencies @@ -684,7 +439,7 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { .args .args .export_options; - args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { + args.args_to_jobs_simple(params, |_kind, args, dependencies| { let YosysNextpnrXrayWriteXdcFileArgs {} = args; let base_job = dependencies.get_job::(); Ok(YosysNextpnrXrayWriteXdcFile { @@ -719,13 +474,23 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { job: &Self::Job, inputs: &[JobItem], params: &JobParams, - _global_params: &GlobalParams, _acquired_job: &mut AcquiredJob, ) -> eyre::Result> { assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); let mut xdc = String::new(); job.write_xdc_contents(&mut xdc, params.main_module())?; - std::fs::write(job.xdc_file, xdc)?; + // TODO: create actual .xdc from input module + std::fs::write( + job.xdc_file, + r"# autogenerated +set_property LOC G6 [get_ports led] +set_property IOSTANDARD LVCMOS33 [get_ports led] +set_property LOC E3 [get_ports clk] +set_property IOSTANDARD LVCMOS33 [get_ports clk] +set_property LOC C2 [get_ports rst] +set_property IOSTANDARD LVCMOS33 [get_ports rst] +", + )?; Ok(vec![JobItem::Path { path: job.xdc_file }]) } @@ -743,12 +508,130 @@ impl ExternalProgramTrait for NextpnrXilinx { } } +macro_rules! make_device_enum { + ($vis:vis enum $Device:ident { + $( + #[ + name = $name:literal, + xray_part = $xray_part:literal, + xray_device = $xray_device:literal, + xray_family = $xray_family:literal, + ] + $variant:ident, + )* + }) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, ValueEnum)] + $vis enum $Device { + $( + #[value(name = $name, alias = $xray_part)] + $variant, + )* + } + + impl $Device { + $vis fn as_str(self) -> &'static str { + match self { + $(Self::$variant => $name,)* + } + } + $vis fn xray_part(self) -> &'static str { + match self { + $(Self::$variant => $xray_part,)* + } + } + $vis fn xray_device(self) -> &'static str { + match self { + $(Self::$variant => $xray_device,)* + } + } + $vis fn xray_family(self) -> &'static str { + match self { + $(Self::$variant => $xray_family,)* + } + } + } + + struct DeviceVisitor; + + impl<'de> serde::de::Visitor<'de> for DeviceVisitor { + type Value = $Device; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a Xilinx device string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match $Device::from_str(v, false) { + Ok(v) => Ok(v), + Err(_) => Err(E::invalid_value(serde::de::Unexpected::Str(v), &self)), + } + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + match str::from_utf8(v).ok().and_then(|v| $Device::from_str(v, false).ok()) { + Some(v) => Ok(v), + None => Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)), + } + } + } + + impl<'de> Deserialize<'de> for $Device { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_string(DeviceVisitor) + } + } + + impl Serialize for $Device { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.as_str().serialize(serializer) + } + } + }; +} + +make_device_enum! { + pub enum Device { + #[ + name = "xc7a35ticsg324-1L", + xray_part = "xc7a35tcsg324-1", + xray_device = "xc7a35t", + xray_family = "artix7", + ] + Xc7a35ticsg324_1l, + #[ + name = "xc7a100ticsg324-1L", + xray_part = "xc7a100tcsg324-1", + xray_device = "xc7a100t", + xray_family = "artix7", + ] + Xc7a100ticsg324_1l, + } +} + +impl fmt::Display for Device { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] pub struct YosysNextpnrXrayRunNextpnrArgs { - #[command(flatten)] - pub common: XilinxArgs, #[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)] pub nextpnr_xilinx_chipdb_dir: PathBuf, + #[arg(long)] + pub device: Device, #[arg(long, default_value_t = 0)] pub nextpnr_xilinx_seed: i32, } @@ -756,12 +639,12 @@ pub struct YosysNextpnrXrayRunNextpnrArgs { impl ToArgs for YosysNextpnrXrayRunNextpnrArgs { fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { let Self { - common, nextpnr_xilinx_chipdb_dir, + device, nextpnr_xilinx_seed, } = self; - common.to_args(args); args.write_long_option_eq("nextpnr-xilinx-chipdb-dir", nextpnr_xilinx_chipdb_dir); + args.write_long_option_eq("device", device.as_str()); args.write_display_arg(format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}")); } } @@ -807,15 +690,14 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr { fn args_to_jobs( args: JobArgsAndDependencies>, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<( Self::AdditionalJobData, ::JobsAndKinds, )> { - args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + args.args_to_jobs_external_simple(params, |args, dependencies| { let YosysNextpnrXrayRunNextpnrArgs { - common, nextpnr_xilinx_chipdb_dir, + device, nextpnr_xilinx_seed, } = args.additional_args; let base_job = dependencies.get_job::(); @@ -825,7 +707,7 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr { let fasm_file = base_job.file_with_ext("fasm"); Ok(Self { nextpnr_xilinx_chipdb_dir: nextpnr_xilinx_chipdb_dir.intern_deref(), - device: common.require_device(base_job.platform(), global_params)?, + device, nextpnr_xilinx_seed, xdc_file: write_xdc_file.xdc_file, xdc_file_name: write_xdc_file @@ -960,12 +842,11 @@ impl ExternalCommand for YosysNextpnrXray { fn args_to_jobs( args: JobArgsAndDependencies>, params: &JobParams, - global_params: &GlobalParams, ) -> eyre::Result<( Self::AdditionalJobData, ::JobsAndKinds, )> { - args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { + args.args_to_jobs_external_simple(params, |args, dependencies| { let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args; let base_job = dependencies.get_job::(); let frames_file = base_job.file_with_ext("frames"); @@ -1037,7 +918,3 @@ pub(crate) fn built_in_job_kinds() -> impl IntoIterator { DynJobKind::new(ExternalCommandJobKind::::new()), ] } - -pub(crate) fn built_in_platforms() -> impl IntoIterator { - [] -} diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 04227ef..5b973d8 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1219,8 +1219,7 @@ "data": { "$kind": "Enum", "XdcLocation": "Visible", - "XdcIOStandard": "Visible", - "XdcCreateClock": "Visible" + "XdcIOStandard": "Visible" } }, "XdcLocationAnnotation": { @@ -1233,11 +1232,6 @@ "$kind": "Opaque" } }, - "XdcCreateClockAnnotation": { - "data": { - "$kind": "Opaque" - } - }, "Target": { "data": { "$kind": "Enum",