WIP adding annotations for generating the .xdc file for yosys-nextpnr-prjxray
All checks were successful
/ test (pull_request) Successful in 4m26s
All checks were successful
/ test (pull_request) Successful in 4m26s
This commit is contained in:
parent
faf1e5113c
commit
c2df30f844
8 changed files with 667 additions and 32 deletions
|
@ -147,7 +147,7 @@ macro_rules! make_annotation_enum {
|
|||
(
|
||||
$(#[$meta:meta])*
|
||||
$vis:vis enum $Annotation:ident {
|
||||
$($Variant:ident($T:ident),)*
|
||||
$($Variant:ident($T:ty),)*
|
||||
}
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
|
@ -199,6 +199,8 @@ make_annotation_enum! {
|
|||
BlackBoxPath(BlackBoxPathAnnotation),
|
||||
DocString(DocStringAnnotation),
|
||||
CustomFirrtl(CustomFirrtlAnnotation),
|
||||
XdcLocation(crate::build::vendor::xilinx::XdcLocationAnnotation),
|
||||
XdcIOStandard(crate::build::vendor::xilinx::XdcIOStandardAnnotation),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
crates/fayalite/src/build/vendor/xilinx.rs
vendored
12
crates/fayalite/src/build/vendor/xilinx.rs
vendored
|
@ -1,8 +1,20 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::intern::Interned;
|
||||
|
||||
pub mod yosys_nextpnr_prjxray;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct XdcIOStandardAnnotation {
|
||||
pub value: Interned<str>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct XdcLocationAnnotation {
|
||||
pub location: Interned<str>,
|
||||
}
|
||||
|
||||
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
||||
yosys_nextpnr_prjxray::built_in_job_kinds()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
annotations::Annotation,
|
||||
build::{
|
||||
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies,
|
||||
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, ToArgs, WriteArgs,
|
||||
|
@ -9,17 +10,20 @@ use crate::{
|
|||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||
},
|
||||
interned_known_utf8_method, interned_known_utf8_path_buf_method,
|
||||
vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation},
|
||||
verilog::{VerilogDialect, VerilogJob, VerilogJobKind},
|
||||
},
|
||||
bundle::Bundle,
|
||||
firrtl::{ScalarizedModuleABI, ScalarizedModuleABIAnnotations, ScalarizedModuleABIPort},
|
||||
intern::{Intern, Interned},
|
||||
module::NameId,
|
||||
module::{Module, NameId},
|
||||
prelude::JobParams,
|
||||
util::job_server::AcquiredJob,
|
||||
};
|
||||
use clap::ValueEnum;
|
||||
use eyre::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::{fmt, ops::ControlFlow};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
||||
pub struct YosysNextpnrXrayWriteYsFileJobKind;
|
||||
|
@ -312,10 +316,90 @@ impl ToArgs for YosysNextpnrXrayWriteXdcFileArgs {
|
|||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct YosysNextpnrXrayWriteXdcFile {
|
||||
firrtl_export_options: crate::firrtl::ExportOptions,
|
||||
output_dir: Interned<str>,
|
||||
xdc_file: Interned<str>,
|
||||
}
|
||||
|
||||
struct WriteXdcContentsError(eyre::Report);
|
||||
|
||||
impl From<eyre::Report> for WriteXdcContentsError {
|
||||
fn from(v: eyre::Report) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for WriteXdcContentsError {
|
||||
fn from(_v: fmt::Error) -> Self {
|
||||
unreachable!("String write can't fail")
|
||||
}
|
||||
}
|
||||
|
||||
fn tcl_escape(s: impl AsRef<str>) -> String {
|
||||
let s = s.as_ref();
|
||||
let mut retval = String::with_capacity(s.len().saturating_add(2));
|
||||
retval.push('"');
|
||||
for ch in s.chars() {
|
||||
if let '$' | '\\' | '[' = ch {
|
||||
retval.push('\\');
|
||||
}
|
||||
retval.push(ch);
|
||||
}
|
||||
retval.push('"');
|
||||
retval
|
||||
}
|
||||
|
||||
impl YosysNextpnrXrayWriteXdcFile {
|
||||
fn write_xdc_contents_for_port_and_annotations(
|
||||
&self,
|
||||
output: &mut impl fmt::Write,
|
||||
port: &ScalarizedModuleABIPort,
|
||||
annotations: ScalarizedModuleABIAnnotations<'_>,
|
||||
) -> Result<(), WriteXdcContentsError> {
|
||||
for annotation in annotations {
|
||||
match annotation.annotation() {
|
||||
Annotation::DontTouch(_)
|
||||
| Annotation::SVAttribute(_)
|
||||
| Annotation::BlackBoxInline(_)
|
||||
| Annotation::BlackBoxPath(_)
|
||||
| Annotation::DocString(_)
|
||||
| Annotation::CustomFirrtl(_) => {}
|
||||
Annotation::XdcLocation(XdcLocationAnnotation { location }) => writeln!(
|
||||
output,
|
||||
"set_property LOC {} [get_ports {}]",
|
||||
tcl_escape(location),
|
||||
tcl_escape(port.scalarized_name())
|
||||
)?,
|
||||
Annotation::XdcIOStandard(XdcIOStandardAnnotation { value }) => writeln!(
|
||||
output,
|
||||
"set_property IOSTANDARD {} [get_ports {}]",
|
||||
tcl_escape(value),
|
||||
tcl_escape(port.scalarized_name())
|
||||
)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn write_xdc_contents(
|
||||
&self,
|
||||
output: &mut String,
|
||||
top_module: &Module<Bundle>,
|
||||
) -> eyre::Result<()> {
|
||||
let scalarized_module_abi =
|
||||
ScalarizedModuleABI::new(top_module, self.firrtl_export_options)
|
||||
.map_err(eyre::Report::from)?;
|
||||
match scalarized_module_abi.for_each_port_and_annotations(|port, annotations| {
|
||||
match self.write_xdc_contents_for_port_and_annotations(output, port, annotations) {
|
||||
Ok(()) => ControlFlow::Continue(()),
|
||||
Err(e) => ControlFlow::Break(e),
|
||||
}
|
||||
}) {
|
||||
ControlFlow::Continue(()) => Ok(()),
|
||||
ControlFlow::Break(e) => Err(e.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
||||
type Args = YosysNextpnrXrayWriteXdcFileArgs;
|
||||
type Job = YosysNextpnrXrayWriteXdcFile;
|
||||
|
@ -329,9 +413,19 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
|||
args: JobArgsAndDependencies<Self>,
|
||||
params: &JobParams,
|
||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
||||
let firrtl_export_options = args
|
||||
.dependencies
|
||||
.dependencies
|
||||
.dependencies
|
||||
.dependencies
|
||||
.dependencies
|
||||
.args
|
||||
.args
|
||||
.export_options;
|
||||
args.args_to_jobs_simple(params, |_kind, args, dependencies| {
|
||||
let YosysNextpnrXrayWriteXdcFileArgs {} = args;
|
||||
Ok(YosysNextpnrXrayWriteXdcFile {
|
||||
firrtl_export_options,
|
||||
output_dir: dependencies.base_job().output_dir(),
|
||||
xdc_file: dependencies.base_job().file_with_ext("xdc"),
|
||||
})
|
||||
|
@ -361,10 +455,12 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
|||
self,
|
||||
job: &Self::Job,
|
||||
inputs: &[JobItem],
|
||||
_params: &JobParams,
|
||||
params: &JobParams,
|
||||
_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,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use crate::{
|
||||
annotations::{
|
||||
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
|
||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
||||
},
|
||||
array::Array,
|
||||
build::{ToArgs, WriteArgs},
|
||||
|
@ -24,9 +24,9 @@ use crate::{
|
|||
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
||||
module::{
|
||||
AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter,
|
||||
ExternModuleParameterValue, Module, ModuleBody, NameId, NameOptId, NormalModuleBody, Stmt,
|
||||
StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg,
|
||||
StmtWire,
|
||||
ExternModuleParameterValue, Module, ModuleBody, ModuleIO, NameId, NameOptId,
|
||||
NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance,
|
||||
StmtMatch, StmtReg, StmtWire,
|
||||
transform::{
|
||||
simplify_enums::{SimplifyEnumsError, SimplifyEnumsKind, simplify_enums},
|
||||
simplify_memories::simplify_memories,
|
||||
|
@ -53,7 +53,7 @@ use std::{
|
|||
fs,
|
||||
hash::Hash,
|
||||
io,
|
||||
ops::Range,
|
||||
ops::{ControlFlow, Range},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
|
@ -405,10 +405,10 @@ impl TypeState {
|
|||
self.next_type_name.set(id + 1);
|
||||
Ident(Intern::intern_owned(format!("Ty{id}")))
|
||||
}
|
||||
fn get_bundle_field(&mut self, ty: Bundle, name: Interned<str>) -> Result<Ident> {
|
||||
fn get_bundle_field(&mut self, ty: Bundle, name: Interned<str>) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.bundle_ns(ty)?.borrow_mut().get(name))
|
||||
}
|
||||
fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc<RefCell<Namespace>>)> {
|
||||
fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc<RefCell<Namespace>>), FirrtlError> {
|
||||
self.bundle_defs.get_or_make(ty, |&ty, definitions| {
|
||||
let mut ns = Namespace::default();
|
||||
let mut body = String::new();
|
||||
|
@ -429,13 +429,13 @@ impl TypeState {
|
|||
Ok((name, Rc::new(RefCell::new(ns))))
|
||||
})
|
||||
}
|
||||
fn bundle_ty(&self, ty: Bundle) -> Result<Ident> {
|
||||
fn bundle_ty(&self, ty: Bundle) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.bundle_def(ty)?.0)
|
||||
}
|
||||
fn bundle_ns(&self, ty: Bundle) -> Result<Rc<RefCell<Namespace>>> {
|
||||
fn bundle_ns(&self, ty: Bundle) -> Result<Rc<RefCell<Namespace>>, FirrtlError> {
|
||||
Ok(self.bundle_def(ty)?.1)
|
||||
}
|
||||
fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc<EnumDef>)> {
|
||||
fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc<EnumDef>), FirrtlError> {
|
||||
self.enum_defs.get_or_make(ty, |&ty, definitions| {
|
||||
let mut variants = Namespace::default();
|
||||
let mut body = String::new();
|
||||
|
@ -462,13 +462,13 @@ impl TypeState {
|
|||
))
|
||||
})
|
||||
}
|
||||
fn enum_ty(&self, ty: Enum) -> Result<Ident> {
|
||||
fn enum_ty(&self, ty: Enum) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.enum_def(ty)?.0)
|
||||
}
|
||||
fn get_enum_variant(&mut self, ty: Enum, name: Interned<str>) -> Result<Ident> {
|
||||
fn get_enum_variant(&mut self, ty: Enum, name: Interned<str>) -> Result<Ident, FirrtlError> {
|
||||
Ok(self.enum_def(ty)?.1.variants.borrow_mut().get(name))
|
||||
}
|
||||
fn ty<T: Type>(&self, ty: T) -> Result<String> {
|
||||
fn ty<T: Type>(&self, ty: T) -> Result<String, FirrtlError> {
|
||||
Ok(match ty.canonical() {
|
||||
CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(),
|
||||
CanonicalType::Enum(ty) => self.enum_ty(ty)?.to_string(),
|
||||
|
@ -486,7 +486,7 @@ impl TypeState {
|
|||
CanonicalType::Reset(Reset {}) => "Reset".into(),
|
||||
CanonicalType::PhantomConst(_) => "{}".into(),
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
return Err(FirrtlError::SimOnlyValuesAreNotPermitted.into());
|
||||
return Err(FirrtlError::SimOnlyValuesAreNotPermitted);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1904,6 +1904,7 @@ impl<'a> Exporter<'a> {
|
|||
class: str::to_string(class),
|
||||
additional_fields: (*additional_fields).into(),
|
||||
},
|
||||
Annotation::XdcLocation(_) | Annotation::XdcIOStandard(_) => return,
|
||||
};
|
||||
self.annotations.push(FirrtlAnnotation {
|
||||
data,
|
||||
|
@ -2674,21 +2675,12 @@ impl FileBackendTrait for TestBackend {
|
|||
|
||||
fn export_impl(
|
||||
file_backend: &mut dyn WrappedFileBackendTrait,
|
||||
mut top_module: Interned<Module<Bundle>>,
|
||||
top_module: Interned<Module<Bundle>>,
|
||||
options: ExportOptions,
|
||||
) -> Result<(), WrappedError> {
|
||||
let ExportOptions {
|
||||
simplify_memories: do_simplify_memories,
|
||||
simplify_enums: do_simplify_enums,
|
||||
__private: _,
|
||||
} = options;
|
||||
if let Some(kind) = do_simplify_enums {
|
||||
top_module =
|
||||
simplify_enums(top_module, kind).map_err(|e| file_backend.simplify_enums_error(e))?;
|
||||
}
|
||||
if do_simplify_memories {
|
||||
top_module = simplify_memories(top_module);
|
||||
}
|
||||
let top_module = options
|
||||
.prepare_top_module(top_module)
|
||||
.map_err(|e| file_backend.simplify_enums_error(e))?;
|
||||
let indent_depth = Cell::new(0);
|
||||
let mut global_ns = Namespace::default();
|
||||
let circuit_name = global_ns.get(top_module.name_id());
|
||||
|
@ -2856,6 +2848,29 @@ impl ExportOptions {
|
|||
if f.alternate() { "\n}" } else { " }" }
|
||||
)
|
||||
}
|
||||
fn prepare_top_module_helper(
|
||||
self,
|
||||
mut top_module: Interned<Module<Bundle>>,
|
||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||
let Self {
|
||||
simplify_memories: do_simplify_memories,
|
||||
simplify_enums: do_simplify_enums,
|
||||
__private: _,
|
||||
} = self;
|
||||
if let Some(kind) = do_simplify_enums {
|
||||
top_module = simplify_enums(top_module, kind)?;
|
||||
}
|
||||
if do_simplify_memories {
|
||||
top_module = simplify_memories(top_module);
|
||||
}
|
||||
Ok(top_module)
|
||||
}
|
||||
pub fn prepare_top_module<T: BundleType>(
|
||||
self,
|
||||
top_module: impl AsRef<Module<T>>,
|
||||
) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> {
|
||||
self.prepare_top_module_helper(top_module.as_ref().canonical().intern())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ExportOptions {
|
||||
|
@ -2885,6 +2900,497 @@ pub fn export<T: BundleType, B: FileBackendTrait>(
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ScalarizedModuleABIError {
|
||||
SimOnlyValuesAreNotPermitted,
|
||||
SimplifyEnumsError(SimplifyEnumsError),
|
||||
}
|
||||
|
||||
impl fmt::Display for ScalarizedModuleABIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted => {
|
||||
FirrtlError::SimOnlyValuesAreNotPermitted.fmt(f)
|
||||
}
|
||||
ScalarizedModuleABIError::SimplifyEnumsError(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ScalarizedModuleABIError {}
|
||||
|
||||
impl From<SimplifyEnumsError> for ScalarizedModuleABIError {
|
||||
fn from(value: SimplifyEnumsError) -> Self {
|
||||
Self::SimplifyEnumsError(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ScalarizedModuleABIPortItem {
|
||||
Group(ScalarizedModuleABIPortGroup),
|
||||
Port(ScalarizedModuleABIPort),
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABIPortItem {
|
||||
pub fn module_io(self) -> ModuleIO<CanonicalType> {
|
||||
*self
|
||||
.target()
|
||||
.base()
|
||||
.module_io()
|
||||
.expect("known to be ModuleIO")
|
||||
}
|
||||
pub fn target(self) -> Interned<Target> {
|
||||
match self {
|
||||
Self::Group(v) => v.target(),
|
||||
Self::Port(v) => v.target(),
|
||||
}
|
||||
}
|
||||
fn for_each_port_and_annotations_helper<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
&self,
|
||||
parent: Option<&ScalarizedModuleABIAnnotations<'_>>,
|
||||
f: &mut F,
|
||||
) -> ControlFlow<B> {
|
||||
match self {
|
||||
Self::Group(v) => v.for_each_port_and_annotations_helper(parent, f),
|
||||
Self::Port(port) => f(
|
||||
port,
|
||||
ScalarizedModuleABIAnnotations::new(parent, port.annotations.iter()),
|
||||
),
|
||||
}
|
||||
}
|
||||
pub fn for_each_port_and_annotations<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> ControlFlow<B> {
|
||||
self.for_each_port_and_annotations_helper(None, &mut f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ScalarizedModuleABIPortItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Group(v) => v.fmt(f),
|
||||
Self::Port(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScalarizedModuleABIAnnotations<'a> {
|
||||
parent: Option<&'a ScalarizedModuleABIAnnotations<'a>>,
|
||||
parent_len: usize,
|
||||
annotations: std::slice::Iter<'a, TargetedAnnotation>,
|
||||
}
|
||||
|
||||
impl<'a> ScalarizedModuleABIAnnotations<'a> {
|
||||
fn new(
|
||||
parent: Option<&'a ScalarizedModuleABIAnnotations<'a>>,
|
||||
annotations: std::slice::Iter<'a, TargetedAnnotation>,
|
||||
) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
parent_len: parent.map_or(0, |parent| parent.len()),
|
||||
annotations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for ScalarizedModuleABIAnnotations<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
parent: None,
|
||||
parent_len: 0,
|
||||
annotations: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ScalarizedModuleABIAnnotations<'a> {
|
||||
type Item = &'a TargetedAnnotation;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let retval @ Some(_) = self.annotations.next() {
|
||||
break retval;
|
||||
}
|
||||
*self = self.parent?.clone();
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.len();
|
||||
(len, Some(len))
|
||||
}
|
||||
|
||||
fn fold<B, F>(mut self, mut init: B, mut f: F) -> B
|
||||
where
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
loop {
|
||||
let Self {
|
||||
parent,
|
||||
parent_len: _,
|
||||
annotations,
|
||||
} = self;
|
||||
init = annotations.fold(init, &mut f);
|
||||
let Some(next) = parent else {
|
||||
break;
|
||||
};
|
||||
self = next.clone();
|
||||
}
|
||||
init
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FusedIterator for ScalarizedModuleABIAnnotations<'_> {}
|
||||
|
||||
impl ExactSizeIterator for ScalarizedModuleABIAnnotations<'_> {
|
||||
fn len(&self) -> usize {
|
||||
self.parent_len + self.annotations.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScalarizedModuleABIPortGroup {
|
||||
target: Interned<Target>,
|
||||
common_annotations: Interned<[TargetedAnnotation]>,
|
||||
children: Interned<[ScalarizedModuleABIPortItem]>,
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABIPortGroup {
|
||||
pub fn module_io(self) -> ModuleIO<CanonicalType> {
|
||||
*self
|
||||
.target
|
||||
.base()
|
||||
.module_io()
|
||||
.expect("known to be ModuleIO")
|
||||
}
|
||||
pub fn target(self) -> Interned<Target> {
|
||||
self.target
|
||||
}
|
||||
pub fn common_annotations(self) -> Interned<[TargetedAnnotation]> {
|
||||
self.common_annotations
|
||||
}
|
||||
pub fn children(self) -> Interned<[ScalarizedModuleABIPortItem]> {
|
||||
self.children
|
||||
}
|
||||
fn for_each_port_and_annotations_helper<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
&self,
|
||||
parent: Option<&ScalarizedModuleABIAnnotations<'_>>,
|
||||
f: &mut F,
|
||||
) -> ControlFlow<B> {
|
||||
let parent = ScalarizedModuleABIAnnotations::new(parent, self.common_annotations.iter());
|
||||
for item in &self.children {
|
||||
item.for_each_port_and_annotations_helper(Some(&parent), f)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
pub fn for_each_port_and_annotations<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> ControlFlow<B> {
|
||||
self.for_each_port_and_annotations_helper(None, &mut f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScalarizedModuleABIPort {
|
||||
target: Interned<Target>,
|
||||
annotations: Interned<[TargetedAnnotation]>,
|
||||
scalarized_name: Interned<str>,
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABIPort {
|
||||
pub fn module_io(self) -> ModuleIO<CanonicalType> {
|
||||
*self
|
||||
.target
|
||||
.base()
|
||||
.module_io()
|
||||
.expect("known to be ModuleIO")
|
||||
}
|
||||
pub fn target(self) -> Interned<Target> {
|
||||
self.target
|
||||
}
|
||||
pub fn annotations(self) -> Interned<[TargetedAnnotation]> {
|
||||
self.annotations
|
||||
}
|
||||
pub fn scalarized_name(self) -> Interned<str> {
|
||||
self.scalarized_name
|
||||
}
|
||||
}
|
||||
|
||||
enum ScalarizeTreeNodeBody {
|
||||
Leaf {
|
||||
scalarized_name: Interned<str>,
|
||||
},
|
||||
Bundle {
|
||||
ty: Bundle,
|
||||
fields: Vec<ScalarizeTreeNode>,
|
||||
},
|
||||
Array {
|
||||
elements: Vec<ScalarizeTreeNode>,
|
||||
},
|
||||
}
|
||||
|
||||
struct ScalarizeTreeNode {
|
||||
target: Interned<Target>,
|
||||
annotations: Vec<TargetedAnnotation>,
|
||||
body: ScalarizeTreeNodeBody,
|
||||
}
|
||||
|
||||
impl ScalarizeTreeNode {
|
||||
#[track_caller]
|
||||
fn find_target(&mut self, annotation_target: Interned<Target>) -> &mut Self {
|
||||
match *annotation_target {
|
||||
Target::Base(_) => {
|
||||
assert_eq!(
|
||||
annotation_target, self.target,
|
||||
"annotation not on correct ModuleIO",
|
||||
);
|
||||
self
|
||||
}
|
||||
Target::Child(target_child) => {
|
||||
let parent = self.find_target(target_child.parent());
|
||||
match *target_child.path_element() {
|
||||
TargetPathElement::BundleField(TargetPathBundleField { name }) => {
|
||||
match parent.body {
|
||||
ScalarizeTreeNodeBody::Leaf { .. } => parent,
|
||||
ScalarizeTreeNodeBody::Bundle { ty, ref mut fields } => {
|
||||
&mut fields[ty.name_indexes()[&name]]
|
||||
}
|
||||
ScalarizeTreeNodeBody::Array { .. } => {
|
||||
unreachable!("types are known to match")
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => {
|
||||
match parent.body {
|
||||
ScalarizeTreeNodeBody::Leaf { .. } => parent,
|
||||
ScalarizeTreeNodeBody::Bundle { .. } => {
|
||||
unreachable!("types are known to match")
|
||||
}
|
||||
ScalarizeTreeNodeBody::Array { ref mut elements } => {
|
||||
&mut elements[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
TargetPathElement::DynArrayElement(_) => {
|
||||
unreachable!("annotations are only on static targets");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn into_scalarized_item(self) -> ScalarizedModuleABIPortItem {
|
||||
let Self {
|
||||
target,
|
||||
annotations,
|
||||
body,
|
||||
} = self;
|
||||
match body {
|
||||
ScalarizeTreeNodeBody::Leaf { scalarized_name } => {
|
||||
ScalarizedModuleABIPortItem::Port(ScalarizedModuleABIPort {
|
||||
target,
|
||||
annotations: Intern::intern_owned(annotations),
|
||||
scalarized_name,
|
||||
})
|
||||
}
|
||||
ScalarizeTreeNodeBody::Bundle { fields: items, .. }
|
||||
| ScalarizeTreeNodeBody::Array { elements: items } => {
|
||||
ScalarizedModuleABIPortItem::Group(ScalarizedModuleABIPortGroup {
|
||||
target,
|
||||
common_annotations: Intern::intern_owned(annotations),
|
||||
children: Interned::from_iter(
|
||||
items.into_iter().map(Self::into_scalarized_item),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ScalarizeTreeBuilder {
|
||||
scalarized_ns: Namespace,
|
||||
type_state: TypeState,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ScalarizeTreeBuilder {
|
||||
#[track_caller]
|
||||
fn build_bundle(
|
||||
&mut self,
|
||||
target: Interned<Target>,
|
||||
ty: Bundle,
|
||||
) -> Result<ScalarizeTreeNode, ScalarizedModuleABIError> {
|
||||
let mut fields = Vec::with_capacity(ty.fields().len());
|
||||
let original_len = self.name.len();
|
||||
for BundleField { name, .. } in ty.fields() {
|
||||
let firrtl_name = self
|
||||
.type_state
|
||||
.get_bundle_field(ty, name)
|
||||
.map_err(|e| match e {
|
||||
FirrtlError::SimOnlyValuesAreNotPermitted => {
|
||||
ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted
|
||||
}
|
||||
})?
|
||||
.0;
|
||||
write!(self.name, "_{firrtl_name}").expect("writing to String is infallible");
|
||||
fields.push(
|
||||
self.build(
|
||||
target
|
||||
.join(TargetPathElement::intern_sized(
|
||||
TargetPathBundleField { name }.into(),
|
||||
))
|
||||
.intern_sized(),
|
||||
)?,
|
||||
);
|
||||
self.name.truncate(original_len);
|
||||
}
|
||||
Ok(ScalarizeTreeNode {
|
||||
target,
|
||||
annotations: Vec::new(),
|
||||
body: ScalarizeTreeNodeBody::Bundle { ty, fields },
|
||||
})
|
||||
}
|
||||
#[track_caller]
|
||||
fn build(
|
||||
&mut self,
|
||||
target: Interned<Target>,
|
||||
) -> Result<ScalarizeTreeNode, ScalarizedModuleABIError> {
|
||||
Ok(match target.canonical_ty() {
|
||||
CanonicalType::UInt(_)
|
||||
| CanonicalType::SInt(_)
|
||||
| CanonicalType::Bool(_)
|
||||
| CanonicalType::Enum(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_) => {
|
||||
let scalarized_name = self.scalarized_ns.get(str::intern(&self.name)).0;
|
||||
ScalarizeTreeNode {
|
||||
target,
|
||||
annotations: Vec::new(),
|
||||
body: ScalarizeTreeNodeBody::Leaf { scalarized_name },
|
||||
}
|
||||
}
|
||||
CanonicalType::Array(ty) => {
|
||||
let mut elements = Vec::with_capacity(ty.len());
|
||||
let original_len = self.name.len();
|
||||
for index in 0..ty.len() {
|
||||
write!(self.name, "_{index}").expect("writing to String is infallible");
|
||||
elements.push(
|
||||
self.build(
|
||||
target
|
||||
.join(TargetPathElement::intern_sized(
|
||||
TargetPathArrayElement { index }.into(),
|
||||
))
|
||||
.intern_sized(),
|
||||
)?,
|
||||
);
|
||||
self.name.truncate(original_len);
|
||||
}
|
||||
ScalarizeTreeNode {
|
||||
target,
|
||||
annotations: Vec::new(),
|
||||
body: ScalarizeTreeNodeBody::Array { elements },
|
||||
}
|
||||
}
|
||||
CanonicalType::Bundle(ty) => self.build_bundle(target, ty)?,
|
||||
CanonicalType::PhantomConst(_) => {
|
||||
self.build_bundle(target, Bundle::new(Interned::default()))?
|
||||
}
|
||||
CanonicalType::DynSimOnly(_) => {
|
||||
return Err(ScalarizedModuleABIError::SimOnlyValuesAreNotPermitted);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScalarizedModuleABI {
|
||||
module_io: Interned<[AnnotatedModuleIO]>,
|
||||
items: Interned<[ScalarizedModuleABIPortItem]>,
|
||||
}
|
||||
|
||||
impl ScalarizedModuleABI {
|
||||
#[track_caller]
|
||||
fn from_io(module_io: Interned<[AnnotatedModuleIO]>) -> Result<Self, ScalarizedModuleABIError> {
|
||||
let mut firrtl_ns = Namespace::default();
|
||||
let mut tree_builder = ScalarizeTreeBuilder::default();
|
||||
let mut items = Vec::new();
|
||||
for module_io in module_io {
|
||||
let firrtl_name = firrtl_ns.get(module_io.module_io.name_id());
|
||||
tree_builder.name.clear();
|
||||
tree_builder.name.push_str(&firrtl_name.0);
|
||||
let mut tree = tree_builder.build(Target::from(module_io.module_io).intern_sized())?;
|
||||
for annotation in module_io.annotations {
|
||||
tree.find_target(annotation.target())
|
||||
.annotations
|
||||
.push(annotation);
|
||||
}
|
||||
items.push(tree.into_scalarized_item());
|
||||
}
|
||||
Ok(Self {
|
||||
module_io,
|
||||
items: Intern::intern_owned(items),
|
||||
})
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new<T: BundleType>(
|
||||
module: impl AsRef<Module<T>>,
|
||||
options: ExportOptions,
|
||||
) -> Result<Self, ScalarizedModuleABIError> {
|
||||
Self::from_io(options.prepare_top_module(module)?.module_io())
|
||||
}
|
||||
pub fn module_io(&self) -> Interned<[AnnotatedModuleIO]> {
|
||||
self.module_io
|
||||
}
|
||||
pub fn items(&self) -> Interned<[ScalarizedModuleABIPortItem]> {
|
||||
self.items
|
||||
}
|
||||
pub fn for_each_port_and_annotations<
|
||||
F: for<'a> FnMut(
|
||||
&'a ScalarizedModuleABIPort,
|
||||
ScalarizedModuleABIAnnotations<'a>,
|
||||
) -> ControlFlow<B>,
|
||||
B,
|
||||
>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> ControlFlow<B> {
|
||||
for item in &self.items {
|
||||
item.for_each_port_and_annotations_helper(None, &mut f)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) {
|
||||
|
|
|
@ -833,6 +833,8 @@ pub struct AnnotatedModuleIO<S: ModuleBuildingStatus = ModuleBuilt> {
|
|||
pub module_io: ModuleIO<CanonicalType>,
|
||||
}
|
||||
|
||||
impl Copy for AnnotatedModuleIO {}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum ModuleKind {
|
||||
Extern,
|
||||
|
|
|
@ -1817,6 +1817,8 @@ impl_run_pass_copy!([] UInt);
|
|||
impl_run_pass_copy!([] usize);
|
||||
impl_run_pass_copy!([] FormalKind);
|
||||
impl_run_pass_copy!([] PhantomConst);
|
||||
impl_run_pass_copy!([] crate::build::vendor::xilinx::XdcIOStandardAnnotation);
|
||||
impl_run_pass_copy!([] crate::build::vendor::xilinx::XdcLocationAnnotation);
|
||||
|
||||
macro_rules! impl_run_pass_for_struct {
|
||||
(
|
||||
|
@ -2217,6 +2219,8 @@ impl_run_pass_for_enum! {
|
|||
BlackBoxPath(v),
|
||||
DocString(v),
|
||||
CustomFirrtl(v),
|
||||
XdcLocation(v),
|
||||
XdcIOStandard(v),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
||||
},
|
||||
array::ArrayType,
|
||||
build::vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation},
|
||||
bundle::{Bundle, BundleField, BundleType},
|
||||
clock::Clock,
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
|
|
|
@ -1176,7 +1176,9 @@
|
|||
"BlackBoxInline": "Visible",
|
||||
"BlackBoxPath": "Visible",
|
||||
"DocString": "Visible",
|
||||
"CustomFirrtl": "Visible"
|
||||
"CustomFirrtl": "Visible",
|
||||
"XdcLocation": "Visible",
|
||||
"XdcIOStandard": "Visible"
|
||||
}
|
||||
},
|
||||
"DontTouchAnnotation": {
|
||||
|
@ -1214,6 +1216,16 @@
|
|||
"$kind": "Opaque"
|
||||
}
|
||||
},
|
||||
"XdcLocationAnnotation": {
|
||||
"data": {
|
||||
"$kind": "Opaque"
|
||||
}
|
||||
},
|
||||
"XdcIOStandardAnnotation": {
|
||||
"data": {
|
||||
"$kind": "Opaque"
|
||||
}
|
||||
},
|
||||
"Target": {
|
||||
"data": {
|
||||
"$kind": "Enum",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue