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 doc --features=unstable-doc
|
||||
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher
|
||||
- run: cargo run --example blinky yosys-nextpnr-xray --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",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"ordered-float",
|
||||
"petgraph",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -524,6 +525,17 @@ version = "1.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.1"
|
||||
|
@ -576,6 +588,25 @@ version = "0.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
|
|
|
@ -30,6 +30,7 @@ indexmap = { version = "2.5.0", features = ["serde"] }
|
|||
jobslot = "0.2.23"
|
||||
num-bigint = "0.4.6"
|
||||
num-traits = "0.2.16"
|
||||
ordered-float = { version = "5.1.0", features = ["serde"] }
|
||||
petgraph = "0.8.1"
|
||||
prettyplease = "0.2.20"
|
||||
proc-macro2 = "1.0.83"
|
||||
|
|
|
@ -26,6 +26,7 @@ hashbrown.workspace = true
|
|||
jobslot.workspace = true
|
||||
num-bigint.workspace = true
|
||||
num-traits.workspace = true
|
||||
ordered-float.workspace = true
|
||||
petgraph.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
|
|
|
@ -1,55 +1,64 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::{
|
||||
build::{ToArgs, WriteArgs},
|
||||
prelude::*,
|
||||
};
|
||||
use fayalite::prelude::*;
|
||||
|
||||
#[hdl_module]
|
||||
fn blinky(clock_frequency: u64) {
|
||||
#[hdl]
|
||||
let clk: Clock = m.input();
|
||||
#[hdl]
|
||||
let rst: SyncReset = m.input();
|
||||
fn blinky(platform_io_builder: PlatformIOBuilder<'_>) {
|
||||
let clk_input =
|
||||
platform_io_builder.peripherals_with_type::<peripherals::ClockInput>()[0].use_peripheral();
|
||||
let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral();
|
||||
let cd = #[hdl]
|
||||
ClockDomain {
|
||||
clk,
|
||||
rst: rst.to_reset(),
|
||||
clk: clk_input.clk,
|
||||
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);
|
||||
#[hdl]
|
||||
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
|
||||
#[hdl]
|
||||
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
|
||||
#[hdl]
|
||||
let rgb_output_reg = reg_builder().clock_domain(cd).reset(
|
||||
#[hdl]
|
||||
peripherals::RgbLed {
|
||||
r: false,
|
||||
g: false,
|
||||
b: false,
|
||||
},
|
||||
);
|
||||
#[hdl]
|
||||
if counter_reg.cmp_eq(max_value) {
|
||||
connect_any(counter_reg, 0u8);
|
||||
connect(output_reg, !output_reg);
|
||||
connect(rgb_output_reg.r, !rgb_output_reg.r);
|
||||
#[hdl]
|
||||
if rgb_output_reg.r {
|
||||
connect(rgb_output_reg.g, !rgb_output_reg.g);
|
||||
#[hdl]
|
||||
if rgb_output_reg.g {
|
||||
connect(rgb_output_reg.b, !rgb_output_reg.b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connect_any(counter_reg, counter_reg + 1_hdl_u1);
|
||||
}
|
||||
#[hdl]
|
||||
let led: Bool = m.output();
|
||||
connect(led, output_reg);
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct ExtraArgs {
|
||||
/// clock frequency in hertz
|
||||
#[arg(long, default_value = "1000000", value_parser = clap::value_parser!(u64).range(2..))]
|
||||
clock_frequency: u64,
|
||||
}
|
||||
|
||||
impl ToArgs for ExtraArgs {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self { clock_frequency } = self;
|
||||
args.write_arg(format!("--clock-frequency={clock_frequency}"));
|
||||
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]
|
||||
let io = m.add_platform_io(platform_io_builder);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
BuildCli::main(|_cli, ExtraArgs { clock_frequency }| {
|
||||
Ok(JobParams::new(blinky(clock_frequency), "blinky"))
|
||||
<BuildCli>::main("blinky", |_, platform, _| {
|
||||
Ok(JobParams::new(platform.wrap_main_module(blinky)))
|
||||
});
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,9 +3,9 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
ArgsWriter, BaseJob, CommandParams, GetJob, JobAndDependencies, JobAndKind,
|
||||
JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs,
|
||||
JobParams, ToArgs, WriteArgs,
|
||||
ArgsWriter, CommandParams, GlobalParams, JobAndDependencies, JobAndKind,
|
||||
JobArgsAndDependencies, JobDependencies, JobDependenciesHasBase, JobItem, JobItemName,
|
||||
JobKind, JobKindAndArgs, JobParams, ToArgs, WriteArgs,
|
||||
},
|
||||
intern::{Intern, Interned},
|
||||
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
|
||||
+ DeserializeOwned;
|
||||
type BaseJobPosition;
|
||||
type Dependencies: JobDependencies<JobsAndKinds: GetJob<BaseJob, Self::BaseJobPosition>>;
|
||||
type Dependencies: JobDependenciesHasBase;
|
||||
type ExternalProgram: ExternalProgramTrait;
|
||||
fn dependencies() -> Self::Dependencies;
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
||||
|
@ -1028,6 +1029,7 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
let JobKindAndArgs {
|
||||
kind,
|
||||
|
@ -1042,8 +1044,8 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
additional_args: _,
|
||||
},
|
||||
} = args.args;
|
||||
let (additional_job_data, dependencies) = T::args_to_jobs(args, params)?;
|
||||
let base_job = GetJob::<BaseJob, _>::get_job(&dependencies);
|
||||
let (additional_job_data, dependencies) = T::args_to_jobs(args, params, global_params)?;
|
||||
let base_job = T::Dependencies::base_job(&dependencies);
|
||||
let job = ExternalCommandJob {
|
||||
additional_job_data,
|
||||
program_path,
|
||||
|
@ -1078,7 +1080,8 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
self,
|
||||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
params: &JobParams,
|
||||
_params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(
|
||||
|
@ -1093,7 +1096,7 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
} = job.command_params();
|
||||
ExternalJobCaching::new(
|
||||
&job.output_dir,
|
||||
¶ms.application_name(),
|
||||
&global_params.application_name(),
|
||||
&T::job_kind_name(),
|
||||
job.run_even_if_cached,
|
||||
)?
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJob, BaseJobKind, CommandParams, DynJobKind, JobAndDependencies,
|
||||
BaseJob, BaseJobKind, CommandParams, DynJobKind, GlobalParams, JobAndDependencies,
|
||||
JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams,
|
||||
ToArgs, WriteArgs,
|
||||
},
|
||||
|
@ -64,9 +64,11 @@ impl JobKind for FirrtlJobKind {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
args.args_to_jobs_simple(
|
||||
params,
|
||||
global_params,
|
||||
|_kind, FirrtlArgs { export_options }, dependencies| {
|
||||
Ok(Firrtl {
|
||||
base: dependencies.get_job::<BaseJob, _>().clone(),
|
||||
|
@ -103,6 +105,7 @@ impl JobKind for FirrtlJobKind {
|
|||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
params: &JobParams,
|
||||
_global_params: &GlobalParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
let [JobItem::Path { path: input_path }] = *inputs else {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies,
|
||||
JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams,
|
||||
JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
||||
JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
|
@ -201,6 +201,7 @@ impl JobKind for WriteSbyFileJobKind {
|
|||
fn args_to_jobs(
|
||||
mut args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
args.dependencies
|
||||
.dependencies
|
||||
|
@ -209,7 +210,7 @@ impl JobKind for WriteSbyFileJobKind {
|
|||
.additional_args
|
||||
.verilog_dialect
|
||||
.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 {
|
||||
sby_extra_args,
|
||||
formal_mode,
|
||||
|
@ -255,6 +256,7 @@ impl JobKind for WriteSbyFileJobKind {
|
|||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
params: &JobParams,
|
||||
_global_params: &GlobalParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||
|
@ -351,11 +353,12 @@ impl ExternalCommand for Formal {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<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 write_sby_file = dependencies.get_job::<WriteSbyFileJob, _>().clone();
|
||||
Ok(Formal {
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
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,
|
||||
platform::DynPlatform,
|
||||
util::{HashMap, HashSet, job_server::AcquiredJob},
|
||||
};
|
||||
use eyre::{ContextCompat, eyre};
|
||||
|
@ -489,15 +492,21 @@ impl JobGraph {
|
|||
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(
|
||||
&[program_name_for_internal_jobs()],
|
||||
platform,
|
||||
extra_args,
|
||||
)
|
||||
}
|
||||
pub fn to_unix_makefile_with_internal_program_prefix(
|
||||
&self,
|
||||
internal_program_prefix: &[Interned<OsStr>],
|
||||
platform: Option<&DynPlatform>,
|
||||
extra_args: &[Interned<OsStr>],
|
||||
) -> Result<String, Utf8Error> {
|
||||
let mut retval = String::new();
|
||||
|
@ -572,19 +581,23 @@ impl JobGraph {
|
|||
}
|
||||
}
|
||||
retval.push_str("\n\t");
|
||||
job.command_params_with_internal_program_prefix(internal_program_prefix, extra_args)
|
||||
.to_unix_shell_line(&mut retval, |arg, output| {
|
||||
write_str!(
|
||||
output,
|
||||
"{}",
|
||||
EscapeForUnixMakefile::new(
|
||||
arg,
|
||||
UnixMakefileEscapeKind::RecipeWithShellEscaping,
|
||||
&mut needed_variables
|
||||
)?
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
job.command_params_with_internal_program_prefix(
|
||||
internal_program_prefix,
|
||||
platform,
|
||||
extra_args,
|
||||
)
|
||||
.to_unix_shell_line(&mut retval, |arg, output| {
|
||||
write_str!(
|
||||
output,
|
||||
"{}",
|
||||
EscapeForUnixMakefile::new(
|
||||
arg,
|
||||
UnixMakefileEscapeKind::RecipeWithShellEscaping,
|
||||
&mut needed_variables
|
||||
)?
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
retval.push_str("\n\n");
|
||||
}
|
||||
if !phony_targets.is_empty() {
|
||||
|
@ -610,15 +623,21 @@ impl JobGraph {
|
|||
}
|
||||
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(
|
||||
&[program_name_for_internal_jobs()],
|
||||
platform,
|
||||
extra_args,
|
||||
)
|
||||
}
|
||||
pub fn to_unix_shell_script_with_internal_program_prefix(
|
||||
&self,
|
||||
internal_program_prefix: &[Interned<OsStr>],
|
||||
platform: Option<&DynPlatform>,
|
||||
extra_args: &[Interned<OsStr>],
|
||||
) -> String {
|
||||
let mut retval = String::from(
|
||||
|
@ -630,7 +649,11 @@ impl JobGraph {
|
|||
continue;
|
||||
};
|
||||
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> {
|
||||
write_str!(output, "{}", EscapeForUnixShell::new(&arg));
|
||||
Ok(())
|
||||
|
@ -639,7 +662,7 @@ impl JobGraph {
|
|||
}
|
||||
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
|
||||
thread::scope(|scope| {
|
||||
struct WaitingJobState {
|
||||
|
@ -725,13 +748,18 @@ impl JobGraph {
|
|||
job: DynJob,
|
||||
inputs: Vec<JobItem>,
|
||||
params: &'a JobParams,
|
||||
global_params: &'a GlobalParams,
|
||||
acquired_job: AcquiredJob,
|
||||
finished_jobs_sender: mpsc::Sender<<JobGraphInner as GraphBase>::NodeId>,
|
||||
}
|
||||
impl RunningJobInThread<'_> {
|
||||
fn run(mut self) -> eyre::Result<Vec<JobItem>> {
|
||||
self.job
|
||||
.run(&self.inputs, self.params, &mut self.acquired_job)
|
||||
self.job.run(
|
||||
&self.inputs,
|
||||
self.params,
|
||||
self.global_params,
|
||||
&mut self.acquired_job,
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Drop for RunningJobInThread<'_> {
|
||||
|
@ -749,6 +777,7 @@ impl JobGraph {
|
|||
})
|
||||
}))?,
|
||||
params,
|
||||
global_params,
|
||||
acquired_job: AcquiredJob::acquire()?,
|
||||
finished_jobs_sender: finished_jobs_sender.clone(),
|
||||
};
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
use crate::{
|
||||
build::{DynJobKind, JobKind, built_in_job_kinds},
|
||||
intern::Interned,
|
||||
util::InternedStrCompareAsStr,
|
||||
};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
collections::BTreeMap,
|
||||
fmt,
|
||||
sync::{Arc, OnceLock, RwLock, RwLockWriteGuard},
|
||||
|
@ -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)]
|
||||
struct JobKindRegistry {
|
||||
job_kinds: BTreeMap<InternedStrCompareAsStr, DynJobKind>,
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
use crate::{
|
||||
build::{
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GetJobPositionJob,
|
||||
JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
||||
JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
GlobalParams, JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem,
|
||||
JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
|
@ -152,11 +152,12 @@ impl ExternalCommand for UnadjustedVerilog {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<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 {
|
||||
firtool_extra_args,
|
||||
verilog_dialect,
|
||||
|
@ -316,8 +317,9 @@ impl JobKind for VerilogJobKind {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> 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 base_job = dependencies.get_job::<BaseJob, _>();
|
||||
Ok(VerilogJob {
|
||||
|
@ -364,6 +366,7 @@ impl JobKind for VerilogJobKind {
|
|||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
_params: &JobParams,
|
||||
_global_params: &GlobalParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
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},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
util::HashMap,
|
||||
util::{HashMap, HashSet, InternedStrCompareAsStr},
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::Cow,
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
convert::Infallible,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter::FusedIterator,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
sync::{Arc, Mutex, MutexGuard, OnceLock},
|
||||
sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock, RwLockWriteGuard},
|
||||
};
|
||||
|
||||
pub mod peripherals;
|
||||
|
||||
trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn eq_dyn(&self, other: &dyn DynPlatformTrait) -> bool;
|
||||
fn hash_dyn(&self, state: &mut dyn Hasher);
|
||||
fn name_dyn(&self) -> Interned<str>;
|
||||
fn new_peripherals_dyn<'builder>(
|
||||
&self,
|
||||
builder_factory: PeripheralsBuilderFactory<'builder>,
|
||||
|
@ -33,6 +39,7 @@ trait DynPlatformTrait: 'static + Send + Sync + fmt::Debug {
|
|||
fn source_location_dyn(&self) -> SourceLocation;
|
||||
#[track_caller]
|
||||
fn add_peripherals_in_wrapper_module_dyn(&self, m: &ModuleBuilder, peripherals: DynPeripherals);
|
||||
fn aspects_dyn(&self) -> PlatformAspectSet;
|
||||
}
|
||||
|
||||
impl<T: Platform> DynPlatformTrait for T {
|
||||
|
@ -51,6 +58,10 @@ impl<T: Platform> DynPlatformTrait for T {
|
|||
self.hash(&mut state);
|
||||
}
|
||||
|
||||
fn name_dyn(&self) -> Interned<str> {
|
||||
self.name()
|
||||
}
|
||||
|
||||
fn new_peripherals_dyn<'builder>(
|
||||
&self,
|
||||
builder_factory: PeripheralsBuilderFactory<'builder>,
|
||||
|
@ -80,6 +91,10 @@ impl<T: Platform> DynPlatformTrait for T {
|
|||
};
|
||||
self.add_peripherals_in_wrapper_module(m, *peripherals)
|
||||
}
|
||||
|
||||
fn aspects_dyn(&self) -> PlatformAspectSet {
|
||||
self.aspects()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -155,6 +170,9 @@ impl Peripherals for DynPeripherals {
|
|||
|
||||
impl Platform for DynPlatform {
|
||||
type Peripherals = DynPeripherals;
|
||||
fn name(&self) -> Interned<str> {
|
||||
DynPlatformTrait::name_dyn(&*self.0)
|
||||
}
|
||||
fn new_peripherals<'a>(
|
||||
&self,
|
||||
builder_factory: PeripheralsBuilderFactory<'a>,
|
||||
|
@ -168,6 +186,9 @@ impl Platform for DynPlatform {
|
|||
fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) {
|
||||
DynPlatformTrait::add_peripherals_in_wrapper_module_dyn(&*self.0, m, peripherals);
|
||||
}
|
||||
fn aspects(&self) -> PlatformAspectSet {
|
||||
DynPlatformTrait::aspects_dyn(&*self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
@ -448,9 +469,7 @@ impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> {
|
|||
id,
|
||||
Box::new(move |state, peripheral_ref, wire| {
|
||||
on_use(
|
||||
state
|
||||
.as_any()
|
||||
.downcast_mut()
|
||||
<dyn Any>::downcast_mut::<S>(PeripheralsOnUseSharedState::as_any(state))
|
||||
.expect("known to be correct type"),
|
||||
PeripheralRef::from_canonical(peripheral_ref),
|
||||
Expr::from_canonical(wire),
|
||||
|
@ -518,6 +537,7 @@ impl<'a, S: PeripheralsOnUseSharedState> PeripheralsBuilder<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct Peripheral<T: Type> {
|
||||
ty: T,
|
||||
common: PeripheralCommon,
|
||||
|
@ -528,6 +548,27 @@ impl<T: Type> Peripheral<T> {
|
|||
let Self { ty, ref common } = *self;
|
||||
PeripheralRef { ty, common }
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.as_ref().ty()
|
||||
}
|
||||
pub fn id(&self) -> PeripheralId {
|
||||
self.as_ref().id()
|
||||
}
|
||||
pub fn name(&self) -> Interned<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 {
|
||||
self.as_ref().is_available()
|
||||
}
|
||||
|
@ -588,7 +629,7 @@ impl<T: Type> Peripheral<T> {
|
|||
|
||||
impl<T: Type> fmt::Debug for Peripheral<T> {
|
||||
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> {
|
||||
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> {
|
||||
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)]
|
||||
|
@ -627,7 +689,7 @@ pub struct PeripheralRef<'a, T: Type> {
|
|||
|
||||
impl<'a, T: Type> fmt::Debug for PeripheralRef<'a, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.debug_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<'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 {
|
||||
ty,
|
||||
common:
|
||||
|
@ -673,12 +739,13 @@ impl<'a, T: Type> PeripheralRef<'a, T> {
|
|||
peripherals_state: _,
|
||||
},
|
||||
} = self;
|
||||
f.debug_struct(struct_name)
|
||||
let mut retval = f.debug_struct(struct_name);
|
||||
retval
|
||||
.field("ty", ty)
|
||||
.field("id", id)
|
||||
.field("is_input", is_input)
|
||||
.field("availability", &self.availability())
|
||||
.finish()
|
||||
.field("availability", &self.availability());
|
||||
retval
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
|
@ -850,7 +917,7 @@ impl<'a, T: Type> PeripheralRef<'a, T> {
|
|||
flipped: self.is_input(),
|
||||
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);
|
||||
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 {
|
||||
type Peripherals: Peripherals;
|
||||
fn name(&self) -> Interned<str>;
|
||||
fn new_peripherals<'builder>(
|
||||
&self,
|
||||
builder_factory: PeripheralsBuilderFactory<'builder>,
|
||||
|
@ -1113,7 +1499,7 @@ pub trait Platform: Clone + 'static + Send + Sync + fmt::Debug + Hash + Eq {
|
|||
crate::module::ModuleKind::Normal,
|
||||
|m| {
|
||||
let instance =
|
||||
instance_with_loc("main", main_module.intern(), 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 mut state = peripherals_state.state.lock().expect("shouldn't be poison");
|
||||
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)))
|
||||
.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,
|
||||
},
|
||||
phantom_const::PhantomConst,
|
||||
platform::{DynPlatform, Platform, PlatformIOBuilder, peripherals},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||
sim::{
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJobArgs, BaseJobKind, JobArgsAndDependencies, JobKindAndArgs, JobParams, NoArgs,
|
||||
RunBuild,
|
||||
BaseJobArgs, BaseJobKind, GlobalParams, JobArgsAndDependencies, JobKindAndArgs, JobParams,
|
||||
NoArgs, RunBuild,
|
||||
external::{ExternalCommandArgs, ExternalCommandJobKind},
|
||||
firrtl::{FirrtlArgs, FirrtlJobKind},
|
||||
formal::{Formal, FormalAdditionalArgs, FormalArgs, FormalMode, WriteSbyFileJobKind},
|
||||
|
@ -106,7 +106,7 @@ fn make_assert_formal_args(
|
|||
) -> eyre::Result<JobArgsAndDependencies<ExternalCommandJobKind<Formal>>> {
|
||||
let args = JobKindAndArgs {
|
||||
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 {
|
||||
args,
|
||||
|
@ -168,9 +168,9 @@ pub fn try_assert_formal<M: AsRef<Module<T>>, T: BundleType>(
|
|||
solver,
|
||||
export_options,
|
||||
)?
|
||||
.run(
|
||||
|NoArgs {}| Ok(JobParams::new(module, APP_NAME)),
|
||||
clap::Command::new(APP_NAME), // not actually used, so we can use an arbitrary value
|
||||
.run_without_platform(
|
||||
|NoArgs {}| Ok(JobParams::new(module)),
|
||||
&GlobalParams::new(None, APP_NAME),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ pub use const_cmp::{
|
|||
#[doc(inline)]
|
||||
pub use scoped_ref::ScopedRef;
|
||||
|
||||
pub(crate) use misc::chain;
|
||||
#[doc(inline)]
|
||||
pub use misc::{
|
||||
BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter,
|
||||
|
@ -42,6 +41,7 @@ pub use misc::{
|
|||
os_str_strip_suffix, serialize_to_json_ascii, serialize_to_json_ascii_pretty,
|
||||
serialize_to_json_ascii_pretty_with_indent, slice_range, try_slice_range,
|
||||
};
|
||||
pub(crate) use misc::{InternedStrCompareAsStr, chain};
|
||||
|
||||
pub mod job_server;
|
||||
pub mod prefix_sum;
|
||||
|
|
|
@ -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) }
|
||||
})
|
||||
}
|
||||
|
||||
#[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> {
|
||||
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
|
||||
// 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;
|
||||
|
||||
#[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> {
|
||||
yosys_nextpnr_prjxray::built_in_job_kinds()
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
||||
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::{
|
||||
annotations::Annotation,
|
||||
build::{
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, JobAndDependencies,
|
||||
JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
||||
BaseJob, CommandParams, DynJobKind, GetJobPositionDependencies, GlobalParams,
|
||||
JobAndDependencies, JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind,
|
||||
JobKindAndDependencies, ToArgs, WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
|
@ -18,9 +18,10 @@ use crate::{
|
|||
module::{Module, NameId},
|
||||
prelude::JobParams,
|
||||
util::job_server::AcquiredJob,
|
||||
vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation},
|
||||
vendor::xilinx::{
|
||||
Device, XdcIOStandardAnnotation, XdcLocationAnnotation, XilinxAnnotation, XilinxArgs,
|
||||
},
|
||||
};
|
||||
use clap::ValueEnum;
|
||||
use eyre::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
|
@ -105,6 +106,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
|
|||
fn args_to_jobs(
|
||||
mut args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
args.dependencies
|
||||
.dependencies
|
||||
|
@ -113,7 +115,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
|
|||
.additional_args
|
||||
.verilog_dialect
|
||||
.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 base_job = dependencies.get_job::<BaseJob, _>();
|
||||
let verilog_job = dependencies.get_job::<VerilogJob, _>();
|
||||
|
@ -153,6 +155,7 @@ impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
|
|||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
params: &JobParams,
|
||||
_global_params: &GlobalParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||
|
@ -260,11 +263,12 @@ impl ExternalCommand for YosysNextpnrXraySynth {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<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;
|
||||
Ok(Self {
|
||||
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 {
|
||||
let s = s.as_ref();
|
||||
if !s.contains(|ch: char| !ch.is_alphanumeric() && ch != '_') {
|
||||
return s.into();
|
||||
}
|
||||
let mut retval = String::with_capacity(s.len().saturating_add(2));
|
||||
retval.push('"');
|
||||
for ch in s.chars() {
|
||||
|
@ -429,6 +436,7 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
let firrtl_export_options = args
|
||||
.dependencies
|
||||
|
@ -439,7 +447,7 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
|||
.args
|
||||
.args
|
||||
.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 base_job = dependencies.get_job::<BaseJob, _>();
|
||||
Ok(YosysNextpnrXrayWriteXdcFile {
|
||||
|
@ -474,23 +482,13 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
|||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
params: &JobParams,
|
||||
_global_params: &GlobalParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||
let mut xdc = String::new();
|
||||
job.write_xdc_contents(&mut xdc, params.main_module())?;
|
||||
// TODO: create actual .xdc from input module
|
||||
std::fs::write(
|
||||
job.xdc_file,
|
||||
r"# autogenerated
|
||||
set_property LOC G6 [get_ports led]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports led]
|
||||
set_property LOC E3 [get_ports clk]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports clk]
|
||||
set_property LOC C2 [get_ports rst]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports rst]
|
||||
",
|
||||
)?;
|
||||
std::fs::write(job.xdc_file, xdc)?;
|
||||
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)]
|
||||
pub struct YosysNextpnrXrayRunNextpnrArgs {
|
||||
#[command(flatten)]
|
||||
pub common: XilinxArgs,
|
||||
#[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)]
|
||||
pub nextpnr_xilinx_chipdb_dir: PathBuf,
|
||||
#[arg(long)]
|
||||
pub device: Device,
|
||||
#[arg(long, default_value_t = 0)]
|
||||
pub nextpnr_xilinx_seed: i32,
|
||||
}
|
||||
|
@ -639,12 +519,12 @@ pub struct YosysNextpnrXrayRunNextpnrArgs {
|
|||
impl ToArgs for YosysNextpnrXrayRunNextpnrArgs {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self {
|
||||
common,
|
||||
nextpnr_xilinx_chipdb_dir,
|
||||
device,
|
||||
nextpnr_xilinx_seed,
|
||||
} = self;
|
||||
common.to_args(args);
|
||||
args.write_long_option_eq("nextpnr-xilinx-chipdb-dir", nextpnr_xilinx_chipdb_dir);
|
||||
args.write_long_option_eq("device", device.as_str());
|
||||
args.write_display_arg(format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}"));
|
||||
}
|
||||
}
|
||||
|
@ -690,14 +570,15 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<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 {
|
||||
common,
|
||||
nextpnr_xilinx_chipdb_dir,
|
||||
device,
|
||||
nextpnr_xilinx_seed,
|
||||
} = args.additional_args;
|
||||
let base_job = dependencies.get_job::<BaseJob, _>();
|
||||
|
@ -707,7 +588,7 @@ impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
|
|||
let fasm_file = base_job.file_with_ext("fasm");
|
||||
Ok(Self {
|
||||
nextpnr_xilinx_chipdb_dir: nextpnr_xilinx_chipdb_dir.intern_deref(),
|
||||
device,
|
||||
device: common.require_device(base_job.platform(), global_params)?,
|
||||
nextpnr_xilinx_seed,
|
||||
xdc_file: write_xdc_file.xdc_file,
|
||||
xdc_file_name: write_xdc_file
|
||||
|
@ -842,11 +723,12 @@ impl ExternalCommand for YosysNextpnrXray {
|
|||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
global_params: &GlobalParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<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 base_job = dependencies.get_job::<BaseJob, _>();
|
||||
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()),
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
|
||||
[]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue