forked from libre-chip/fayalite
		
	Add peripherals and Arty A7 platforms -- blinky works correctly now on arty-a7-100t!
This commit is contained in:
		
							parent
							
								
									4d54f903be
								
							
						
					
					
						commit
						477a1f2d29
					
				
					 23 changed files with 2297 additions and 554 deletions
				
			
		|  | @ -21,4 +21,4 @@ jobs: | ||||||
|       - run: cargo test --doc --features=unstable-doc |       - run: cargo test --doc --features=unstable-doc | ||||||
|       - run: cargo 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: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher | ||||||
|       - 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)) |       - 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 | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -319,6 +319,7 @@ dependencies = [ | ||||||
|  "jobslot", |  "jobslot", | ||||||
|  "num-bigint", |  "num-bigint", | ||||||
|  "num-traits", |  "num-traits", | ||||||
|  |  "ordered-float", | ||||||
|  "petgraph", |  "petgraph", | ||||||
|  "serde", |  "serde", | ||||||
|  "serde_json", |  "serde_json", | ||||||
|  | @ -524,6 +525,17 @@ version = "1.19.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ordered-float" | ||||||
|  | version = "5.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" | ||||||
|  | dependencies = [ | ||||||
|  |  "num-traits", | ||||||
|  |  "rand", | ||||||
|  |  "serde", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "petgraph" | name = "petgraph" | ||||||
| version = "0.8.1" | version = "0.8.1" | ||||||
|  | @ -576,6 +588,25 @@ version = "0.7.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" | 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]] | [[package]] | ||||||
| name = "rustix" | name = "rustix" | ||||||
| version = "0.38.31" | version = "0.38.31" | ||||||
|  |  | ||||||
|  | @ -30,6 +30,7 @@ indexmap = { version = "2.5.0", features = ["serde"] } | ||||||
| jobslot = "0.2.23" | jobslot = "0.2.23" | ||||||
| num-bigint = "0.4.6" | num-bigint = "0.4.6" | ||||||
| num-traits = "0.2.16" | num-traits = "0.2.16" | ||||||
|  | ordered-float = { version = "5.1.0", features = ["serde"] } | ||||||
| petgraph = "0.8.1" | petgraph = "0.8.1" | ||||||
| prettyplease = "0.2.20" | prettyplease = "0.2.20" | ||||||
| proc-macro2 = "1.0.83" | proc-macro2 = "1.0.83" | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ hashbrown.workspace = true | ||||||
| jobslot.workspace = true | jobslot.workspace = true | ||||||
| num-bigint.workspace = true | num-bigint.workspace = true | ||||||
| num-traits.workspace = true | num-traits.workspace = true | ||||||
|  | ordered-float.workspace = true | ||||||
| petgraph.workspace = true | petgraph.workspace = true | ||||||
| serde_json.workspace = true | serde_json.workspace = true | ||||||
| serde.workspace = true | serde.workspace = true | ||||||
|  |  | ||||||
|  | @ -1,55 +1,64 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use fayalite::{ | use fayalite::prelude::*; | ||||||
|     build::{ToArgs, WriteArgs}, |  | ||||||
|     prelude::*, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| #[hdl_module] | #[hdl_module] | ||||||
| fn blinky(clock_frequency: u64) { | fn blinky(platform_io_builder: PlatformIOBuilder<'_>) { | ||||||
|     #[hdl] |     let clk_input = | ||||||
|     let clk: Clock = m.input(); |         platform_io_builder.peripherals_with_type::<peripherals::ClockInput>()[0].use_peripheral(); | ||||||
|     #[hdl] |     let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral(); | ||||||
|     let rst: SyncReset = m.input(); |  | ||||||
|     let cd = #[hdl] |     let cd = #[hdl] | ||||||
|     ClockDomain { |     ClockDomain { | ||||||
|         clk, |         clk: clk_input.clk, | ||||||
|         rst: rst.to_reset(), |         rst, | ||||||
|     }; |     }; | ||||||
|     let max_value = clock_frequency / 2 - 1; |     let max_value = (Expr::ty(clk_input).frequency() / 2.0).round_ties_even() as u64 - 1; | ||||||
|     let int_ty = UInt::range_inclusive(0..=max_value); |     let int_ty = UInt::range_inclusive(0..=max_value); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); |     let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let output_reg: Bool = reg_builder().clock_domain(cd).reset(false); |     let output_reg: Bool = reg_builder().clock_domain(cd).reset(false); | ||||||
|     #[hdl] |     #[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) { |     if counter_reg.cmp_eq(max_value) { | ||||||
|         connect_any(counter_reg, 0u8); |         connect_any(counter_reg, 0u8); | ||||||
|         connect(output_reg, !output_reg); |         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 { |     } else { | ||||||
|         connect_any(counter_reg, counter_reg + 1_hdl_u1); |         connect_any(counter_reg, counter_reg + 1_hdl_u1); | ||||||
|     } |     } | ||||||
|  |     for led in platform_io_builder.peripherals_with_type::<peripherals::Led>() { | ||||||
|  |         if let Ok(led) = led.try_use_peripheral() { | ||||||
|  |             connect(led.on, output_reg); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     for rgb_led in platform_io_builder.peripherals_with_type::<peripherals::RgbLed>() { | ||||||
|  |         if let Ok(rgb_led) = rgb_led.try_use_peripheral() { | ||||||
|  |             connect(rgb_led, rgb_output_reg); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let led: Bool = m.output(); |     let io = m.add_platform_io(platform_io_builder); | ||||||
|     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() { | fn main() { | ||||||
|     BuildCli::main(|_cli, ExtraArgs { clock_frequency }| { |     <BuildCli>::main("blinky", |_, platform, _| { | ||||||
|         Ok(JobParams::new(blinky(clock_frequency), "blinky")) |         Ok(JobParams::new(platform.wrap_main_module(blinky))) | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,9 +3,9 @@ | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{ |     build::{ | ||||||
|         ArgsWriter, BaseJob, CommandParams, GetJob, JobAndDependencies, JobAndKind, |         ArgsWriter, CommandParams, GlobalParams, JobAndDependencies, JobAndKind, | ||||||
|         JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs, |         JobArgsAndDependencies, JobDependencies, JobDependenciesHasBase, JobItem, JobItemName, | ||||||
|         JobParams, ToArgs, WriteArgs, |         JobKind, JobKindAndArgs, JobParams, ToArgs, WriteArgs, | ||||||
|     }, |     }, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, |     util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, | ||||||
|  | @ -990,12 +990,13 @@ pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Size | ||||||
|         + Serialize |         + Serialize | ||||||
|         + DeserializeOwned; |         + DeserializeOwned; | ||||||
|     type BaseJobPosition; |     type BaseJobPosition; | ||||||
|     type Dependencies: JobDependencies<JobsAndKinds: GetJob<BaseJob, Self::BaseJobPosition>>; |     type Dependencies: JobDependenciesHasBase; | ||||||
|     type ExternalProgram: ExternalProgramTrait; |     type ExternalProgram: ExternalProgramTrait; | ||||||
|     fn dependencies() -> Self::Dependencies; |     fn dependencies() -> Self::Dependencies; | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, |         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<( |     ) -> eyre::Result<( | ||||||
|         Self::AdditionalJobData, |         Self::AdditionalJobData, | ||||||
|         <Self::Dependencies as JobDependencies>::JobsAndKinds, |         <Self::Dependencies as JobDependencies>::JobsAndKinds, | ||||||
|  | @ -1028,6 +1029,7 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<Self>, |         args: JobArgsAndDependencies<Self>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<JobAndDependencies<Self>> { |     ) -> eyre::Result<JobAndDependencies<Self>> { | ||||||
|         let JobKindAndArgs { |         let JobKindAndArgs { | ||||||
|             kind, |             kind, | ||||||
|  | @ -1042,8 +1044,8 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> { | ||||||
|                     additional_args: _, |                     additional_args: _, | ||||||
|                 }, |                 }, | ||||||
|         } = args.args; |         } = args.args; | ||||||
|         let (additional_job_data, dependencies) = T::args_to_jobs(args, params)?; |         let (additional_job_data, dependencies) = T::args_to_jobs(args, params, global_params)?; | ||||||
|         let base_job = GetJob::<BaseJob, _>::get_job(&dependencies); |         let base_job = T::Dependencies::base_job(&dependencies); | ||||||
|         let job = ExternalCommandJob { |         let job = ExternalCommandJob { | ||||||
|             additional_job_data, |             additional_job_data, | ||||||
|             program_path, |             program_path, | ||||||
|  | @ -1078,7 +1080,8 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> { | ||||||
|         self, |         self, | ||||||
|         job: &Self::Job, |         job: &Self::Job, | ||||||
|         inputs: &[JobItem], |         inputs: &[JobItem], | ||||||
|         params: &JobParams, |         _params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|         acquired_job: &mut AcquiredJob, |         acquired_job: &mut AcquiredJob, | ||||||
|     ) -> eyre::Result<Vec<JobItem>> { |     ) -> eyre::Result<Vec<JobItem>> { | ||||||
|         assert!( |         assert!( | ||||||
|  | @ -1093,7 +1096,7 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> { | ||||||
|         } = job.command_params(); |         } = job.command_params(); | ||||||
|         ExternalJobCaching::new( |         ExternalJobCaching::new( | ||||||
|             &job.output_dir, |             &job.output_dir, | ||||||
|             ¶ms.application_name(), |             &global_params.application_name(), | ||||||
|             &T::job_kind_name(), |             &T::job_kind_name(), | ||||||
|             job.run_even_if_cached, |             job.run_even_if_cached, | ||||||
|         )? |         )? | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{ |     build::{ | ||||||
|         BaseJob, BaseJobKind, CommandParams, DynJobKind, JobAndDependencies, |         BaseJob, BaseJobKind, CommandParams, DynJobKind, GlobalParams, JobAndDependencies, | ||||||
|         JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, |         JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, | ||||||
|         ToArgs, WriteArgs, |         ToArgs, WriteArgs, | ||||||
|     }, |     }, | ||||||
|  | @ -64,9 +64,11 @@ impl JobKind for FirrtlJobKind { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<Self>, |         args: JobArgsAndDependencies<Self>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<JobAndDependencies<Self>> { |     ) -> eyre::Result<JobAndDependencies<Self>> { | ||||||
|         args.args_to_jobs_simple( |         args.args_to_jobs_simple( | ||||||
|             params, |             params, | ||||||
|  |             global_params, | ||||||
|             |_kind, FirrtlArgs { export_options }, dependencies| { |             |_kind, FirrtlArgs { export_options }, dependencies| { | ||||||
|                 Ok(Firrtl { |                 Ok(Firrtl { | ||||||
|                     base: dependencies.get_job::<BaseJob, _>().clone(), |                     base: dependencies.get_job::<BaseJob, _>().clone(), | ||||||
|  | @ -103,6 +105,7 @@ impl JobKind for FirrtlJobKind { | ||||||
|         job: &Self::Job, |         job: &Self::Job, | ||||||
|         inputs: &[JobItem], |         inputs: &[JobItem], | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         _global_params: &GlobalParams, | ||||||
|         _acquired_job: &mut AcquiredJob, |         _acquired_job: &mut AcquiredJob, | ||||||
|     ) -> eyre::Result<Vec<JobItem>> { |     ) -> eyre::Result<Vec<JobItem>> { | ||||||
|         let [JobItem::Path { path: input_path }] = *inputs else { |         let [JobItem::Path { path: input_path }] = *inputs else { | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{ |     build::{ | ||||||
|         BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies, |         BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams, | ||||||
|         JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, |         JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, | ||||||
|         JobKindAndDependencies, JobParams, ToArgs, WriteArgs, |         JobKindAndDependencies, JobParams, ToArgs, WriteArgs, | ||||||
|         external::{ |         external::{ | ||||||
|             ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, |             ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, | ||||||
|  | @ -201,6 +201,7 @@ impl JobKind for WriteSbyFileJobKind { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         mut args: JobArgsAndDependencies<Self>, |         mut args: JobArgsAndDependencies<Self>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<JobAndDependencies<Self>> { |     ) -> eyre::Result<JobAndDependencies<Self>> { | ||||||
|         args.dependencies |         args.dependencies | ||||||
|             .dependencies |             .dependencies | ||||||
|  | @ -209,7 +210,7 @@ impl JobKind for WriteSbyFileJobKind { | ||||||
|             .additional_args |             .additional_args | ||||||
|             .verilog_dialect |             .verilog_dialect | ||||||
|             .get_or_insert(VerilogDialect::Yosys); |             .get_or_insert(VerilogDialect::Yosys); | ||||||
|         args.args_to_jobs_simple(params, |_kind, args, dependencies| { |         args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { | ||||||
|             let FormalArgs { |             let FormalArgs { | ||||||
|                 sby_extra_args, |                 sby_extra_args, | ||||||
|                 formal_mode, |                 formal_mode, | ||||||
|  | @ -255,6 +256,7 @@ impl JobKind for WriteSbyFileJobKind { | ||||||
|         job: &Self::Job, |         job: &Self::Job, | ||||||
|         inputs: &[JobItem], |         inputs: &[JobItem], | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         _global_params: &GlobalParams, | ||||||
|         _acquired_job: &mut AcquiredJob, |         _acquired_job: &mut AcquiredJob, | ||||||
|     ) -> eyre::Result<Vec<JobItem>> { |     ) -> eyre::Result<Vec<JobItem>> { | ||||||
|         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); |         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); | ||||||
|  | @ -351,11 +353,12 @@ impl ExternalCommand for Formal { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, |         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<( |     ) -> eyre::Result<( | ||||||
|         Self::AdditionalJobData, |         Self::AdditionalJobData, | ||||||
|         <Self::Dependencies as JobDependencies>::JobsAndKinds, |         <Self::Dependencies as JobDependencies>::JobsAndKinds, | ||||||
|     )> { |     )> { | ||||||
|         args.args_to_jobs_external_simple(params, |args, dependencies| { |         args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { | ||||||
|             let FormalAdditionalArgs {} = args.additional_args; |             let FormalAdditionalArgs {} = args.additional_args; | ||||||
|             let write_sby_file = dependencies.get_job::<WriteSbyFileJob, _>().clone(); |             let write_sby_file = dependencies.get_job::<WriteSbyFileJob, _>().clone(); | ||||||
|             Ok(Formal { |             Ok(Formal { | ||||||
|  |  | ||||||
|  | @ -2,8 +2,11 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{DynJob, JobItem, JobItemName, JobParams, program_name_for_internal_jobs}, |     build::{ | ||||||
|  |         DynJob, GlobalParams, JobItem, JobItemName, JobParams, program_name_for_internal_jobs, | ||||||
|  |     }, | ||||||
|     intern::Interned, |     intern::Interned, | ||||||
|  |     platform::DynPlatform, | ||||||
|     util::{HashMap, HashSet, job_server::AcquiredJob}, |     util::{HashMap, HashSet, job_server::AcquiredJob}, | ||||||
| }; | }; | ||||||
| use eyre::{ContextCompat, eyre}; | use eyre::{ContextCompat, eyre}; | ||||||
|  | @ -489,15 +492,21 @@ impl JobGraph { | ||||||
|             Err(e) => panic!("error: {e}"), |             Err(e) => panic!("error: {e}"), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn to_unix_makefile(&self, extra_args: &[Interned<OsStr>]) -> Result<String, Utf8Error> { |     pub fn to_unix_makefile( | ||||||
|  |         &self, | ||||||
|  |         platform: Option<&DynPlatform>, | ||||||
|  |         extra_args: &[Interned<OsStr>], | ||||||
|  |     ) -> Result<String, Utf8Error> { | ||||||
|         self.to_unix_makefile_with_internal_program_prefix( |         self.to_unix_makefile_with_internal_program_prefix( | ||||||
|             &[program_name_for_internal_jobs()], |             &[program_name_for_internal_jobs()], | ||||||
|  |             platform, | ||||||
|             extra_args, |             extra_args, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|     pub fn to_unix_makefile_with_internal_program_prefix( |     pub fn to_unix_makefile_with_internal_program_prefix( | ||||||
|         &self, |         &self, | ||||||
|         internal_program_prefix: &[Interned<OsStr>], |         internal_program_prefix: &[Interned<OsStr>], | ||||||
|  |         platform: Option<&DynPlatform>, | ||||||
|         extra_args: &[Interned<OsStr>], |         extra_args: &[Interned<OsStr>], | ||||||
|     ) -> Result<String, Utf8Error> { |     ) -> Result<String, Utf8Error> { | ||||||
|         let mut retval = String::new(); |         let mut retval = String::new(); | ||||||
|  | @ -572,7 +581,11 @@ impl JobGraph { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             retval.push_str("\n\t"); |             retval.push_str("\n\t"); | ||||||
|             job.command_params_with_internal_program_prefix(internal_program_prefix, extra_args) |             job.command_params_with_internal_program_prefix( | ||||||
|  |                 internal_program_prefix, | ||||||
|  |                 platform, | ||||||
|  |                 extra_args, | ||||||
|  |             ) | ||||||
|             .to_unix_shell_line(&mut retval, |arg, output| { |             .to_unix_shell_line(&mut retval, |arg, output| { | ||||||
|                 write_str!( |                 write_str!( | ||||||
|                     output, |                     output, | ||||||
|  | @ -610,15 +623,21 @@ impl JobGraph { | ||||||
|         } |         } | ||||||
|         Ok(retval) |         Ok(retval) | ||||||
|     } |     } | ||||||
|     pub fn to_unix_shell_script(&self, extra_args: &[Interned<OsStr>]) -> String { |     pub fn to_unix_shell_script( | ||||||
|  |         &self, | ||||||
|  |         platform: Option<&DynPlatform>, | ||||||
|  |         extra_args: &[Interned<OsStr>], | ||||||
|  |     ) -> String { | ||||||
|         self.to_unix_shell_script_with_internal_program_prefix( |         self.to_unix_shell_script_with_internal_program_prefix( | ||||||
|             &[program_name_for_internal_jobs()], |             &[program_name_for_internal_jobs()], | ||||||
|  |             platform, | ||||||
|             extra_args, |             extra_args, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|     pub fn to_unix_shell_script_with_internal_program_prefix( |     pub fn to_unix_shell_script_with_internal_program_prefix( | ||||||
|         &self, |         &self, | ||||||
|         internal_program_prefix: &[Interned<OsStr>], |         internal_program_prefix: &[Interned<OsStr>], | ||||||
|  |         platform: Option<&DynPlatform>, | ||||||
|         extra_args: &[Interned<OsStr>], |         extra_args: &[Interned<OsStr>], | ||||||
|     ) -> String { |     ) -> String { | ||||||
|         let mut retval = String::from( |         let mut retval = String::from( | ||||||
|  | @ -630,7 +649,11 @@ impl JobGraph { | ||||||
|                 continue; |                 continue; | ||||||
|             }; |             }; | ||||||
|             let Ok(()) = job |             let Ok(()) = job | ||||||
|                 .command_params_with_internal_program_prefix(internal_program_prefix, extra_args) |                 .command_params_with_internal_program_prefix( | ||||||
|  |                     internal_program_prefix, | ||||||
|  |                     platform, | ||||||
|  |                     extra_args, | ||||||
|  |                 ) | ||||||
|                 .to_unix_shell_line(&mut retval, |arg, output| -> Result<(), Infallible> { |                 .to_unix_shell_line(&mut retval, |arg, output| -> Result<(), Infallible> { | ||||||
|                     write_str!(output, "{}", EscapeForUnixShell::new(&arg)); |                     write_str!(output, "{}", EscapeForUnixShell::new(&arg)); | ||||||
|                     Ok(()) |                     Ok(()) | ||||||
|  | @ -639,7 +662,7 @@ impl JobGraph { | ||||||
|         } |         } | ||||||
|         retval |         retval | ||||||
|     } |     } | ||||||
|     pub fn run(&self, params: &JobParams) -> eyre::Result<()> { |     pub fn run(&self, params: &JobParams, global_params: &GlobalParams) -> eyre::Result<()> { | ||||||
|         // use scope to auto-join threads on errors
 |         // use scope to auto-join threads on errors
 | ||||||
|         thread::scope(|scope| { |         thread::scope(|scope| { | ||||||
|             struct WaitingJobState { |             struct WaitingJobState { | ||||||
|  | @ -725,13 +748,18 @@ impl JobGraph { | ||||||
|                         job: DynJob, |                         job: DynJob, | ||||||
|                         inputs: Vec<JobItem>, |                         inputs: Vec<JobItem>, | ||||||
|                         params: &'a JobParams, |                         params: &'a JobParams, | ||||||
|  |                         global_params: &'a GlobalParams, | ||||||
|                         acquired_job: AcquiredJob, |                         acquired_job: AcquiredJob, | ||||||
|                         finished_jobs_sender: mpsc::Sender<<JobGraphInner as GraphBase>::NodeId>, |                         finished_jobs_sender: mpsc::Sender<<JobGraphInner as GraphBase>::NodeId>, | ||||||
|                     } |                     } | ||||||
|                     impl RunningJobInThread<'_> { |                     impl RunningJobInThread<'_> { | ||||||
|                         fn run(mut self) -> eyre::Result<Vec<JobItem>> { |                         fn run(mut self) -> eyre::Result<Vec<JobItem>> { | ||||||
|                             self.job |                             self.job.run( | ||||||
|                                 .run(&self.inputs, self.params, &mut self.acquired_job) |                                 &self.inputs, | ||||||
|  |                                 self.params, | ||||||
|  |                                 self.global_params, | ||||||
|  |                                 &mut self.acquired_job, | ||||||
|  |                             ) | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     impl Drop for RunningJobInThread<'_> { |                     impl Drop for RunningJobInThread<'_> { | ||||||
|  | @ -749,6 +777,7 @@ impl JobGraph { | ||||||
|                             }) |                             }) | ||||||
|                         }))?, |                         }))?, | ||||||
|                         params, |                         params, | ||||||
|  |                         global_params, | ||||||
|                         acquired_job: AcquiredJob::acquire()?, |                         acquired_job: AcquiredJob::acquire()?, | ||||||
|                         finished_jobs_sender: finished_jobs_sender.clone(), |                         finished_jobs_sender: finished_jobs_sender.clone(), | ||||||
|                     }; |                     }; | ||||||
|  |  | ||||||
|  | @ -4,10 +4,9 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{DynJobKind, JobKind, built_in_job_kinds}, |     build::{DynJobKind, JobKind, built_in_job_kinds}, | ||||||
|     intern::Interned, |     intern::Interned, | ||||||
|  |     util::InternedStrCompareAsStr, | ||||||
| }; | }; | ||||||
| use std::{ | use std::{ | ||||||
|     borrow::Borrow, |  | ||||||
|     cmp::Ordering, |  | ||||||
|     collections::BTreeMap, |     collections::BTreeMap, | ||||||
|     fmt, |     fmt, | ||||||
|     sync::{Arc, OnceLock, RwLock, RwLockWriteGuard}, |     sync::{Arc, OnceLock, RwLock, RwLockWriteGuard}, | ||||||
|  | @ -23,33 +22,6 @@ impl DynJobKind { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, PartialEq, Eq)] |  | ||||||
| struct InternedStrCompareAsStr(Interned<str>); |  | ||||||
| 
 |  | ||||||
| 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<Ordering> { |  | ||||||
|         Some(self.cmp(other)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Borrow<str> for InternedStrCompareAsStr { |  | ||||||
|     fn borrow(&self) -> &str { |  | ||||||
|         &self.0 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| struct JobKindRegistry { | struct JobKindRegistry { | ||||||
|     job_kinds: BTreeMap<InternedStrCompareAsStr, DynJobKind>, |     job_kinds: BTreeMap<InternedStrCompareAsStr, DynJobKind>, | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{ |     build::{ | ||||||
|         BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob, |         BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob, | ||||||
|         JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, |         GlobalParams, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, | ||||||
|         JobKindAndDependencies, JobParams, ToArgs, WriteArgs, |         JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs, | ||||||
|         external::{ |         external::{ | ||||||
|             ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, |             ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, | ||||||
|         }, |         }, | ||||||
|  | @ -152,11 +152,12 @@ impl ExternalCommand for UnadjustedVerilog { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, |         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<( |     ) -> eyre::Result<( | ||||||
|         Self::AdditionalJobData, |         Self::AdditionalJobData, | ||||||
|         <Self::Dependencies as JobDependencies>::JobsAndKinds, |         <Self::Dependencies as JobDependencies>::JobsAndKinds, | ||||||
|     )> { |     )> { | ||||||
|         args.args_to_jobs_external_simple(params, |args, dependencies| { |         args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { | ||||||
|             let UnadjustedVerilogArgs { |             let UnadjustedVerilogArgs { | ||||||
|                 firtool_extra_args, |                 firtool_extra_args, | ||||||
|                 verilog_dialect, |                 verilog_dialect, | ||||||
|  | @ -316,8 +317,9 @@ impl JobKind for VerilogJobKind { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<Self>, |         args: JobArgsAndDependencies<Self>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<JobAndDependencies<Self>> { |     ) -> eyre::Result<JobAndDependencies<Self>> { | ||||||
|         args.args_to_jobs_simple(params, |_kind, args, dependencies| { |         args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { | ||||||
|             let VerilogJobArgs {} = args; |             let VerilogJobArgs {} = args; | ||||||
|             let base_job = dependencies.get_job::<BaseJob, _>(); |             let base_job = dependencies.get_job::<BaseJob, _>(); | ||||||
|             Ok(VerilogJob { |             Ok(VerilogJob { | ||||||
|  | @ -364,6 +366,7 @@ impl JobKind for VerilogJobKind { | ||||||
|         job: &Self::Job, |         job: &Self::Job, | ||||||
|         inputs: &[JobItem], |         inputs: &[JobItem], | ||||||
|         _params: &JobParams, |         _params: &JobParams, | ||||||
|  |         _global_params: &GlobalParams, | ||||||
|         _acquired_job: &mut AcquiredJob, |         _acquired_job: &mut AcquiredJob, | ||||||
|     ) -> eyre::Result<Vec<JobItem>> { |     ) -> eyre::Result<Vec<JobItem>> { | ||||||
|         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); |         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); | ||||||
|  |  | ||||||
|  | @ -8,24 +8,30 @@ use crate::{ | ||||||
|     module::{Module, ModuleBuilder, ModuleIO, connect_with_loc, instance_with_loc, wire_with_loc}, |     module::{Module, ModuleBuilder, ModuleIO, connect_with_loc, instance_with_loc, wire_with_loc}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{CanonicalType, Type}, |     ty::{CanonicalType, Type}, | ||||||
|     util::HashMap, |     util::{HashMap, HashSet, InternedStrCompareAsStr}, | ||||||
| }; | }; | ||||||
|  | use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; | ||||||
| use std::{ | use std::{ | ||||||
|     any::{Any, TypeId}, |     any::{Any, TypeId}, | ||||||
|  |     borrow::Cow, | ||||||
|     cmp::Ordering, |     cmp::Ordering, | ||||||
|     collections::{BTreeMap, BTreeSet}, |     collections::{BTreeMap, BTreeSet}, | ||||||
|     convert::Infallible, |     convert::Infallible, | ||||||
|     fmt, |     fmt, | ||||||
|     hash::{Hash, Hasher}, |     hash::{Hash, Hasher}, | ||||||
|  |     iter::FusedIterator, | ||||||
|     marker::PhantomData, |     marker::PhantomData, | ||||||
|     mem, |     mem, | ||||||
|     sync::{Arc, Mutex, MutexGuard, OnceLock}, |     sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock, RwLockWriteGuard}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | pub mod peripherals; | ||||||
|  | 
 | ||||||
| trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug { | trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug { | ||||||
|     fn as_any(&self) -> &dyn Any; |     fn as_any(&self) -> &dyn Any; | ||||||
|     fn eq_dyn(&self, other: &dyn DynPlatformTrait) -> bool; |     fn eq_dyn(&self, other: &dyn DynPlatformTrait) -> bool; | ||||||
|     fn hash_dyn(&self, state: &mut dyn Hasher); |     fn hash_dyn(&self, state: &mut dyn Hasher); | ||||||
|  |     fn name_dyn(&self) -> Interned<str>; | ||||||
|     fn new_peripherals_dyn<'builder>( |     fn new_peripherals_dyn<'builder>( | ||||||
|         &self, |         &self, | ||||||
|         builder_factory: PeripheralsBuilderFactory<'builder>, |         builder_factory: PeripheralsBuilderFactory<'builder>, | ||||||
|  | @ -33,6 +39,7 @@ trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug { | ||||||
|     fn source_location_dyn(&self) -> SourceLocation; |     fn source_location_dyn(&self) -> SourceLocation; | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     fn add_peripherals_in_wrapper_module_dyn(&self, m: &ModuleBuilder, peripherals: DynPeripherals); |     fn add_peripherals_in_wrapper_module_dyn(&self, m: &ModuleBuilder, peripherals: DynPeripherals); | ||||||
|  |     fn aspects_dyn(&self) -> PlatformAspectSet; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<T: Platform> DynPlatformTrait for T { | impl<T: Platform> DynPlatformTrait for T { | ||||||
|  | @ -51,6 +58,10 @@ impl<T: Platform> DynPlatformTrait for T { | ||||||
|         self.hash(&mut state); |         self.hash(&mut state); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn name_dyn(&self) -> Interned<str> { | ||||||
|  |         self.name() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn new_peripherals_dyn<'builder>( |     fn new_peripherals_dyn<'builder>( | ||||||
|         &self, |         &self, | ||||||
|         builder_factory: PeripheralsBuilderFactory<'builder>, |         builder_factory: PeripheralsBuilderFactory<'builder>, | ||||||
|  | @ -80,6 +91,10 @@ impl<T: Platform> DynPlatformTrait for T { | ||||||
|         }; |         }; | ||||||
|         self.add_peripherals_in_wrapper_module(m, *peripherals) |         self.add_peripherals_in_wrapper_module(m, *peripherals) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn aspects_dyn(&self) -> PlatformAspectSet { | ||||||
|  |         self.aspects() | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
|  | @ -155,6 +170,9 @@ impl Peripherals for DynPeripherals { | ||||||
| 
 | 
 | ||||||
| impl Platform for DynPlatform { | impl Platform for DynPlatform { | ||||||
|     type Peripherals = DynPeripherals; |     type Peripherals = DynPeripherals; | ||||||
|  |     fn name(&self) -> Interned<str> { | ||||||
|  |         DynPlatformTrait::name_dyn(&*self.0) | ||||||
|  |     } | ||||||
|     fn new_peripherals<'a>( |     fn new_peripherals<'a>( | ||||||
|         &self, |         &self, | ||||||
|         builder_factory: PeripheralsBuilderFactory<'a>, |         builder_factory: PeripheralsBuilderFactory<'a>, | ||||||
|  | @ -168,6 +186,9 @@ impl Platform for DynPlatform { | ||||||
|     fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { |     fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { | ||||||
|         DynPlatformTrait::add_peripherals_in_wrapper_module_dyn(&*self.0, m, 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)] | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|  | @ -448,9 +469,7 @@ impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> { | ||||||
|             id, |             id, | ||||||
|             Box::new(move |state, peripheral_ref, wire| { |             Box::new(move |state, peripheral_ref, wire| { | ||||||
|                 on_use( |                 on_use( | ||||||
|                     state |                     <dyn Any>::downcast_mut::<S>(PeripheralsOnUseSharedState::as_any(state)) | ||||||
|                         .as_any() |  | ||||||
|                         .downcast_mut() |  | ||||||
|                         .expect("known to be correct type"), |                         .expect("known to be correct type"), | ||||||
|                     PeripheralRef::from_canonical(peripheral_ref), |                     PeripheralRef::from_canonical(peripheral_ref), | ||||||
|                     Expr::from_canonical(wire), |                     Expr::from_canonical(wire), | ||||||
|  | @ -518,6 +537,7 @@ impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[must_use] | ||||||
| pub struct Peripheral<T: Type> { | pub struct Peripheral<T: Type> { | ||||||
|     ty: T, |     ty: T, | ||||||
|     common: PeripheralCommon, |     common: PeripheralCommon, | ||||||
|  | @ -528,6 +548,27 @@ impl<T: Type> Peripheral<T> { | ||||||
|         let Self { ty, ref common } = *self; |         let Self { ty, ref common } = *self; | ||||||
|         PeripheralRef { ty, common } |         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<str> { | ||||||
|  |         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<BTreeSet<PeripheralId>> { | ||||||
|  |         self.as_ref().conflicts_with() | ||||||
|  |     } | ||||||
|  |     pub fn availability(&self) -> PeripheralAvailability { | ||||||
|  |         self.as_ref().availability() | ||||||
|  |     } | ||||||
|     pub fn is_available(&self) -> bool { |     pub fn is_available(&self) -> bool { | ||||||
|         self.as_ref().is_available() |         self.as_ref().is_available() | ||||||
|     } |     } | ||||||
|  | @ -588,7 +629,7 @@ impl<T: Type> Peripheral<T> { | ||||||
| 
 | 
 | ||||||
| impl<T: Type> fmt::Debug for Peripheral<T> { | impl<T: Type> fmt::Debug for Peripheral<T> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         self.as_ref().debug_fmt("Peripheral", f) |         self.as_ref().debug_common_fields("Peripheral", f).finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -599,7 +640,10 @@ pub struct UsedPeripheral<T: Type> { | ||||||
| 
 | 
 | ||||||
| impl<T: Type> fmt::Debug for UsedPeripheral<T> { | impl<T: Type> fmt::Debug for UsedPeripheral<T> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         self.as_ref().debug_fmt("UsedPeripheral", f) |         self.as_ref() | ||||||
|  |             .debug_common_fields("UsedPeripheral", f) | ||||||
|  |             .field("instance_io_field", &self.instance_io_field()) | ||||||
|  |             .finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -617,6 +661,24 @@ impl<T: Type> UsedPeripheral<T> { | ||||||
|     pub fn instance_io_field(&self) -> Expr<T> { |     pub fn instance_io_field(&self) -> Expr<T> { | ||||||
|         self.instance_io_field |         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<str> { | ||||||
|  |         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<BTreeSet<PeripheralId>> { | ||||||
|  |         self.as_ref().conflicts_with() | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone)] | #[derive(Copy, Clone)] | ||||||
|  | @ -627,7 +689,7 @@ pub struct PeripheralRef<'a, T: Type> { | ||||||
| 
 | 
 | ||||||
| impl<'a, T: Type> fmt::Debug for PeripheralRef<'a, T> { | impl<'a, T: Type> fmt::Debug for PeripheralRef<'a, T> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         self.debug_fmt("PeripheralRef", f) |         self.debug_common_fields("PeripheralRef", f).finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -662,7 +724,11 @@ impl fmt::Display for PeripheralUnavailableError { | ||||||
| impl std::error::Error for PeripheralUnavailableError {} | impl std::error::Error for PeripheralUnavailableError {} | ||||||
| 
 | 
 | ||||||
| impl<'a, T: Type> PeripheralRef<'a, T> { | impl<'a, T: Type> PeripheralRef<'a, T> { | ||||||
|     fn debug_fmt(&self, struct_name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn debug_common_fields<'f1, 'f2>( | ||||||
|  |         &self, | ||||||
|  |         struct_name: &str, | ||||||
|  |         f: &'f1 mut fmt::Formatter<'f2>, | ||||||
|  |     ) -> fmt::DebugStruct<'f1, 'f2> { | ||||||
|         let Self { |         let Self { | ||||||
|             ty, |             ty, | ||||||
|             common: |             common: | ||||||
|  | @ -673,12 +739,13 @@ impl<'a, T: Type> PeripheralRef<'a, T> { | ||||||
|                     peripherals_state: _, |                     peripherals_state: _, | ||||||
|                 }, |                 }, | ||||||
|         } = self; |         } = self; | ||||||
|         f.debug_struct(struct_name) |         let mut retval = f.debug_struct(struct_name); | ||||||
|  |         retval | ||||||
|             .field("ty", ty) |             .field("ty", ty) | ||||||
|             .field("id", id) |             .field("id", id) | ||||||
|             .field("is_input", is_input) |             .field("is_input", is_input) | ||||||
|             .field("availability", &self.availability()) |             .field("availability", &self.availability()); | ||||||
|             .finish() |         retval | ||||||
|     } |     } | ||||||
|     pub fn ty(&self) -> T { |     pub fn ty(&self) -> T { | ||||||
|         self.ty |         self.ty | ||||||
|  | @ -850,7 +917,7 @@ impl<'a, T: Type> PeripheralRef<'a, T> { | ||||||
|             flipped: self.is_input(), |             flipped: self.is_input(), | ||||||
|             ty: Expr::ty(canonical_wire), |             ty: Expr::ty(canonical_wire), | ||||||
|         }); |         }); | ||||||
|         on_use_function(shared_state, self.canonical(), canonical_wire); |         on_use_function(&mut **shared_state, self.canonical(), canonical_wire); | ||||||
|         drop(on_use_state); |         drop(on_use_state); | ||||||
|         Ok(wire) |         Ok(wire) | ||||||
|     } |     } | ||||||
|  | @ -1031,8 +1098,327 @@ 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<Self>) -> Arc<dyn Any + Send + Sync>; | ||||||
|  |     fn eq_dyn(&self, other: &dyn PlatformAspectTrait) -> bool; | ||||||
|  |     fn hash_dyn(&self, state: &mut dyn Hasher); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: 'static + Send + Sync + fmt::Debug + Eq + Hash> PlatformAspectTrait for T { | ||||||
|  |     fn any_ref(&self) -> &dyn Any { | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> { | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn eq_dyn(&self, other: &dyn PlatformAspectTrait) -> bool { | ||||||
|  |         other | ||||||
|  |             .any_ref() | ||||||
|  |             .downcast_ref::<T>() | ||||||
|  |             .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<dyn PlatformAspectTrait>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Hash for PlatformAspect { | ||||||
|  |     fn hash<H: Hasher>(&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<T: 'static + Send + Sync + fmt::Debug + Hash + Eq>(value: Arc<T>) -> Self { | ||||||
|  |         Self { | ||||||
|  |             type_id: TypeId::of::<T>(), | ||||||
|  |             type_name: std::any::type_name::<T>(), | ||||||
|  |             value, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn new<T: 'static + Send + Sync + fmt::Debug + Hash + Eq>(value: T) -> Self { | ||||||
|  |         Self::new_arc(Arc::new(value)) | ||||||
|  |     } | ||||||
|  |     pub fn type_id(&self) -> TypeId { | ||||||
|  |         self.type_id | ||||||
|  |     } | ||||||
|  |     pub fn downcast_arc<T: 'static + Send + Sync + fmt::Debug>(self) -> Result<Arc<T>, Self> { | ||||||
|  |         if self.type_id == TypeId::of::<T>() { | ||||||
|  |             let Ok(retval) = self.value.any_arc().downcast() else { | ||||||
|  |                 unreachable!(); | ||||||
|  |             }; | ||||||
|  |             Ok(retval) | ||||||
|  |         } else { | ||||||
|  |             Err(self) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn downcast_unwrap_or_clone<T: 'static + Send + Sync + fmt::Debug + Clone>( | ||||||
|  |         self, | ||||||
|  |     ) -> Result<T, Self> { | ||||||
|  |         Ok(Arc::unwrap_or_clone(self.downcast_arc()?)) | ||||||
|  |     } | ||||||
|  |     pub fn downcast_ref<T: 'static + Send + Sync + fmt::Debug>(&self) -> Option<&T> { | ||||||
|  |         PlatformAspectTrait::any_ref(&*self.value).downcast_ref() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Default)] | ||||||
|  | pub struct PlatformAspectSet { | ||||||
|  |     aspects_by_type_id: Arc<HashMap<TypeId, HashSet<PlatformAspect>>>, | ||||||
|  |     aspects: Arc<Vec<PlatformAspect>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PlatformAspectSet { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         Self::default() | ||||||
|  |     } | ||||||
|  |     pub fn insert_new<T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( | ||||||
|  |         &mut self, | ||||||
|  |         value: T, | ||||||
|  |     ) -> bool { | ||||||
|  |         self.insert(PlatformAspect::new(value)) | ||||||
|  |     } | ||||||
|  |     pub fn insert_new_arc<T: 'static + Send + Sync + fmt::Debug + Hash + Eq>( | ||||||
|  |         &mut self, | ||||||
|  |         value: Arc<T>, | ||||||
|  |     ) -> bool { | ||||||
|  |         self.insert(PlatformAspect::new_arc(value)) | ||||||
|  |     } | ||||||
|  |     fn insert_inner( | ||||||
|  |         aspects_by_type_id: &mut HashMap<TypeId, HashSet<PlatformAspect>>, | ||||||
|  |         aspects: &mut Vec<PlatformAspect>, | ||||||
|  |         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<Item = &'a PlatformAspect> + FusedIterator + ExactSizeIterator + 'a | ||||||
|  |     { | ||||||
|  |         self.aspects_by_type_id | ||||||
|  |             .get(&TypeId::of::<T>()) | ||||||
|  |             .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<Item = &'a T> + FusedIterator + ExactSizeIterator + 'a { | ||||||
|  |         self.get_aspects_by_type::<T>() | ||||||
|  |             .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::<T>(); | ||||||
|  |         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<Item = Arc<T>> + FusedIterator + ExactSizeIterator + 'a { | ||||||
|  |         self.get_aspects_by_type::<T>().map(|aspect| { | ||||||
|  |             aspect | ||||||
|  |                 .clone() | ||||||
|  |                 .downcast_arc() | ||||||
|  |                 .ok() | ||||||
|  |                 .expect("already checked type") | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Extend<&'a PlatformAspect> for PlatformAspectSet { | ||||||
|  |     fn extend<T: IntoIterator<Item = &'a PlatformAspect>>(&mut self, iter: T) { | ||||||
|  |         self.extend(iter.into_iter().cloned()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Extend<PlatformAspect> for PlatformAspectSet { | ||||||
|  |     fn extend<T: IntoIterator<Item = PlatformAspect>>(&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<T: IntoIterator<Item = &'a PlatformAspect>>(iter: T) -> Self { | ||||||
|  |         let mut retval = Self::default(); | ||||||
|  |         retval.extend(iter); | ||||||
|  |         retval | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FromIterator<PlatformAspect> for PlatformAspectSet { | ||||||
|  |     fn from_iter<T: IntoIterator<Item = PlatformAspect>>(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<Vec<PlatformAspect>>, | ||||||
|  |     indexes: std::ops::Range<usize>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Iterator for PlatformAspectsIntoIter { | ||||||
|  |     type Item = PlatformAspect; | ||||||
|  | 
 | ||||||
|  |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|  |         self.indexes.next().map(|index| self.aspects[index].clone()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn size_hint(&self) -> (usize, Option<usize>) { | ||||||
|  |         self.indexes.size_hint() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn count(self) -> usize { | ||||||
|  |         self.indexes.len() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn last(mut self) -> Option<Self::Item> { | ||||||
|  |         self.next_back() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn nth(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.indexes.nth(n).map(|index| self.aspects[index].clone()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn fold<B, F>(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::Item> { | ||||||
|  |         self.indexes | ||||||
|  |             .next_back() | ||||||
|  |             .map(|index| self.aspects[index].clone()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn nth_back(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.indexes | ||||||
|  |             .nth_back(n) | ||||||
|  |             .map(|index| self.aspects[index].clone()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn rfold<B, F>(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 { | pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { | ||||||
|     type Peripherals: Peripherals; |     type Peripherals: Peripherals; | ||||||
|  |     fn name(&self) -> Interned<str>; | ||||||
|     fn new_peripherals<'builder>( |     fn new_peripherals<'builder>( | ||||||
|         &self, |         &self, | ||||||
|         builder_factory: PeripheralsBuilderFactory<'builder>, |         builder_factory: PeripheralsBuilderFactory<'builder>, | ||||||
|  | @ -1113,7 +1499,7 @@ pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { | ||||||
|             crate::module::ModuleKind::Normal, |             crate::module::ModuleKind::Normal, | ||||||
|             |m| { |             |m| { | ||||||
|                 let instance = |                 let instance = | ||||||
|                     instance_with_loc("main", main_module.intern(), SourceLocation::caller()); |                     instance_with_loc("main", main_module.intern(), self.source_location()); | ||||||
|                 let output_expr = Expr::field(instance, &output_module_io.bundle_field().name); |                 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 mut state = peripherals_state.state.lock().expect("shouldn't be poison"); | ||||||
|                 let PeripheralsStateEnum::BuildingWrapperModule( |                 let PeripheralsStateEnum::BuildingWrapperModule( | ||||||
|  | @ -1143,4 +1529,395 @@ pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq { | ||||||
|         self.try_wrap_main_module(|p| Ok(make_main_module(p))) |         self.try_wrap_main_module(|p| Ok(make_main_module(p))) | ||||||
|             .unwrap_or_else(|e: Infallible| match e {}) |             .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<InternedStrCompareAsStr, DynPlatform>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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<Arc<PlatformRegistry>> { | ||||||
|  |     type Locked = RwLockWriteGuard<'static, Arc<PlatformRegistry>>; | ||||||
|  |     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<Arc<Self>> { | ||||||
|  |         static REGISTRY: OnceLock<RwLock<Arc<PlatformRegistry>>> = OnceLock::new(); | ||||||
|  |         REGISTRY.get_or_init(Default::default) | ||||||
|  |     } | ||||||
|  |     fn try_register<L: PlatformRegistryRegisterLock>( | ||||||
|  |         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<L: PlatformRegistryRegisterLock>(lock: L, platform: DynPlatform) { | ||||||
|  |         match Self::try_register(lock, platform) { | ||||||
|  |             Err(e) => panic!("{e}"), | ||||||
|  |             Ok(()) => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     fn get() -> Arc<Self> { | ||||||
|  |         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<PlatformRegistry>); | ||||||
|  | 
 | ||||||
|  | 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::Item> { | ||||||
|  |         self.0.next() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn size_hint(&self) -> (usize, Option<usize>) { | ||||||
|  |         self.0.size_hint() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn count(self) -> usize | ||||||
|  |     where | ||||||
|  |         Self: Sized, | ||||||
|  |     { | ||||||
|  |         self.0.count() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn last(self) -> Option<Self::Item> { | ||||||
|  |         self.0.last() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn nth(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.0.nth(n) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn fold<B, F>(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::Item> { | ||||||
|  |         self.0.next_back() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn nth_back(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.0.nth_back(n) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn rfold<B, F>(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<str>, &'a DynPlatform); | ||||||
|  | 
 | ||||||
|  |     fn next(&mut self) -> Option<Self::Item> { | ||||||
|  |         self.0.next().map(|(name, platform)| (name.0, platform)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn size_hint(&self) -> (usize, Option<usize>) { | ||||||
|  |         self.0.size_hint() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn count(self) -> usize | ||||||
|  |     where | ||||||
|  |         Self: Sized, | ||||||
|  |     { | ||||||
|  |         self.0.count() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn last(self) -> Option<Self::Item> { | ||||||
|  |         self.0.last().map(|(name, platform)| (name.0, platform)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn nth(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.0.nth(n).map(|(name, platform)| (name.0, platform)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn fold<B, F>(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::Item> { | ||||||
|  |         self.0 | ||||||
|  |             .next_back() | ||||||
|  |             .map(|(name, platform)| (name.0, platform)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn nth_back(&mut self, n: usize) -> Option<Self::Item> { | ||||||
|  |         self.0 | ||||||
|  |             .nth_back(n) | ||||||
|  |             .map(|(name, platform)| (name.0, platform)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn rfold<B, F>(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<K: Platform>(kind: K) { | ||||||
|  |     DynPlatform::new(kind).register(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Serialize for DynPlatform { | ||||||
|  |     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||||
|  |     where | ||||||
|  |         S: Serializer, | ||||||
|  |     { | ||||||
|  |         self.name().serialize(serializer) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'de> Deserialize<'de> for DynPlatform { | ||||||
|  |     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||||
|  |     where | ||||||
|  |         D: Deserializer<'de>, | ||||||
|  |     { | ||||||
|  |         let name = Cow::<str>::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<str>, | ||||||
|  |     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<clap::builder::PossibleValue> { | ||||||
|  |         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<Self::Value> { | ||||||
|  |         clap::builder::EnumValueParser::<DynPlatformValueEnum>::new() | ||||||
|  |             .parse_ref(cmd, arg, value) | ||||||
|  |             .map(|v| v.platform) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn possible_values( | ||||||
|  |         &self, | ||||||
|  |     ) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> { | ||||||
|  |         static ENUM_VALUE_PARSER: OnceLock<clap::builder::EnumValueParser<DynPlatformValueEnum>> = | ||||||
|  |             OnceLock::new(); | ||||||
|  |         ENUM_VALUE_PARSER | ||||||
|  |             .get_or_init(clap::builder::EnumValueParser::<DynPlatformValueEnum>::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<Item = DynPlatform> { | ||||||
|  |     crate::vendor::built_in_platforms() | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										52
									
								
								crates/fayalite/src/platform/peripherals.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								crates/fayalite/src/platform/peripherals.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | // 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<f64>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[hdl(no_runtime_generics, no_static)] | ||||||
|  | pub struct ClockInput { | ||||||
|  |     pub clk: Clock, | ||||||
|  |     pub properties: PhantomConst<ClockInputProperties>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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, | ||||||
|  | } | ||||||
|  | @ -28,6 +28,7 @@ pub use crate::{ | ||||||
|         memory, memory_array, memory_with_init, reg_builder, wire, |         memory, memory_array, memory_with_init, reg_builder, wire, | ||||||
|     }, |     }, | ||||||
|     phantom_const::PhantomConst, |     phantom_const::PhantomConst, | ||||||
|  |     platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals}, | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, |     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, | ||||||
|     sim::{ |     sim::{ | ||||||
|  |  | ||||||
|  | @ -2,8 +2,8 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     build::{ |     build::{ | ||||||
|         BaseJobArgs, BaseJobKind, JobArgsAndDependencies, JobKindAndArgs, JobParams, NoArgs, |         BaseJobArgs, BaseJobKind, GlobalParams, JobArgsAndDependencies, JobKindAndArgs, JobParams, | ||||||
|         RunBuild, |         NoArgs, RunBuild, | ||||||
|         external::{ExternalCommandArgs, ExternalCommandJobKind}, |         external::{ExternalCommandArgs, ExternalCommandJobKind}, | ||||||
|         firrtl::{FirrtlArgs, FirrtlJobKind}, |         firrtl::{FirrtlArgs, FirrtlJobKind}, | ||||||
|         formal::{Formal, FormalAdditionalArgs, FormalArgs, FormalMode, WriteSbyFileJobKind}, |         formal::{Formal, FormalAdditionalArgs, FormalArgs, FormalMode, WriteSbyFileJobKind}, | ||||||
|  | @ -106,7 +106,7 @@ fn make_assert_formal_args( | ||||||
| ) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> { | ) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> { | ||||||
|     let args = JobKindAndArgs { |     let args = JobKindAndArgs { | ||||||
|         kind: BaseJobKind, |         kind: BaseJobKind, | ||||||
|         args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name)), |         args: BaseJobArgs::from_output_dir_and_env(get_assert_formal_target_path(&test_name), None), | ||||||
|     }; |     }; | ||||||
|     let dependencies = JobArgsAndDependencies { |     let dependencies = JobArgsAndDependencies { | ||||||
|         args, |         args, | ||||||
|  | @ -168,9 +168,9 @@ pub fn try_assert_formal<M: AsRef<Module<T>>, T: BundleType>( | ||||||
|         solver, |         solver, | ||||||
|         export_options, |         export_options, | ||||||
|     )? |     )? | ||||||
|     .run( |     .run_without_platform( | ||||||
|         |NoArgs {}| Ok(JobParams::new(module, APP_NAME)), |         |NoArgs {}| Ok(JobParams::new(module)), | ||||||
|         clap::Command::new(APP_NAME), // not actually used, so we can use an arbitrary value
 |         &GlobalParams::new(None, APP_NAME), | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -33,7 +33,6 @@ pub use const_cmp::{ | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use scoped_ref::ScopedRef; | pub use scoped_ref::ScopedRef; | ||||||
| 
 | 
 | ||||||
| pub(crate) use misc::chain; |  | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| pub use misc::{ | pub use misc::{ | ||||||
|     BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, |     BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, | ||||||
|  | @ -42,6 +41,7 @@ pub use misc::{ | ||||||
|     os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty, |     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, |     serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range, | ||||||
| }; | }; | ||||||
|  | pub(crate) use misc::{InternedStrCompareAsStr, chain}; | ||||||
| 
 | 
 | ||||||
| pub mod job_server; | pub mod job_server; | ||||||
| pub mod prefix_sum; | pub mod prefix_sum; | ||||||
|  |  | ||||||
|  | @ -585,3 +585,30 @@ pub fn os_str_strip_suffix<'a>(os_str: &'a OsStr, suffix: impl AsRef<str>) -> Op | ||||||
|             unsafe { OsStr::from_encoded_bytes_unchecked(bytes) } |             unsafe { OsStr::from_encoded_bytes_unchecked(bytes) } | ||||||
|         }) |         }) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Copy, Clone, PartialEq, Eq)] | ||||||
|  | pub(crate) struct InternedStrCompareAsStr(pub(crate) Interned<str>); | ||||||
|  | 
 | ||||||
|  | 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<std::cmp::Ordering> { | ||||||
|  |         Some(self.cmp(other)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl std::borrow::Borrow<str> for InternedStrCompareAsStr { | ||||||
|  |     fn borrow(&self) -> &str { | ||||||
|  |         &self.0 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -6,3 +6,7 @@ pub mod xilinx; | ||||||
| pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> { | pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> { | ||||||
|     xilinx::built_in_job_kinds() |     xilinx::built_in_job_kinds() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> { | ||||||
|  |     xilinx::built_in_platforms() | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										177
									
								
								crates/fayalite/src/vendor/xilinx.rs
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										177
									
								
								crates/fayalite/src/vendor/xilinx.rs
									
										
									
									
										vendored
									
									
								
							|  | @ -1,8 +1,18 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{annotations::make_annotation_enum, intern::Interned}; | use crate::{ | ||||||
|  |     annotations::make_annotation_enum, | ||||||
|  |     build::{GlobalParams, ToArgs, WriteArgs}, | ||||||
|  |     intern::Interned, | ||||||
|  |     prelude::{DynPlatform, Platform}, | ||||||
|  | }; | ||||||
|  | use clap::ValueEnum; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  | use std::fmt; | ||||||
| 
 | 
 | ||||||
|  | pub mod arty_a7; | ||||||
|  | pub mod primitives; | ||||||
| pub mod yosys_nextpnr_prjxray; | pub mod yosys_nextpnr_prjxray; | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|  | @ -23,6 +33,167 @@ make_annotation_enum! { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> { | #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] | ||||||
|     yosys_nextpnr_prjxray::built_in_job_kinds() | pub struct XilinxArgs { | ||||||
|  |     #[arg(long)] | ||||||
|  |     pub device: Option<Device>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl XilinxArgs { | ||||||
|  |     pub fn require_device( | ||||||
|  |         &self, | ||||||
|  |         platform: Option<&DynPlatform>, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|  |     ) -> clap::error::Result<Device> { | ||||||
|  |         if let Some(device) = self.device { | ||||||
|  |             return Ok(device); | ||||||
|  |         } | ||||||
|  |         if let Some(device) = | ||||||
|  |             platform.and_then(|platform| platform.aspects().get_single_by_type::<Device>().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<E>(self, v: &str) -> Result<Self::Value, E> | ||||||
|  |             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<E>(self, v: &[u8]) -> Result<Self::Value, E> | ||||||
|  |             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<D>(deserializer: D) -> Result<Self, D::Error> | ||||||
|  |             where | ||||||
|  |                 D: serde::Deserializer<'de>, | ||||||
|  |             { | ||||||
|  |                 deserializer.deserialize_string(DeviceVisitor) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         impl Serialize for $Device { | ||||||
|  |             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||||
|  |             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<Item = crate::build::DynJobKind> { | ||||||
|  |     arty_a7::built_in_job_kinds() | ||||||
|  |         .into_iter() | ||||||
|  |         .chain(yosys_nextpnr_prjxray::built_in_job_kinds()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> { | ||||||
|  |     arty_a7::built_in_platforms() | ||||||
|  |         .into_iter() | ||||||
|  |         .chain(yosys_nextpnr_prjxray::built_in_platforms()) | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										301
									
								
								crates/fayalite/src/vendor/xilinx/arty_a7.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								crates/fayalite/src/vendor/xilinx/arty_a7.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,301 @@ | ||||||
|  | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
|  | // See Notices.txt for copyright information
 | ||||||
|  | 
 | ||||||
|  | use std::sync::OnceLock; | ||||||
|  | 
 | ||||||
|  | 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}, | ||||||
|  |     }, | ||||||
|  |     prelude::*, | ||||||
|  |     vendor::xilinx::{ | ||||||
|  |         Device, XdcIOStandardAnnotation, XdcLocationAnnotation, | ||||||
|  |         primitives::{self, BUFGCE, STARTUPE2_default_inputs}, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 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<PlatformAspectSet> = 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: Peripheral<ClockInput>, | ||||||
|  |     rst: Peripheral<Reset>, | ||||||
|  |     rst_sync: Peripheral<SyncReset>, | ||||||
|  |     ld0: Peripheral<RgbLed>, | ||||||
|  |     ld1: Peripheral<RgbLed>, | ||||||
|  |     ld2: Peripheral<RgbLed>, | ||||||
|  |     ld3: Peripheral<RgbLed>, | ||||||
|  |     ld4: Peripheral<Led>, | ||||||
|  |     ld5: Peripheral<Led>, | ||||||
|  |     ld6: Peripheral<Led>, | ||||||
|  |     ld7: Peripheral<Led>, | ||||||
|  |     // TODO: add rest of peripherals when we need them
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Peripherals for ArtyA7Peripherals { | ||||||
|  |     fn append_peripherals<'a>(&'a self, peripherals: &mut Vec<PeripheralRef<'a, CanonicalType>>) { | ||||||
|  |         let Self { | ||||||
|  |             clk100, | ||||||
|  |             rst, | ||||||
|  |             rst_sync, | ||||||
|  |             ld0, | ||||||
|  |             ld1, | ||||||
|  |             ld2, | ||||||
|  |             ld3, | ||||||
|  |             ld4, | ||||||
|  |             ld5, | ||||||
|  |             ld6, | ||||||
|  |             ld7, | ||||||
|  |         } = self; | ||||||
|  |         clk100.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); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ArtyA7Platform { | ||||||
|  |     fn make_aspects(self) -> PlatformAspectSet { | ||||||
|  |         let mut retval = PlatformAspectSet::new(); | ||||||
|  |         retval.insert_new(self.device()); | ||||||
|  |         retval | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Platform for ArtyA7Platform { | ||||||
|  |     type Peripherals = ArtyA7Peripherals; | ||||||
|  | 
 | ||||||
|  |     fn name(&self) -> Interned<str> { | ||||||
|  |         self.as_str().intern() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn new_peripherals<'builder>( | ||||||
|  |         &self, | ||||||
|  |         builder_factory: PeripheralsBuilderFactory<'builder>, | ||||||
|  |     ) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) { | ||||||
|  |         let mut builder = builder_factory.builder(); | ||||||
|  |         ( | ||||||
|  |             ArtyA7Peripherals { | ||||||
|  |                 clk100: builder.input_peripheral("clk100", ClockInput::new(100e6)), | ||||||
|  |                 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), | ||||||
|  |             }, | ||||||
|  |             builder.finish(), | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn source_location(&self) -> SourceLocation { | ||||||
|  |         SourceLocation::builtin() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { | ||||||
|  |         let ArtyA7Peripherals { | ||||||
|  |             clk100, | ||||||
|  |             rst, | ||||||
|  |             rst_sync, | ||||||
|  |             ld0, | ||||||
|  |             ld1, | ||||||
|  |             ld2, | ||||||
|  |             ld3, | ||||||
|  |             ld4, | ||||||
|  |             ld5, | ||||||
|  |             ld6, | ||||||
|  |             ld7, | ||||||
|  |         } = 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 clk100_buf = make_buffered_input("clk100", "E3", "LVCMOS33", false); | ||||||
|  |         let startup = instance_with_loc( | ||||||
|  |             "startup", | ||||||
|  |             STARTUPE2_default_inputs(), | ||||||
|  |             SourceLocation::builtin(), | ||||||
|  |         ); | ||||||
|  |         let clk100_sync = instance_with_loc("clk100_sync", BUFGCE(), SourceLocation::builtin()); | ||||||
|  |         connect(clk100_sync.CE, startup.EOS); | ||||||
|  |         connect(clk100_sync.I, clk100_buf); | ||||||
|  |         if let Some(clk100) = clk100.into_used() { | ||||||
|  |             connect(clk100.instance_io_field().clk, clk100_sync.O); | ||||||
|  |         } | ||||||
|  |         let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true); | ||||||
|  |         let rst_sync_cd = wire_with_loc( | ||||||
|  |             "rst_sync_cd", | ||||||
|  |             SourceLocation::builtin(), | ||||||
|  |             ClockDomain[AsyncReset], | ||||||
|  |         ); | ||||||
|  |         connect(rst_sync_cd.clk, clk100_sync.O); | ||||||
|  |         connect(rst_sync_cd.rst, rst_buf.to_async_reset()); | ||||||
|  |         let [rst_sync_0, rst_sync_1] = std::array::from_fn(|index| { | ||||||
|  |             let rst_sync = | ||||||
|  |                 reg_builder_with_loc(&format!("rst_sync_{index}"), SourceLocation::builtin()) | ||||||
|  |                     .clock_domain(rst_sync_cd) | ||||||
|  |                     .reset(true) | ||||||
|  |                     .build(); | ||||||
|  |             annotate( | ||||||
|  |                 rst_sync, | ||||||
|  |                 SVAttributeAnnotation { | ||||||
|  |                     text: "ASYNC_REG = \"TRUE\"".intern(), | ||||||
|  |                 }, | ||||||
|  |             ); | ||||||
|  |             annotate(rst_sync, DontTouchAnnotation); | ||||||
|  |             rst_sync | ||||||
|  |         }); | ||||||
|  |         connect(rst_sync_0, false); | ||||||
|  |         connect(rst_sync_1, rst_sync_0); | ||||||
|  |         let rst_value = rst_sync_1.to_sync_reset(); | ||||||
|  |         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); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn aspects(&self) -> PlatformAspectSet { | ||||||
|  |         self.get_aspects().clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> { | ||||||
|  |     [] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = DynPlatform> { | ||||||
|  |     ArtyA7Platform::VARIANTS | ||||||
|  |         .iter() | ||||||
|  |         .map(|&v| DynPlatform::new(v)) | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								crates/fayalite/src/vendor/xilinx/primitives.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								crates/fayalite/src/vendor/xilinx/primitives.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | // 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: Bool = 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(); | ||||||
|  | } | ||||||
|  | @ -4,8 +4,8 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     annotations::Annotation, |     annotations::Annotation, | ||||||
|     build::{ |     build::{ | ||||||
|         BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies, |         BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams, | ||||||
|         JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, |         JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, | ||||||
|         JobKindAndDependencies, ToArgs, WriteArgs, |         JobKindAndDependencies, ToArgs, WriteArgs, | ||||||
|         external::{ |         external::{ | ||||||
|             ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, |             ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait, | ||||||
|  | @ -18,9 +18,10 @@ use crate::{ | ||||||
|     module::{Module, NameId}, |     module::{Module, NameId}, | ||||||
|     prelude::JobParams, |     prelude::JobParams, | ||||||
|     util::job_server::AcquiredJob, |     util::job_server::AcquiredJob, | ||||||
|     vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation}, |     vendor::xilinx::{ | ||||||
|  |         Device, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation, XilinxArgs, | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| use clap::ValueEnum; |  | ||||||
| use eyre::Context; | use eyre::Context; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use std::{ | use std::{ | ||||||
|  | @ -105,6 +106,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         mut args: JobArgsAndDependencies<Self>, |         mut args: JobArgsAndDependencies<Self>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<JobAndDependencies<Self>> { |     ) -> eyre::Result<JobAndDependencies<Self>> { | ||||||
|         args.dependencies |         args.dependencies | ||||||
|             .dependencies |             .dependencies | ||||||
|  | @ -113,7 +115,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { | ||||||
|             .additional_args |             .additional_args | ||||||
|             .verilog_dialect |             .verilog_dialect | ||||||
|             .get_or_insert(VerilogDialect::Yosys); |             .get_or_insert(VerilogDialect::Yosys); | ||||||
|         args.args_to_jobs_simple(params, |_kind, args, dependencies| { |         args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { | ||||||
|             let YosysNextpnrXrayWriteYsFileArgs {} = args; |             let YosysNextpnrXrayWriteYsFileArgs {} = args; | ||||||
|             let base_job = dependencies.get_job::<BaseJob, _>(); |             let base_job = dependencies.get_job::<BaseJob, _>(); | ||||||
|             let verilog_job = dependencies.get_job::<VerilogJob, _>(); |             let verilog_job = dependencies.get_job::<VerilogJob, _>(); | ||||||
|  | @ -153,6 +155,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind { | ||||||
|         job: &Self::Job, |         job: &Self::Job, | ||||||
|         inputs: &[JobItem], |         inputs: &[JobItem], | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         _global_params: &GlobalParams, | ||||||
|         _acquired_job: &mut AcquiredJob, |         _acquired_job: &mut AcquiredJob, | ||||||
|     ) -> eyre::Result<Vec<JobItem>> { |     ) -> eyre::Result<Vec<JobItem>> { | ||||||
|         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); |         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); | ||||||
|  | @ -260,11 +263,12 @@ impl ExternalCommand for YosysNextpnrXraySynth { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, |         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<( |     ) -> eyre::Result<( | ||||||
|         Self::AdditionalJobData, |         Self::AdditionalJobData, | ||||||
|         <Self::Dependencies as JobDependencies>::JobsAndKinds, |         <Self::Dependencies as JobDependencies>::JobsAndKinds, | ||||||
|     )> { |     )> { | ||||||
|         args.args_to_jobs_external_simple(params, |args, dependencies| { |         args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { | ||||||
|             let YosysNextpnrXraySynthArgs {} = args.additional_args; |             let YosysNextpnrXraySynthArgs {} = args.additional_args; | ||||||
|             Ok(Self { |             Ok(Self { | ||||||
|                 write_ys_file: dependencies.job.job.clone(), |                 write_ys_file: dependencies.job.job.clone(), | ||||||
|  | @ -350,6 +354,9 @@ impl From<fmt::Error> for WriteXdcContentsError { | ||||||
| 
 | 
 | ||||||
| fn tcl_escape(s: impl AsRef<str>) -> String { | fn tcl_escape(s: impl AsRef<str>) -> String { | ||||||
|     let s = s.as_ref(); |     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)); |     let mut retval = String::with_capacity(s.len().saturating_add(2)); | ||||||
|     retval.push('"'); |     retval.push('"'); | ||||||
|     for ch in s.chars() { |     for ch in s.chars() { | ||||||
|  | @ -429,6 +436,7 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<Self>, |         args: JobArgsAndDependencies<Self>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<JobAndDependencies<Self>> { |     ) -> eyre::Result<JobAndDependencies<Self>> { | ||||||
|         let firrtl_export_options = args |         let firrtl_export_options = args | ||||||
|             .dependencies |             .dependencies | ||||||
|  | @ -439,7 +447,7 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { | ||||||
|             .args |             .args | ||||||
|             .args |             .args | ||||||
|             .export_options; |             .export_options; | ||||||
|         args.args_to_jobs_simple(params, |_kind, args, dependencies| { |         args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { | ||||||
|             let YosysNextpnrXrayWriteXdcFileArgs {} = args; |             let YosysNextpnrXrayWriteXdcFileArgs {} = args; | ||||||
|             let base_job = dependencies.get_job::<BaseJob, _>(); |             let base_job = dependencies.get_job::<BaseJob, _>(); | ||||||
|             Ok(YosysNextpnrXrayWriteXdcFile { |             Ok(YosysNextpnrXrayWriteXdcFile { | ||||||
|  | @ -474,23 +482,13 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind { | ||||||
|         job: &Self::Job, |         job: &Self::Job, | ||||||
|         inputs: &[JobItem], |         inputs: &[JobItem], | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         _global_params: &GlobalParams, | ||||||
|         _acquired_job: &mut AcquiredJob, |         _acquired_job: &mut AcquiredJob, | ||||||
|     ) -> eyre::Result<Vec<JobItem>> { |     ) -> eyre::Result<Vec<JobItem>> { | ||||||
|         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); |         assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); | ||||||
|         let mut xdc = String::new(); |         let mut xdc = String::new(); | ||||||
|         job.write_xdc_contents(&mut xdc, params.main_module())?; |         job.write_xdc_contents(&mut xdc, params.main_module())?; | ||||||
|         // TODO: create actual .xdc from input module
 |         std::fs::write(job.xdc_file, xdc)?; | ||||||
|         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 }]) |         Ok(vec![JobItem::Path { path: job.xdc_file }]) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -508,130 +506,12 @@ 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<E>(self, v: &str) -> Result<Self::Value, E> |  | ||||||
|             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<E>(self, v: &[u8]) -> Result<Self::Value, E> |  | ||||||
|             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<D>(deserializer: D) -> Result<Self, D::Error> |  | ||||||
|             where |  | ||||||
|                 D: serde::Deserializer<'de>, |  | ||||||
|             { |  | ||||||
|                 deserializer.deserialize_string(DeviceVisitor) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         impl Serialize for $Device { |  | ||||||
|             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |  | ||||||
|             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)] | #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] | ||||||
| pub struct YosysNextpnrXrayRunNextpnrArgs { | pub struct YosysNextpnrXrayRunNextpnrArgs { | ||||||
|  |     #[command(flatten)] | ||||||
|  |     pub common: XilinxArgs, | ||||||
|     #[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)] |     #[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)] | ||||||
|     pub nextpnr_xilinx_chipdb_dir: PathBuf, |     pub nextpnr_xilinx_chipdb_dir: PathBuf, | ||||||
|     #[arg(long)] |  | ||||||
|     pub device: Device, |  | ||||||
|     #[arg(long, default_value_t = 0)] |     #[arg(long, default_value_t = 0)] | ||||||
|     pub nextpnr_xilinx_seed: i32, |     pub nextpnr_xilinx_seed: i32, | ||||||
| } | } | ||||||
|  | @ -639,12 +519,12 @@ pub struct YosysNextpnrXrayRunNextpnrArgs { | ||||||
| impl ToArgs for YosysNextpnrXrayRunNextpnrArgs { | impl ToArgs for YosysNextpnrXrayRunNextpnrArgs { | ||||||
|     fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { |     fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { | ||||||
|         let Self { |         let Self { | ||||||
|  |             common, | ||||||
|             nextpnr_xilinx_chipdb_dir, |             nextpnr_xilinx_chipdb_dir, | ||||||
|             device, |  | ||||||
|             nextpnr_xilinx_seed, |             nextpnr_xilinx_seed, | ||||||
|         } = self; |         } = self; | ||||||
|  |         common.to_args(args); | ||||||
|         args.write_long_option_eq("nextpnr-xilinx-chipdb-dir", nextpnr_xilinx_chipdb_dir); |         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}")); |         args.write_display_arg(format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}")); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -690,14 +570,15 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, |         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<( |     ) -> eyre::Result<( | ||||||
|         Self::AdditionalJobData, |         Self::AdditionalJobData, | ||||||
|         <Self::Dependencies as JobDependencies>::JobsAndKinds, |         <Self::Dependencies as JobDependencies>::JobsAndKinds, | ||||||
|     )> { |     )> { | ||||||
|         args.args_to_jobs_external_simple(params, |args, dependencies| { |         args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { | ||||||
|             let YosysNextpnrXrayRunNextpnrArgs { |             let YosysNextpnrXrayRunNextpnrArgs { | ||||||
|  |                 common, | ||||||
|                 nextpnr_xilinx_chipdb_dir, |                 nextpnr_xilinx_chipdb_dir, | ||||||
|                 device, |  | ||||||
|                 nextpnr_xilinx_seed, |                 nextpnr_xilinx_seed, | ||||||
|             } = args.additional_args; |             } = args.additional_args; | ||||||
|             let base_job = dependencies.get_job::<BaseJob, _>(); |             let base_job = dependencies.get_job::<BaseJob, _>(); | ||||||
|  | @ -707,7 +588,7 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr { | ||||||
|             let fasm_file = base_job.file_with_ext("fasm"); |             let fasm_file = base_job.file_with_ext("fasm"); | ||||||
|             Ok(Self { |             Ok(Self { | ||||||
|                 nextpnr_xilinx_chipdb_dir: nextpnr_xilinx_chipdb_dir.intern_deref(), |                 nextpnr_xilinx_chipdb_dir: nextpnr_xilinx_chipdb_dir.intern_deref(), | ||||||
|                 device, |                 device: common.require_device(base_job.platform(), global_params)?, | ||||||
|                 nextpnr_xilinx_seed, |                 nextpnr_xilinx_seed, | ||||||
|                 xdc_file: write_xdc_file.xdc_file, |                 xdc_file: write_xdc_file.xdc_file, | ||||||
|                 xdc_file_name: write_xdc_file |                 xdc_file_name: write_xdc_file | ||||||
|  | @ -842,11 +723,12 @@ impl ExternalCommand for YosysNextpnrXray { | ||||||
|     fn args_to_jobs( |     fn args_to_jobs( | ||||||
|         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, |         args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>, | ||||||
|         params: &JobParams, |         params: &JobParams, | ||||||
|  |         global_params: &GlobalParams, | ||||||
|     ) -> eyre::Result<( |     ) -> eyre::Result<( | ||||||
|         Self::AdditionalJobData, |         Self::AdditionalJobData, | ||||||
|         <Self::Dependencies as JobDependencies>::JobsAndKinds, |         <Self::Dependencies as JobDependencies>::JobsAndKinds, | ||||||
|     )> { |     )> { | ||||||
|         args.args_to_jobs_external_simple(params, |args, dependencies| { |         args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { | ||||||
|             let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args; |             let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args; | ||||||
|             let base_job = dependencies.get_job::<BaseJob, _>(); |             let base_job = dependencies.get_job::<BaseJob, _>(); | ||||||
|             let frames_file = base_job.file_with_ext("frames"); |             let frames_file = base_job.file_with_ext("frames"); | ||||||
|  | @ -918,3 +800,7 @@ pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> { | ||||||
|         DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrXray>::new()), |         DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrXray>::new()), | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> { | ||||||
|  |     [] | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue