WIP adding yosys-nextpnr-xray xilinx fpga toolchain -- blinky works on arty a7 100t (except for inverted reset)
This commit is contained in:
parent
338ce7ed32
commit
74ad0e104e
11 changed files with 1250 additions and 164 deletions
|
@ -31,17 +31,20 @@ pub mod firrtl;
|
|||
pub mod formal;
|
||||
pub mod graph;
|
||||
pub mod registry;
|
||||
pub mod vendor;
|
||||
pub mod verilog;
|
||||
|
||||
pub(crate) const BUILT_IN_JOB_KINDS: &'static [fn() -> DynJobKind] = &[
|
||||
|| DynJobKind::new(BaseJobKind),
|
||||
|| DynJobKind::new(CreateOutputDirJobKind),
|
||||
|| DynJobKind::new(firrtl::FirrtlJobKind),
|
||||
|| DynJobKind::new(external::ExternalCommandJobKind::<verilog::UnadjustedVerilog>::new()),
|
||||
|| DynJobKind::new(verilog::VerilogJobKind),
|
||||
|| DynJobKind::new(formal::WriteSbyFileJobKind),
|
||||
|| DynJobKind::new(external::ExternalCommandJobKind::<formal::Formal>::new()),
|
||||
];
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
||||
[
|
||||
DynJobKind::new(BaseJobKind),
|
||||
DynJobKind::new(CreateOutputDirJobKind),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(firrtl::built_in_job_kinds())
|
||||
.chain(formal::built_in_job_kinds())
|
||||
.chain(vendor::built_in_job_kinds())
|
||||
.chain(verilog::built_in_job_kinds())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn intern_known_utf8_path_buf(v: PathBuf) -> Interned<str> {
|
||||
|
@ -99,7 +102,7 @@ impl JobItem {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum JobItemName {
|
||||
Path { path: Interned<str> },
|
||||
|
@ -171,12 +174,30 @@ pub trait WriteArgs:
|
|||
fn write_interned_arg(&mut self, arg: Interned<str>) {
|
||||
self.extend([arg]);
|
||||
}
|
||||
/// finds the first option that is `--{option_name}={value}` and returns `value`
|
||||
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&str>;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct WriteArgsWrapper<W>(W);
|
||||
pub struct ArgsWriter<T>(pub Vec<T>);
|
||||
|
||||
impl<'a, W> Extend<fmt::Arguments<'a>> for WriteArgsWrapper<W>
|
||||
impl<T> Default for ArgsWriter<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ArgsWriter<T> {
|
||||
fn get_long_option_eq_helper(&self, option_name: &str) -> Option<&str> {
|
||||
self.0.iter().find_map(|arg| {
|
||||
arg.as_ref()
|
||||
.strip_prefix("--")
|
||||
.and_then(|arg| arg.strip_prefix(option_name))
|
||||
.and_then(|arg| arg.strip_prefix("="))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W> Extend<fmt::Arguments<'a>> for ArgsWriter<W>
|
||||
where
|
||||
Self: Extend<String>,
|
||||
{
|
||||
|
@ -185,39 +206,47 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a str> for WriteArgsWrapper<Vec<Interned<str>>> {
|
||||
impl<'a> Extend<&'a str> for ArgsWriter<Interned<str>> {
|
||||
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
|
||||
self.extend(iter.into_iter().map(str::intern))
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<String> for WriteArgsWrapper<Vec<Interned<str>>> {
|
||||
impl Extend<String> for ArgsWriter<Interned<str>> {
|
||||
fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
|
||||
self.extend(iter.into_iter().map(str::intern_owned))
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<Interned<str>> for WriteArgsWrapper<Vec<String>> {
|
||||
impl Extend<Interned<str>> for ArgsWriter<String> {
|
||||
fn extend<T: IntoIterator<Item = Interned<str>>>(&mut self, iter: T) {
|
||||
self.extend(iter.into_iter().map(String::from))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a str> for WriteArgsWrapper<Vec<String>> {
|
||||
impl<'a> Extend<&'a str> for ArgsWriter<String> {
|
||||
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
|
||||
self.extend(iter.into_iter().map(String::from))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Extend<I> for WriteArgsWrapper<Vec<I>> {
|
||||
impl<I> Extend<I> for ArgsWriter<I> {
|
||||
fn extend<T: IntoIterator<Item = I>>(&mut self, iter: T) {
|
||||
self.0.extend(iter);
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteArgs for WriteArgsWrapper<Vec<String>> {}
|
||||
impl WriteArgs for ArgsWriter<String> {
|
||||
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&str> {
|
||||
self.get_long_option_eq_helper(option_name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteArgs for WriteArgsWrapper<Vec<Interned<str>>> {}
|
||||
impl WriteArgs for ArgsWriter<Interned<str>> {
|
||||
fn get_long_option_eq(&self, option_name: impl AsRef<str>) -> Option<&str> {
|
||||
self.get_long_option_eq_helper(option_name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToArgs: clap::Args + 'static + Send + Sync + Hash + Eq + fmt::Debug + Clone {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized));
|
||||
|
@ -225,12 +254,12 @@ pub trait ToArgs: clap::Args + 'static + Send + Sync + Hash + Eq + fmt::Debug +
|
|||
Intern::intern_owned(self.to_interned_args_vec())
|
||||
}
|
||||
fn to_interned_args_vec(&self) -> Vec<Interned<str>> {
|
||||
let mut retval = WriteArgsWrapper::default();
|
||||
let mut retval = ArgsWriter::default();
|
||||
self.to_args(&mut retval);
|
||||
retval.0
|
||||
}
|
||||
fn to_string_args(&self) -> Vec<String> {
|
||||
let mut retval = WriteArgsWrapper::default();
|
||||
let mut retval = ArgsWriter::default();
|
||||
self.to_args(&mut retval);
|
||||
retval.0
|
||||
}
|
||||
|
@ -642,6 +671,9 @@ pub trait JobKind: 'static + Send + Sync + Hash + Eq + fmt::Debug + Sized + Copy
|
|||
fn subcommand_hidden(self) -> bool {
|
||||
false
|
||||
}
|
||||
fn external_program(self) -> Option<Interned<external::ExternalProgram>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
trait DynJobKindTrait: 'static + Send + Sync + fmt::Debug {
|
||||
|
@ -1010,7 +1042,7 @@ impl<K: JobKind> DynJobArgsTrait for DynJobArgsInner<K> {
|
|||
}
|
||||
|
||||
fn to_args_extend_vec(&self, args: Vec<Interned<str>>) -> Vec<Interned<str>> {
|
||||
let mut writer = WriteArgsWrapper(args);
|
||||
let mut writer = ArgsWriter(args);
|
||||
self.0.args.to_args(&mut writer);
|
||||
writer.0
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
CommandParams, GetBaseJob, JobAndDependencies, JobAndKind, JobArgsAndDependencies,
|
||||
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs, JobParams, ToArgs,
|
||||
WriteArgs, intern_known_utf8_path_buf,
|
||||
ArgsWriter, CommandParams, GetBaseJob, JobAndDependencies, JobAndKind,
|
||||
JobArgsAndDependencies, JobDependencies, JobItem, JobItemName, JobKind, JobKindAndArgs,
|
||||
JobParams, ToArgs, WriteArgs, intern_known_utf8_path_buf,
|
||||
},
|
||||
intern::{Intern, Interned},
|
||||
util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8},
|
||||
|
@ -431,7 +431,7 @@ impl<T: ExternalCommand> ExternalCommandJobKind<T> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct ExternalCommandProgramPathValueParser<T: ExternalCommand>(PhantomData<T>);
|
||||
struct ExternalProgramPathValueParser(ExternalProgram);
|
||||
|
||||
fn parse_which_result(
|
||||
which_result: which::Result<PathBuf>,
|
||||
|
@ -460,9 +460,7 @@ fn parse_which_result(
|
|||
))
|
||||
}
|
||||
|
||||
impl<T: ExternalCommand> clap::builder::TypedValueParser
|
||||
for ExternalCommandProgramPathValueParser<T>
|
||||
{
|
||||
impl clap::builder::TypedValueParser for ExternalProgramPathValueParser {
|
||||
type Value = Interned<str>;
|
||||
|
||||
fn parse_ref(
|
||||
|
@ -471,10 +469,11 @@ impl<T: ExternalCommand> clap::builder::TypedValueParser
|
|||
arg: Option<&clap::Arg>,
|
||||
value: &OsStr,
|
||||
) -> clap::error::Result<Self::Value> {
|
||||
let program_path_arg_name = self.0.program_path_arg_name;
|
||||
OsStringValueParser::new()
|
||||
.try_map(|program_name| {
|
||||
.try_map(move |program_name| {
|
||||
parse_which_result(which::which(&program_name), program_name, || {
|
||||
T::program_path_arg_name().into()
|
||||
program_path_arg_name.into()
|
||||
})
|
||||
})
|
||||
.parse_ref(cmd, arg, value)
|
||||
|
@ -485,16 +484,8 @@ impl<T: ExternalCommand> clap::builder::TypedValueParser
|
|||
#[group(id = T::args_group_id())]
|
||||
#[non_exhaustive]
|
||||
pub struct ExternalCommandArgs<T: ExternalCommand> {
|
||||
#[arg(
|
||||
name = Interned::into_inner(T::program_path_arg_name()),
|
||||
long = T::program_path_arg_name(),
|
||||
value_name = T::program_path_arg_value_name(),
|
||||
env = T::program_path_env_var_name().map(Interned::into_inner),
|
||||
value_parser = ExternalCommandProgramPathValueParser::<T>(PhantomData),
|
||||
default_value = T::default_program_name(),
|
||||
value_hint = clap::ValueHint::CommandName,
|
||||
)]
|
||||
pub program_path: Interned<str>,
|
||||
#[command(flatten)]
|
||||
pub program_path: ExternalProgramPath<T::ExternalProgram>,
|
||||
#[arg(
|
||||
name = Interned::into_inner(T::run_even_if_cached_arg_name()),
|
||||
long = T::run_even_if_cached_arg_name(),
|
||||
|
@ -575,6 +566,15 @@ impl<T: ExternalCommand> ExternalCommandArgs<T> {
|
|||
pub fn with_resolved_program_path(
|
||||
program_path: Interned<str>,
|
||||
additional_args: T::AdditionalArgs,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
ExternalProgramPath::with_resolved_program_path(program_path),
|
||||
additional_args,
|
||||
)
|
||||
}
|
||||
pub fn new(
|
||||
program_path: ExternalProgramPath<T::ExternalProgram>,
|
||||
additional_args: T::AdditionalArgs,
|
||||
) -> Self {
|
||||
Self {
|
||||
program_path,
|
||||
|
@ -582,16 +582,12 @@ impl<T: ExternalCommand> ExternalCommandArgs<T> {
|
|||
additional_args,
|
||||
}
|
||||
}
|
||||
pub fn new(
|
||||
pub fn resolve_program_path(
|
||||
program_name: Option<&OsStr>,
|
||||
additional_args: T::AdditionalArgs,
|
||||
) -> Result<Self, ResolveProgramPathError> {
|
||||
Ok(Self::with_resolved_program_path(
|
||||
resolve_program_path(
|
||||
program_name,
|
||||
T::default_program_name(),
|
||||
T::program_path_env_var_name().as_ref().map(AsRef::as_ref),
|
||||
)?,
|
||||
Ok(Self::new(
|
||||
ExternalProgramPath::resolve_program_path(program_name)?,
|
||||
additional_args,
|
||||
))
|
||||
}
|
||||
|
@ -604,10 +600,7 @@ impl<T: ExternalCommand> ToArgs for ExternalCommandArgs<T> {
|
|||
run_even_if_cached,
|
||||
ref additional_args,
|
||||
} = *self;
|
||||
args.write_arg(format_args!(
|
||||
"--{}={program_path}",
|
||||
T::program_path_arg_name()
|
||||
));
|
||||
program_path.to_args(args);
|
||||
if run_even_if_cached {
|
||||
args.write_arg(format_args!("--{}", T::run_even_if_cached_arg_name()));
|
||||
}
|
||||
|
@ -626,13 +619,11 @@ struct ExternalCommandJobParams {
|
|||
impl ExternalCommandJobParams {
|
||||
fn new<T: ExternalCommand>(job: &ExternalCommandJob<T>) -> Self {
|
||||
let output_paths = T::output_paths(job);
|
||||
let mut command_line = ArgsWriter(vec![job.program_path]);
|
||||
T::command_line_args(job, &mut command_line);
|
||||
Self {
|
||||
command_params: CommandParams {
|
||||
command_line: Interned::from_iter(
|
||||
[job.program_path]
|
||||
.into_iter()
|
||||
.chain(T::command_line_args(job).iter().copied()),
|
||||
),
|
||||
command_line: Intern::intern_owned(command_line.0),
|
||||
current_dir: T::current_dir(job),
|
||||
},
|
||||
inputs: T::inputs(job),
|
||||
|
@ -758,33 +749,184 @@ impl<T: ExternalCommand> ExternalCommandJob<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Sized + Clone {
|
||||
type AdditionalArgs: ToArgs;
|
||||
type AdditionalJobData: 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Hash
|
||||
+ Eq
|
||||
+ fmt::Debug
|
||||
+ Serialize
|
||||
+ DeserializeOwned;
|
||||
type Dependencies: JobDependencies<JobsAndKinds: GetBaseJob>;
|
||||
fn dependencies() -> Self::Dependencies;
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
||||
)>;
|
||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]>;
|
||||
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]>;
|
||||
fn command_line_args(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]>;
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>>;
|
||||
fn job_kind_name() -> Interned<str>;
|
||||
fn args_group_id() -> clap::Id {
|
||||
Interned::into_inner(Self::job_kind_name()).into()
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExternalProgramPath<T: ExternalProgramTrait> {
|
||||
program_path: Interned<str>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> ExternalProgramPath<T> {
|
||||
pub fn with_resolved_program_path(program_path: Interned<str>) -> Self {
|
||||
Self {
|
||||
program_path,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn resolve_program_path(
|
||||
program_name: Option<&OsStr>,
|
||||
) -> Result<Self, ResolveProgramPathError> {
|
||||
let ExternalProgram {
|
||||
default_program_name,
|
||||
program_path_arg_name: _,
|
||||
program_path_arg_value_name: _,
|
||||
program_path_env_var_name,
|
||||
} = ExternalProgram::new::<T>();
|
||||
Ok(Self {
|
||||
program_path: resolve_program_path(
|
||||
program_name,
|
||||
default_program_name,
|
||||
program_path_env_var_name.as_ref().map(OsStr::new),
|
||||
)?,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
pub fn program_path(&self) -> Interned<str> {
|
||||
self.program_path
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> fmt::Debug for ExternalProgramPath<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
program_path,
|
||||
_phantom: _,
|
||||
} = self;
|
||||
write!(f, "ExternalProgramPath<{}>", std::any::type_name::<T>())?;
|
||||
f.debug_tuple("").field(program_path).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> clap::FromArgMatches for ExternalProgramPath<T> {
|
||||
fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
|
||||
let id = Interned::into_inner(ExternalProgram::new::<T>().program_path_arg_name);
|
||||
// don't remove argument so later instances of Self can use it too
|
||||
let program_path = *matches.get_one(id).expect("arg should always be present");
|
||||
Ok(Self {
|
||||
program_path,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> {
|
||||
*self = Self::from_arg_matches(matches)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> clap::Args for ExternalProgramPath<T> {
|
||||
fn augment_args(cmd: clap::Command) -> clap::Command {
|
||||
let external_program @ ExternalProgram {
|
||||
default_program_name,
|
||||
program_path_arg_name,
|
||||
program_path_arg_value_name,
|
||||
program_path_env_var_name,
|
||||
} = ExternalProgram::new::<T>();
|
||||
let arg = cmd
|
||||
.get_arguments()
|
||||
.find(|arg| *arg.get_id().as_str() == *program_path_arg_name);
|
||||
if let Some(arg) = arg {
|
||||
// don't insert duplicate arguments.
|
||||
// check that the previous argument actually matches this argument:
|
||||
assert!(!arg.is_required_set());
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Set));
|
||||
assert_eq!(arg.get_long(), Some(&*program_path_arg_name));
|
||||
assert_eq!(
|
||||
arg.get_value_names(),
|
||||
Some(&[clap::builder::Str::from(program_path_arg_value_name)][..])
|
||||
);
|
||||
assert_eq!(
|
||||
arg.get_env(),
|
||||
program_path_env_var_name.as_ref().map(OsStr::new)
|
||||
);
|
||||
assert_eq!(
|
||||
arg.get_default_values(),
|
||||
&[OsStr::new(&default_program_name)]
|
||||
);
|
||||
assert_eq!(arg.get_value_hint(), clap::ValueHint::CommandName);
|
||||
cmd
|
||||
} else {
|
||||
cmd.arg(
|
||||
clap::Arg::new(Interned::into_inner(program_path_arg_name))
|
||||
.required(false)
|
||||
.value_parser(ExternalProgramPathValueParser(external_program))
|
||||
.action(clap::ArgAction::Set)
|
||||
.long(program_path_arg_name)
|
||||
.value_name(program_path_arg_value_name)
|
||||
.env(program_path_env_var_name.map(Interned::into_inner))
|
||||
.default_value(default_program_name)
|
||||
.value_hint(clap::ValueHint::CommandName),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
||||
Self::augment_args(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> ToArgs for ExternalProgramPath<T> {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let ExternalProgram {
|
||||
program_path_arg_name,
|
||||
..
|
||||
} = ExternalProgram::new::<T>();
|
||||
let Self {
|
||||
program_path,
|
||||
_phantom: _,
|
||||
} = self;
|
||||
if args.get_long_option_eq(program_path_arg_name) != Some(&**program_path) {
|
||||
args.write_arg(format_args!("--{program_path_arg_name}={program_path}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct ExternalProgram {
|
||||
default_program_name: Interned<str>,
|
||||
program_path_arg_name: Interned<str>,
|
||||
program_path_arg_value_name: Interned<str>,
|
||||
program_path_env_var_name: Option<Interned<str>>,
|
||||
}
|
||||
|
||||
impl ExternalProgram {
|
||||
pub fn new<T: ExternalProgramTrait>() -> Self {
|
||||
Self {
|
||||
default_program_name: T::default_program_name(),
|
||||
program_path_arg_name: T::program_path_arg_name(),
|
||||
program_path_arg_value_name: T::program_path_arg_value_name(),
|
||||
program_path_env_var_name: T::program_path_env_var_name(),
|
||||
}
|
||||
}
|
||||
pub fn default_program_name(&self) -> Interned<str> {
|
||||
self.default_program_name
|
||||
}
|
||||
pub fn program_path_arg_name(&self) -> Interned<str> {
|
||||
self.program_path_arg_name
|
||||
}
|
||||
pub fn program_path_arg_value_name(&self) -> Interned<str> {
|
||||
self.program_path_arg_value_name
|
||||
}
|
||||
pub fn program_path_env_var_name(&self) -> Option<Interned<str>> {
|
||||
self.program_path_env_var_name
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> From<T> for ExternalProgram {
|
||||
fn from(_value: T) -> Self {
|
||||
Self::new::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ExternalProgramTrait> From<T> for Interned<ExternalProgram> {
|
||||
fn from(_value: T) -> Self {
|
||||
ExternalProgram::new::<T>().intern_sized()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalProgramTrait:
|
||||
'static + Send + Sync + Hash + Ord + fmt::Debug + Default + Copy
|
||||
{
|
||||
fn program_path_arg_name() -> Interned<str> {
|
||||
Self::default_program_name()
|
||||
}
|
||||
|
@ -799,6 +941,36 @@ pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Size
|
|||
.replace('-', "_"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalCommand: 'static + Send + Sync + Hash + Eq + fmt::Debug + Sized + Clone {
|
||||
type AdditionalArgs: ToArgs;
|
||||
type AdditionalJobData: 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Hash
|
||||
+ Eq
|
||||
+ fmt::Debug
|
||||
+ Serialize
|
||||
+ DeserializeOwned;
|
||||
type Dependencies: JobDependencies<JobsAndKinds: GetBaseJob>;
|
||||
type ExternalProgram: ExternalProgramTrait;
|
||||
fn dependencies() -> Self::Dependencies;
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
||||
)>;
|
||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]>;
|
||||
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]>;
|
||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W);
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>>;
|
||||
fn job_kind_name() -> Interned<str>;
|
||||
fn args_group_id() -> clap::Id {
|
||||
Interned::into_inner(Self::job_kind_name()).into()
|
||||
}
|
||||
fn run_even_if_cached_arg_name() -> Interned<str> {
|
||||
Intern::intern_owned(format!("{}-run-even-if-cached", Self::job_kind_name()))
|
||||
}
|
||||
|
@ -824,7 +996,11 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
kind,
|
||||
args:
|
||||
ExternalCommandArgs {
|
||||
program_path,
|
||||
program_path:
|
||||
ExternalProgramPath {
|
||||
program_path,
|
||||
_phantom: _,
|
||||
},
|
||||
run_even_if_cached,
|
||||
additional_args: _,
|
||||
},
|
||||
|
@ -868,7 +1044,12 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
params: &JobParams,
|
||||
acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(job.inputs()));
|
||||
assert!(
|
||||
inputs.iter().map(JobItem::name).eq(job.inputs()),
|
||||
"{}\ninputs:\n{inputs:?}\njob.inputs():\n{:?}",
|
||||
std::any::type_name::<Self>(),
|
||||
job.inputs(),
|
||||
);
|
||||
let CommandParams {
|
||||
command_line,
|
||||
current_dir,
|
||||
|
@ -913,4 +1094,8 @@ impl<T: ExternalCommand> JobKind for ExternalCommandJobKind<T> {
|
|||
fn subcommand_hidden(self) -> bool {
|
||||
T::subcommand_hidden()
|
||||
}
|
||||
|
||||
fn external_program(self) -> Option<Interned<ExternalProgram>> {
|
||||
Some(ExternalProgram::new::<T::ExternalProgram>().intern_sized())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
BaseJob, BaseJobKind, CommandParams, JobAndDependencies, JobArgsAndDependencies, JobItem,
|
||||
JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
BaseJob, BaseJobKind, CommandParams, DynJobKind, JobAndDependencies,
|
||||
JobArgsAndDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams,
|
||||
ToArgs, WriteArgs,
|
||||
},
|
||||
firrtl::{ExportOptions, FileBackend},
|
||||
intern::{Intern, Interned},
|
||||
|
@ -118,3 +119,7 @@ impl JobKind for FirrtlJobKind {
|
|||
}])
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
||||
[DynJobKind::new(FirrtlJobKind)]
|
||||
}
|
||||
|
|
|
@ -3,18 +3,21 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
CommandParams, GetBaseJob, JobAndDependencies, JobArgsAndDependencies, JobDependencies,
|
||||
JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
external::{ExternalCommand, ExternalCommandJob, ExternalCommandJobKind},
|
||||
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies,
|
||||
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs,
|
||||
WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
interned_known_utf8_method,
|
||||
verilog::{VerilogDialect, VerilogJobKind},
|
||||
verilog::{VerilogDialect, VerilogJob, VerilogJobKind},
|
||||
},
|
||||
intern::{Intern, Interned},
|
||||
module::NameId,
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::{Args, ValueEnum};
|
||||
use eyre::{Context, eyre};
|
||||
use eyre::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
|
@ -167,28 +170,12 @@ impl WriteSbyFileJob {
|
|||
\n\
|
||||
[script]\n",
|
||||
)?;
|
||||
for verilog_file in [main_verilog_file].into_iter().chain(additional_files) {
|
||||
if !(verilog_file.ends_with(".v") || verilog_file.ends_with(".sv")) {
|
||||
continue;
|
||||
}
|
||||
let verilog_file = match std::path::absolute(verilog_file)
|
||||
.and_then(|v| {
|
||||
v.into_os_string().into_string().map_err(|_| {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "path is not valid UTF-8")
|
||||
})
|
||||
})
|
||||
.wrap_err_with(|| format!("converting {verilog_file:?} to an absolute path failed"))
|
||||
{
|
||||
let all_verilog_files =
|
||||
match VerilogJob::all_verilog_files(*main_verilog_file, additional_files) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Ok(Err(e)),
|
||||
};
|
||||
if verilog_file.contains(|ch: char| {
|
||||
(ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"'
|
||||
}) {
|
||||
return Ok(Err(eyre!(
|
||||
"verilog file path contains characters that aren't permitted"
|
||||
)));
|
||||
}
|
||||
for verilog_file in all_verilog_files {
|
||||
writeln!(output, "read_verilog -sv -formal \"{verilog_file}\"")?;
|
||||
}
|
||||
let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id);
|
||||
|
@ -275,15 +262,10 @@ impl JobKind for WriteSbyFileJobKind {
|
|||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||
let [
|
||||
JobItem::DynamicPaths {
|
||||
paths: additional_files,
|
||||
..
|
||||
},
|
||||
] = inputs
|
||||
else {
|
||||
let [additional_files] = inputs else {
|
||||
unreachable!();
|
||||
};
|
||||
let additional_files = VerilogJob::unwrap_additional_files(additional_files);
|
||||
let mut contents = String::new();
|
||||
match job.write_sby(
|
||||
&mut contents,
|
||||
|
@ -339,6 +321,15 @@ impl fmt::Debug for Formal {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub struct Symbiyosys;
|
||||
|
||||
impl ExternalProgramTrait for Symbiyosys {
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"sby".intern()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Debug, Args)]
|
||||
pub struct FormalAdditionalArgs {}
|
||||
|
||||
|
@ -352,6 +343,7 @@ impl ExternalCommand for Formal {
|
|||
type AdditionalArgs = FormalAdditionalArgs;
|
||||
type AdditionalJobData = Formal;
|
||||
type Dependencies = JobKindAndDependencies<WriteSbyFileJobKind>;
|
||||
type ExternalProgram = Symbiyosys;
|
||||
|
||||
fn dependencies() -> Self::Dependencies {
|
||||
Default::default()
|
||||
|
@ -394,15 +386,11 @@ impl ExternalCommand for Formal {
|
|||
Interned::default()
|
||||
}
|
||||
|
||||
fn command_line_args(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> {
|
||||
[
|
||||
// "-j1".intern(), // sby seems not to respect job count in parallel mode
|
||||
"-f".intern(),
|
||||
job.additional_job_data().sby_file_name,
|
||||
]
|
||||
.into_iter()
|
||||
.chain(job.additional_job_data().write_sby_file.sby_extra_args())
|
||||
.collect()
|
||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
||||
// args.write_str_arg("-j1"); // sby seems not to respect job count in parallel mode
|
||||
args.write_str_arg("-f");
|
||||
args.write_interned_arg(job.additional_job_data().sby_file_name);
|
||||
args.write_interned_args(job.additional_job_data().write_sby_file.sby_extra_args());
|
||||
}
|
||||
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> {
|
||||
|
@ -412,8 +400,11 @@ impl ExternalCommand for Formal {
|
|||
fn job_kind_name() -> Interned<str> {
|
||||
"formal".intern()
|
||||
}
|
||||
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"sby".intern()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
||||
[
|
||||
DynJobKind::new(WriteSbyFileJobKind),
|
||||
DynJobKind::new(ExternalCommandJobKind::<Formal>::new()),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -726,8 +726,8 @@ impl JobGraph {
|
|||
let running_job_in_thread = RunningJobInThread {
|
||||
job_node_id,
|
||||
job: job.clone(),
|
||||
inputs: Result::from_iter(inputs.into_iter().map(|(input_name, input)| {
|
||||
input.into_inner().wrap_err_with(|| {
|
||||
inputs: Result::from_iter(job.inputs().iter().map(|input_name| {
|
||||
inputs.get(input_name).and_then(|v| v.get().cloned()).wrap_err_with(|| {
|
||||
eyre!("failed when trying to run job {name}: nothing provided the input item: {input_name:?}")
|
||||
})
|
||||
}))?,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{BUILT_IN_JOB_KINDS, DynJobKind, JobKind},
|
||||
build::{DynJobKind, JobKind, built_in_job_kinds},
|
||||
intern::Interned,
|
||||
};
|
||||
use std::{
|
||||
|
@ -164,8 +164,8 @@ impl Default for JobKindRegistry {
|
|||
let mut retval = Self {
|
||||
job_kinds: BTreeMap::new(),
|
||||
};
|
||||
for job_kind in BUILT_IN_JOB_KINDS {
|
||||
Self::register(&mut retval, job_kind());
|
||||
for job_kind in built_in_job_kinds() {
|
||||
Self::register(&mut retval, job_kind);
|
||||
}
|
||||
retval
|
||||
}
|
||||
|
|
8
crates/fayalite/src/build/vendor.rs
Normal file
8
crates/fayalite/src/build/vendor.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
pub mod xilinx;
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
||||
xilinx::built_in_job_kinds()
|
||||
}
|
8
crates/fayalite/src/build/vendor/xilinx.rs
vendored
Normal file
8
crates/fayalite/src/build/vendor/xilinx.rs
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
pub mod yosys_nextpnr_prjxray;
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
||||
yosys_nextpnr_prjxray::built_in_job_kinds()
|
||||
}
|
814
crates/fayalite/src/build/vendor/xilinx/yosys_nextpnr_prjxray.rs
vendored
Normal file
814
crates/fayalite/src/build/vendor/xilinx/yosys_nextpnr_prjxray.rs
vendored
Normal file
|
@ -0,0 +1,814 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
build::{
|
||||
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies,
|
||||
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, ToArgs, WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
interned_known_utf8_method, interned_known_utf8_path_buf_method,
|
||||
verilog::{VerilogDialect, VerilogJob, VerilogJobKind},
|
||||
},
|
||||
intern::{Intern, Interned},
|
||||
module::NameId,
|
||||
prelude::JobParams,
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::ValueEnum;
|
||||
use eyre::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
||||
pub struct YosysNextpnrXrayWriteYsFileJobKind;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
||||
pub struct YosysNextpnrXrayWriteYsFileArgs {}
|
||||
|
||||
impl ToArgs for YosysNextpnrXrayWriteYsFileArgs {
|
||||
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self {} = self;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct YosysNextpnrXrayWriteYsFile {
|
||||
main_verilog_file: Interned<str>,
|
||||
ys_file: Interned<str>,
|
||||
json_file: Interned<str>,
|
||||
json_file_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl YosysNextpnrXrayWriteYsFile {
|
||||
pub fn main_verilog_file(&self) -> Interned<str> {
|
||||
self.main_verilog_file
|
||||
}
|
||||
pub fn ys_file(&self) -> Interned<str> {
|
||||
self.ys_file
|
||||
}
|
||||
pub fn json_file(&self) -> Interned<str> {
|
||||
self.json_file
|
||||
}
|
||||
pub fn json_file_name(&self) -> Interned<str> {
|
||||
self.json_file_name
|
||||
}
|
||||
fn write_ys<W: ?Sized + fmt::Write>(
|
||||
&self,
|
||||
output: &mut W,
|
||||
additional_files: &[Interned<str>],
|
||||
main_module_name_id: NameId,
|
||||
) -> Result<eyre::Result<()>, fmt::Error> {
|
||||
let Self {
|
||||
main_verilog_file,
|
||||
ys_file: _,
|
||||
json_file: _,
|
||||
json_file_name,
|
||||
} = self;
|
||||
let all_verilog_files =
|
||||
match VerilogJob::all_verilog_files(*main_verilog_file, additional_files) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Ok(Err(e)),
|
||||
};
|
||||
for verilog_file in all_verilog_files {
|
||||
writeln!(output, "read_verilog -sv \"{verilog_file}\"")?;
|
||||
}
|
||||
let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id);
|
||||
writeln!(
|
||||
output,
|
||||
"synth_xilinx -flatten -abc9 -nobram -arch xc7 -top {circuit_name}"
|
||||
)?;
|
||||
writeln!(output, "write_json \"{json_file_name}\"")?;
|
||||
Ok(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl JobKind for YosysNextpnrXrayWriteYsFileJobKind {
|
||||
type Args = YosysNextpnrXrayWriteYsFileArgs;
|
||||
type Job = YosysNextpnrXrayWriteYsFile;
|
||||
type Dependencies = JobKindAndDependencies<VerilogJobKind>;
|
||||
|
||||
fn dependencies(self) -> Self::Dependencies {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn args_to_jobs(
|
||||
mut args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
args.dependencies
|
||||
.dependencies
|
||||
.args
|
||||
.args
|
||||
.additional_args
|
||||
.verilog_dialect
|
||||
.get_or_insert(VerilogDialect::Yosys);
|
||||
args.args_to_jobs_simple(params, |_kind, args, dependencies| {
|
||||
let YosysNextpnrXrayWriteYsFileArgs {} = args;
|
||||
let json_file = dependencies.base_job().file_with_ext("json");
|
||||
Ok(YosysNextpnrXrayWriteYsFile {
|
||||
main_verilog_file: dependencies.job.job.main_verilog_file(),
|
||||
ys_file: dependencies.base_job().file_with_ext("ys"),
|
||||
json_file,
|
||||
json_file_name: interned_known_utf8_method(json_file, |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inputs(self, _job: &Self::Job) -> Interned<[JobItemName]> {
|
||||
[JobItemName::DynamicPaths {
|
||||
source_job_name: VerilogJobKind.name(),
|
||||
}][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
||||
[JobItemName::Path { path: job.ys_file }][..].intern()
|
||||
}
|
||||
|
||||
fn name(self) -> Interned<str> {
|
||||
"yosys-nextpnr-xray-write-ys-file".intern()
|
||||
}
|
||||
|
||||
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
|
||||
None
|
||||
}
|
||||
|
||||
fn run(
|
||||
self,
|
||||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
params: &JobParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||
let [additional_files] = inputs else {
|
||||
unreachable!();
|
||||
};
|
||||
let additional_files = VerilogJob::unwrap_additional_files(additional_files);
|
||||
let mut contents = String::new();
|
||||
match job.write_ys(
|
||||
&mut contents,
|
||||
additional_files,
|
||||
params.main_module().name_id(),
|
||||
) {
|
||||
Ok(result) => result?,
|
||||
Err(fmt::Error) => unreachable!("writing to String can't fail"),
|
||||
}
|
||||
std::fs::write(job.ys_file, contents)
|
||||
.wrap_err_with(|| format!("writing {} failed", job.ys_file))?;
|
||||
Ok(vec![JobItem::Path { path: job.ys_file }])
|
||||
}
|
||||
|
||||
fn subcommand_hidden(self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
||||
pub struct YosysNextpnrXraySynthArgs {}
|
||||
|
||||
impl ToArgs for YosysNextpnrXraySynthArgs {
|
||||
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self {} = self;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
pub struct YosysNextpnrXraySynth {
|
||||
#[serde(flatten)]
|
||||
write_ys_file: YosysNextpnrXrayWriteYsFile,
|
||||
ys_file_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for YosysNextpnrXraySynth {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
write_ys_file:
|
||||
YosysNextpnrXrayWriteYsFile {
|
||||
main_verilog_file,
|
||||
ys_file,
|
||||
json_file,
|
||||
json_file_name,
|
||||
},
|
||||
ys_file_name,
|
||||
} = self;
|
||||
f.debug_struct("YosysNextpnrXraySynth")
|
||||
.field("main_verilog_file", main_verilog_file)
|
||||
.field("ys_file", ys_file)
|
||||
.field("ys_file_name", ys_file_name)
|
||||
.field("json_file", json_file)
|
||||
.field("json_file_name", json_file_name)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl YosysNextpnrXraySynth {
|
||||
pub fn main_verilog_file(&self) -> Interned<str> {
|
||||
self.write_ys_file.main_verilog_file()
|
||||
}
|
||||
pub fn ys_file(&self) -> Interned<str> {
|
||||
self.write_ys_file.ys_file()
|
||||
}
|
||||
pub fn ys_file_name(&self) -> Interned<str> {
|
||||
self.ys_file_name
|
||||
}
|
||||
pub fn json_file(&self) -> Interned<str> {
|
||||
self.write_ys_file.json_file()
|
||||
}
|
||||
pub fn json_file_name(&self) -> Interned<str> {
|
||||
self.write_ys_file.json_file_name()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub struct Yosys;
|
||||
|
||||
impl ExternalProgramTrait for Yosys {
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"yosys".intern()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCommand for YosysNextpnrXraySynth {
|
||||
type AdditionalArgs = YosysNextpnrXraySynthArgs;
|
||||
type AdditionalJobData = Self;
|
||||
type Dependencies = JobKindAndDependencies<YosysNextpnrXrayWriteYsFileJobKind>;
|
||||
type ExternalProgram = Yosys;
|
||||
|
||||
fn dependencies() -> Self::Dependencies {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
||||
)> {
|
||||
args.args_to_jobs_external_simple(params, |args, dependencies| {
|
||||
let YosysNextpnrXraySynthArgs {} = args.additional_args;
|
||||
Ok(Self {
|
||||
write_ys_file: dependencies.job.job.clone(),
|
||||
ys_file_name: interned_known_utf8_method(dependencies.job.job.ys_file(), |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
|
||||
[
|
||||
JobItemName::Path {
|
||||
path: job.additional_job_data().ys_file(),
|
||||
},
|
||||
JobItemName::Path {
|
||||
path: job.additional_job_data().main_verilog_file(),
|
||||
},
|
||||
JobItemName::DynamicPaths {
|
||||
source_job_name: VerilogJobKind.name(),
|
||||
},
|
||||
][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> {
|
||||
[job.additional_job_data().json_file()][..].intern()
|
||||
}
|
||||
|
||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
||||
args.write_str_arg("-s");
|
||||
args.write_interned_arg(job.additional_job_data().ys_file_name());
|
||||
}
|
||||
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> {
|
||||
Some(job.output_dir())
|
||||
}
|
||||
|
||||
fn job_kind_name() -> Interned<str> {
|
||||
"yosys-nextpnr-xray-synth".intern()
|
||||
}
|
||||
|
||||
fn subcommand_hidden() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
||||
pub struct YosysNextpnrXrayWriteXdcFileJobKind;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
||||
pub struct YosysNextpnrXrayWriteXdcFileArgs {}
|
||||
|
||||
impl ToArgs for YosysNextpnrXrayWriteXdcFileArgs {
|
||||
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self {} = self;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct YosysNextpnrXrayWriteXdcFile {
|
||||
output_dir: Interned<str>,
|
||||
xdc_file: Interned<str>,
|
||||
}
|
||||
|
||||
impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
||||
type Args = YosysNextpnrXrayWriteXdcFileArgs;
|
||||
type Job = YosysNextpnrXrayWriteXdcFile;
|
||||
type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrXraySynth>>;
|
||||
|
||||
fn dependencies(self) -> Self::Dependencies {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
args.args_to_jobs_simple(params, |_kind, args, dependencies| {
|
||||
let YosysNextpnrXrayWriteXdcFileArgs {} = args;
|
||||
Ok(YosysNextpnrXrayWriteXdcFile {
|
||||
output_dir: dependencies.base_job().output_dir(),
|
||||
xdc_file: dependencies.base_job().file_with_ext("xdc"),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
||||
[JobItemName::Path {
|
||||
path: job.output_dir,
|
||||
}][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
|
||||
[JobItemName::Path { path: job.xdc_file }][..].intern()
|
||||
}
|
||||
|
||||
fn name(self) -> Interned<str> {
|
||||
"yosys-nextpnr-xray-write-xdc-file".intern()
|
||||
}
|
||||
|
||||
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
|
||||
None
|
||||
}
|
||||
|
||||
fn run(
|
||||
self,
|
||||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
_params: &JobParams,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> eyre::Result<Vec<JobItem>> {
|
||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||
// TODO: create actual .xdc from input module
|
||||
std::fs::write(
|
||||
job.xdc_file,
|
||||
r"# autogenerated
|
||||
set_property LOC G6 [get_ports led]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports led]
|
||||
set_property LOC E3 [get_ports clk]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports clk]
|
||||
set_property LOC C2 [get_ports rst]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports rst]
|
||||
",
|
||||
)?;
|
||||
Ok(vec![JobItem::Path { path: job.xdc_file }])
|
||||
}
|
||||
|
||||
fn subcommand_hidden(self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub struct NextpnrXilinx;
|
||||
|
||||
impl ExternalProgramTrait for NextpnrXilinx {
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"nextpnr-xilinx".intern()
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
#[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)]
|
||||
pub nextpnr_xilinx_chipdb_dir: String,
|
||||
#[arg(long)]
|
||||
pub device: Device,
|
||||
#[arg(long, default_value_t = 0)]
|
||||
pub nextpnr_xilinx_seed: i32,
|
||||
}
|
||||
|
||||
impl ToArgs for YosysNextpnrXrayRunNextpnrArgs {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self {
|
||||
nextpnr_xilinx_chipdb_dir,
|
||||
device,
|
||||
nextpnr_xilinx_seed,
|
||||
} = self;
|
||||
args.write_args([
|
||||
format_args!("--nextpnr-xilinx-chipdb-dir={nextpnr_xilinx_chipdb_dir}"),
|
||||
format_args!("--device={device}"),
|
||||
format_args!("--nextpnr-xilinx-seed={nextpnr_xilinx_seed}"),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct YosysNextpnrXrayRunNextpnr {
|
||||
nextpnr_xilinx_chipdb_dir: Interned<str>,
|
||||
device: Device,
|
||||
nextpnr_xilinx_seed: i32,
|
||||
xdc_file: Interned<str>,
|
||||
xdc_file_name: Interned<str>,
|
||||
json_file: Interned<str>,
|
||||
json_file_name: Interned<str>,
|
||||
routed_json_file: Interned<str>,
|
||||
routed_json_file_name: Interned<str>,
|
||||
fasm_file: Interned<str>,
|
||||
fasm_file_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl YosysNextpnrXrayRunNextpnr {
|
||||
fn chipdb_file(&self) -> Interned<str> {
|
||||
interned_known_utf8_path_buf_method(self.nextpnr_xilinx_chipdb_dir, |chipdb_dir| {
|
||||
let mut retval = chipdb_dir.join(self.device.xray_device());
|
||||
retval.set_extension("bin");
|
||||
retval
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCommand for YosysNextpnrXrayRunNextpnr {
|
||||
type AdditionalArgs = YosysNextpnrXrayRunNextpnrArgs;
|
||||
type AdditionalJobData = Self;
|
||||
type Dependencies = JobKindAndDependencies<YosysNextpnrXrayWriteXdcFileJobKind>;
|
||||
type ExternalProgram = NextpnrXilinx;
|
||||
|
||||
fn dependencies() -> Self::Dependencies {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
||||
)> {
|
||||
args.args_to_jobs_external_simple(params, |args, dependencies| {
|
||||
let YosysNextpnrXrayRunNextpnrArgs {
|
||||
nextpnr_xilinx_chipdb_dir,
|
||||
device,
|
||||
nextpnr_xilinx_seed,
|
||||
} = args.additional_args;
|
||||
let xdc_file = dependencies.job.job.xdc_file;
|
||||
let routed_json_file = dependencies.base_job().file_with_ext("routed.json");
|
||||
let fasm_file = dependencies.base_job().file_with_ext("fasm");
|
||||
Ok(Self {
|
||||
nextpnr_xilinx_chipdb_dir: str::intern_owned(nextpnr_xilinx_chipdb_dir),
|
||||
device,
|
||||
nextpnr_xilinx_seed,
|
||||
xdc_file,
|
||||
xdc_file_name: interned_known_utf8_method(xdc_file, |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
json_file: dependencies
|
||||
.dependencies
|
||||
.job
|
||||
.job
|
||||
.additional_job_data()
|
||||
.json_file(),
|
||||
json_file_name: dependencies
|
||||
.dependencies
|
||||
.job
|
||||
.job
|
||||
.additional_job_data()
|
||||
.json_file_name(),
|
||||
routed_json_file,
|
||||
routed_json_file_name: interned_known_utf8_method(routed_json_file, |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
fasm_file,
|
||||
fasm_file_name: interned_known_utf8_method(fasm_file, |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
|
||||
[
|
||||
JobItemName::Path {
|
||||
path: job.additional_job_data().json_file,
|
||||
},
|
||||
JobItemName::Path {
|
||||
path: job.additional_job_data().xdc_file,
|
||||
},
|
||||
][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> {
|
||||
[
|
||||
job.additional_job_data().routed_json_file,
|
||||
job.additional_job_data().fasm_file,
|
||||
][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
||||
let job_data @ YosysNextpnrXrayRunNextpnr {
|
||||
nextpnr_xilinx_seed,
|
||||
xdc_file_name,
|
||||
json_file_name,
|
||||
routed_json_file_name,
|
||||
fasm_file_name,
|
||||
..
|
||||
} = job.additional_job_data();
|
||||
args.write_args([
|
||||
format_args!("--chipdb={}", job_data.chipdb_file()),
|
||||
format_args!("--xdc={xdc_file_name}"),
|
||||
format_args!("--json={json_file_name}"),
|
||||
format_args!("--write={routed_json_file_name}"),
|
||||
format_args!("--fasm={fasm_file_name}"),
|
||||
format_args!("--seed={nextpnr_xilinx_seed}"),
|
||||
]);
|
||||
}
|
||||
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> {
|
||||
Some(job.output_dir())
|
||||
}
|
||||
|
||||
fn job_kind_name() -> Interned<str> {
|
||||
"yosys-nextpnr-xray-run-nextpnr".intern()
|
||||
}
|
||||
|
||||
fn subcommand_hidden() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub struct Xcfasm;
|
||||
|
||||
impl ExternalProgramTrait for Xcfasm {
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"xcfasm".intern()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
||||
pub struct YosysNextpnrXrayArgs {
|
||||
#[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)]
|
||||
pub prjxray_db_dir: String,
|
||||
}
|
||||
|
||||
impl ToArgs for YosysNextpnrXrayArgs {
|
||||
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||
let Self { prjxray_db_dir } = self;
|
||||
args.write_arg(format_args!("--prjxray-db-dir={prjxray_db_dir}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct YosysNextpnrXray {
|
||||
prjxray_db_dir: Interned<str>,
|
||||
device: Device,
|
||||
fasm_file: Interned<str>,
|
||||
fasm_file_name: Interned<str>,
|
||||
frames_file: Interned<str>,
|
||||
frames_file_name: Interned<str>,
|
||||
bit_file: Interned<str>,
|
||||
bit_file_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl YosysNextpnrXray {
|
||||
fn db_root(&self) -> Interned<str> {
|
||||
interned_known_utf8_path_buf_method(self.prjxray_db_dir, |prjxray_db_dir| {
|
||||
prjxray_db_dir.join(self.device.xray_family())
|
||||
})
|
||||
}
|
||||
fn part_file(&self) -> Interned<str> {
|
||||
interned_known_utf8_path_buf_method(self.prjxray_db_dir, |prjxray_db_dir| {
|
||||
let mut retval = prjxray_db_dir.join(self.device.xray_family());
|
||||
retval.push(self.device.xray_part());
|
||||
retval.push("part.yaml");
|
||||
retval
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCommand for YosysNextpnrXray {
|
||||
type AdditionalArgs = YosysNextpnrXrayArgs;
|
||||
type AdditionalJobData = Self;
|
||||
type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrXrayRunNextpnr>>;
|
||||
type ExternalProgram = Xcfasm;
|
||||
|
||||
fn dependencies() -> Self::Dependencies {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn args_to_jobs(
|
||||
args: JobArgsAndDependencies<ExternalCommandJobKind<Self>>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<(
|
||||
Self::AdditionalJobData,
|
||||
<Self::Dependencies as JobDependencies>::JobsAndKinds,
|
||||
)> {
|
||||
args.args_to_jobs_external_simple(params, |args, dependencies| {
|
||||
let YosysNextpnrXrayArgs { prjxray_db_dir } = args.additional_args;
|
||||
let frames_file = dependencies.base_job().file_with_ext("frames");
|
||||
let bit_file = dependencies.base_job().file_with_ext("bit");
|
||||
Ok(Self {
|
||||
prjxray_db_dir: str::intern_owned(prjxray_db_dir),
|
||||
device: dependencies.job.job.additional_job_data().device,
|
||||
fasm_file: dependencies.job.job.additional_job_data().fasm_file,
|
||||
fasm_file_name: dependencies.job.job.additional_job_data().fasm_file_name,
|
||||
frames_file,
|
||||
frames_file_name: interned_known_utf8_method(frames_file, |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
bit_file,
|
||||
bit_file_name: interned_known_utf8_method(bit_file, |v| {
|
||||
v.file_name().expect("known to have file name")
|
||||
}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
|
||||
[JobItemName::Path {
|
||||
path: job.additional_job_data().fasm_file,
|
||||
}][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> {
|
||||
[
|
||||
job.additional_job_data().frames_file,
|
||||
job.additional_job_data().bit_file,
|
||||
][..]
|
||||
.intern()
|
||||
}
|
||||
|
||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
||||
let job_data @ YosysNextpnrXray {
|
||||
device,
|
||||
fasm_file_name,
|
||||
frames_file_name,
|
||||
bit_file_name,
|
||||
..
|
||||
} = job.additional_job_data();
|
||||
args.write_args([
|
||||
format_args!("--sparse"),
|
||||
format_args!("--db-root={}", job_data.db_root()),
|
||||
format_args!("--part={}", device.xray_part()),
|
||||
format_args!("--part_file={}", job_data.part_file()),
|
||||
format_args!("--fn_in={fasm_file_name}"),
|
||||
format_args!("--frm_out={frames_file_name}"),
|
||||
format_args!("--bit_out={bit_file_name}"),
|
||||
]);
|
||||
}
|
||||
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> {
|
||||
Some(job.output_dir())
|
||||
}
|
||||
|
||||
fn job_kind_name() -> Interned<str> {
|
||||
"yosys-nextpnr-xray".intern()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
||||
[
|
||||
DynJobKind::new(YosysNextpnrXrayWriteYsFileJobKind),
|
||||
DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrXraySynth>::new()),
|
||||
DynJobKind::new(YosysNextpnrXrayWriteXdcFileJobKind),
|
||||
DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrXrayRunNextpnr>::new()),
|
||||
DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrXray>::new()),
|
||||
]
|
||||
}
|
|
@ -3,9 +3,12 @@
|
|||
|
||||
use crate::{
|
||||
build::{
|
||||
CommandParams, GetBaseJob, JobAndDependencies, JobArgsAndDependencies, JobDependencies,
|
||||
JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs, WriteArgs,
|
||||
external::{ExternalCommand, ExternalCommandJob, ExternalCommandJobKind},
|
||||
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies,
|
||||
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, JobParams, ToArgs,
|
||||
WriteArgs,
|
||||
external::{
|
||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
firrtl::FirrtlJobKind,
|
||||
interned_known_utf8_method, interned_known_utf8_path_buf_method,
|
||||
},
|
||||
|
@ -13,7 +16,7 @@ use crate::{
|
|||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::Args;
|
||||
use eyre::bail;
|
||||
use eyre::{Context, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, mem};
|
||||
|
||||
|
@ -96,6 +99,15 @@ impl ToArgs for UnadjustedVerilogArgs {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub struct Firtool;
|
||||
|
||||
impl ExternalProgramTrait for Firtool {
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"firtool".intern()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize)]
|
||||
pub struct UnadjustedVerilog {
|
||||
firrtl_file: Interned<str>,
|
||||
|
@ -129,6 +141,7 @@ impl ExternalCommand for UnadjustedVerilog {
|
|||
type AdditionalArgs = UnadjustedVerilogArgs;
|
||||
type AdditionalJobData = UnadjustedVerilog;
|
||||
type Dependencies = JobKindAndDependencies<FirrtlJobKind>;
|
||||
type ExternalProgram = Firtool;
|
||||
|
||||
fn dependencies() -> Self::Dependencies {
|
||||
Default::default()
|
||||
|
@ -184,7 +197,7 @@ impl ExternalCommand for UnadjustedVerilog {
|
|||
[job.additional_job_data().unadjusted_verilog_file][..].intern()
|
||||
}
|
||||
|
||||
fn command_line_args(job: &ExternalCommandJob<Self>) -> Interned<[Interned<str>]> {
|
||||
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
|
||||
let UnadjustedVerilog {
|
||||
firrtl_file: _,
|
||||
firrtl_file_name,
|
||||
|
@ -194,26 +207,16 @@ impl ExternalCommand for UnadjustedVerilog {
|
|||
verilog_dialect,
|
||||
verilog_debug,
|
||||
} = *job.additional_job_data();
|
||||
let mut retval = vec![
|
||||
firrtl_file_name,
|
||||
"-o".intern(),
|
||||
unadjusted_verilog_file_name,
|
||||
];
|
||||
args.write_interned_arg(firrtl_file_name);
|
||||
args.write_str_arg("-o");
|
||||
args.write_interned_arg(unadjusted_verilog_file_name);
|
||||
if verilog_debug {
|
||||
retval.push("-g".intern());
|
||||
retval.push("--preserve-values=all".intern());
|
||||
args.write_str_args(["-g", "--preserve-values=all"]);
|
||||
}
|
||||
if let Some(dialect) = verilog_dialect {
|
||||
retval.extend(
|
||||
dialect
|
||||
.firtool_extra_args()
|
||||
.iter()
|
||||
.copied()
|
||||
.map(str::intern),
|
||||
);
|
||||
args.write_str_args(dialect.firtool_extra_args().iter().copied());
|
||||
}
|
||||
retval.extend_from_slice(&firtool_extra_args);
|
||||
Intern::intern_owned(retval)
|
||||
args.write_interned_args(firtool_extra_args);
|
||||
}
|
||||
|
||||
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<str>> {
|
||||
|
@ -224,10 +227,6 @@ impl ExternalCommand for UnadjustedVerilog {
|
|||
"unadjusted-verilog".intern()
|
||||
}
|
||||
|
||||
fn default_program_name() -> Interned<str> {
|
||||
"firtool".intern()
|
||||
}
|
||||
|
||||
fn subcommand_hidden() -> bool {
|
||||
true
|
||||
}
|
||||
|
@ -267,6 +266,43 @@ impl VerilogJob {
|
|||
pub fn main_verilog_file(&self) -> Interned<str> {
|
||||
self.main_verilog_file
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn unwrap_additional_files(additional_files: &JobItem) -> &[Interned<str>] {
|
||||
match additional_files {
|
||||
JobItem::DynamicPaths {
|
||||
paths,
|
||||
source_job_name,
|
||||
} if *source_job_name == VerilogJobKind.name() => paths,
|
||||
v => panic!("expected VerilogJob's additional files JobItem: {v:?}"),
|
||||
}
|
||||
}
|
||||
pub fn all_verilog_files(
|
||||
main_verilog_file: Interned<str>,
|
||||
additional_files: &[Interned<str>],
|
||||
) -> eyre::Result<Interned<[Interned<str>]>> {
|
||||
let mut retval = Vec::with_capacity(additional_files.len().saturating_add(1));
|
||||
for verilog_file in [main_verilog_file].iter().chain(additional_files) {
|
||||
if !(verilog_file.ends_with(".v") || verilog_file.ends_with(".sv")) {
|
||||
continue;
|
||||
}
|
||||
let verilog_file = std::path::absolute(verilog_file)
|
||||
.and_then(|v| {
|
||||
v.into_os_string().into_string().map_err(|_| {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "path is not valid UTF-8")
|
||||
})
|
||||
})
|
||||
.wrap_err_with(|| {
|
||||
format!("converting {verilog_file:?} to an absolute path failed")
|
||||
})?;
|
||||
if verilog_file.contains(|ch: char| {
|
||||
(ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"'
|
||||
}) {
|
||||
bail!("verilog file path contains characters that aren't permitted");
|
||||
}
|
||||
retval.push(str::intern_owned(verilog_file));
|
||||
}
|
||||
Ok(Intern::intern_owned(retval))
|
||||
}
|
||||
}
|
||||
|
||||
impl JobKind for VerilogJobKind {
|
||||
|
@ -371,3 +407,10 @@ impl JobKind for VerilogJobKind {
|
|||
])
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
|
||||
[
|
||||
DynJobKind::new(ExternalCommandJobKind::<UnadjustedVerilog>::new()),
|
||||
DynJobKind::new(VerilogJobKind),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ fn make_assert_formal_args(
|
|||
let dependencies = JobArgsAndDependencies { args, dependencies };
|
||||
let args = JobKindAndArgs {
|
||||
kind: ExternalCommandJobKind::new(),
|
||||
args: ExternalCommandArgs::new(
|
||||
args: ExternalCommandArgs::resolve_program_path(
|
||||
None,
|
||||
UnadjustedVerilogArgs {
|
||||
firtool_extra_args: vec![],
|
||||
|
@ -153,7 +153,7 @@ fn make_assert_formal_args(
|
|||
let dependencies = JobArgsAndDependencies { args, dependencies };
|
||||
let args = JobKindAndArgs {
|
||||
kind: ExternalCommandJobKind::new(),
|
||||
args: ExternalCommandArgs::new(None, FormalAdditionalArgs {})?,
|
||||
args: ExternalCommandArgs::resolve_program_path(None, FormalAdditionalArgs {})?,
|
||||
};
|
||||
Ok(JobArgsAndDependencies { args, dependencies })
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue