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])*
|
$(#[$meta:meta])*
|
||||||
$vis:vis enum $Annotation:ident {
|
$vis:vis enum $Annotation:ident {
|
||||||
$($Variant:ident($T:ident),)*
|
$($Variant:ident($T:ty),)*
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
$(#[$meta])*
|
$(#[$meta])*
|
||||||
|
@ -199,6 +199,8 @@ make_annotation_enum! {
|
||||||
BlackBoxPath(BlackBoxPathAnnotation),
|
BlackBoxPath(BlackBoxPathAnnotation),
|
||||||
DocString(DocStringAnnotation),
|
DocString(DocStringAnnotation),
|
||||||
CustomFirrtl(CustomFirrtlAnnotation),
|
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
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use crate::intern::Interned;
|
||||||
|
|
||||||
pub mod yosys_nextpnr_prjxray;
|
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> {
|
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
|
||||||
yosys_nextpnr_prjxray::built_in_job_kinds()
|
yosys_nextpnr_prjxray::built_in_job_kinds()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
annotations::Annotation,
|
||||||
build::{
|
build::{
|
||||||
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies,
|
CommandParams, DynJobKind, GetBaseJob, JobAndDependencies, JobArgsAndDependencies,
|
||||||
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, ToArgs, WriteArgs,
|
JobDependencies, JobItem, JobItemName, JobKind, JobKindAndDependencies, ToArgs, WriteArgs,
|
||||||
|
@ -9,17 +10,20 @@ use crate::{
|
||||||
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
ExternalCommand, ExternalCommandJob, ExternalCommandJobKind, ExternalProgramTrait,
|
||||||
},
|
},
|
||||||
interned_known_utf8_method, interned_known_utf8_path_buf_method,
|
interned_known_utf8_method, interned_known_utf8_path_buf_method,
|
||||||
|
vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation},
|
||||||
verilog::{VerilogDialect, VerilogJob, VerilogJobKind},
|
verilog::{VerilogDialect, VerilogJob, VerilogJobKind},
|
||||||
},
|
},
|
||||||
|
bundle::Bundle,
|
||||||
|
firrtl::{ScalarizedModuleABI, ScalarizedModuleABIAnnotations, ScalarizedModuleABIPort},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
module::NameId,
|
module::{Module, NameId},
|
||||||
prelude::JobParams,
|
prelude::JobParams,
|
||||||
util::job_server::AcquiredJob,
|
util::job_server::AcquiredJob,
|
||||||
};
|
};
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use eyre::Context;
|
use eyre::Context;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::{fmt, ops::ControlFlow};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
|
||||||
pub struct YosysNextpnrXrayWriteYsFileJobKind;
|
pub struct YosysNextpnrXrayWriteYsFileJobKind;
|
||||||
|
@ -312,10 +316,90 @@ impl ToArgs for YosysNextpnrXrayWriteXdcFileArgs {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||||
pub struct YosysNextpnrXrayWriteXdcFile {
|
pub struct YosysNextpnrXrayWriteXdcFile {
|
||||||
|
firrtl_export_options: crate::firrtl::ExportOptions,
|
||||||
output_dir: Interned<str>,
|
output_dir: Interned<str>,
|
||||||
xdc_file: 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 {
|
impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
||||||
type Args = YosysNextpnrXrayWriteXdcFileArgs;
|
type Args = YosysNextpnrXrayWriteXdcFileArgs;
|
||||||
type Job = YosysNextpnrXrayWriteXdcFile;
|
type Job = YosysNextpnrXrayWriteXdcFile;
|
||||||
|
@ -329,9 +413,19 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
||||||
args: JobArgsAndDependencies<Self>,
|
args: JobArgsAndDependencies<Self>,
|
||||||
params: &JobParams,
|
params: &JobParams,
|
||||||
) -> eyre::Result<JobAndDependencies<Self>> {
|
) -> 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| {
|
args.args_to_jobs_simple(params, |_kind, args, dependencies| {
|
||||||
let YosysNextpnrXrayWriteXdcFileArgs {} = args;
|
let YosysNextpnrXrayWriteXdcFileArgs {} = args;
|
||||||
Ok(YosysNextpnrXrayWriteXdcFile {
|
Ok(YosysNextpnrXrayWriteXdcFile {
|
||||||
|
firrtl_export_options,
|
||||||
output_dir: dependencies.base_job().output_dir(),
|
output_dir: dependencies.base_job().output_dir(),
|
||||||
xdc_file: dependencies.base_job().file_with_ext("xdc"),
|
xdc_file: dependencies.base_job().file_with_ext("xdc"),
|
||||||
})
|
})
|
||||||
|
@ -361,10 +455,12 @@ impl JobKind for YosysNextpnrXrayWriteXdcFileJobKind {
|
||||||
self,
|
self,
|
||||||
job: &Self::Job,
|
job: &Self::Job,
|
||||||
inputs: &[JobItem],
|
inputs: &[JobItem],
|
||||||
_params: &JobParams,
|
params: &JobParams,
|
||||||
_acquired_job: &mut AcquiredJob,
|
_acquired_job: &mut AcquiredJob,
|
||||||
) -> eyre::Result<Vec<JobItem>> {
|
) -> eyre::Result<Vec<JobItem>> {
|
||||||
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
|
||||||
|
let mut xdc = String::new();
|
||||||
|
job.write_xdc_contents(&mut xdc, params.main_module())?;
|
||||||
// TODO: create actual .xdc from input module
|
// TODO: create actual .xdc from input module
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
job.xdc_file,
|
job.xdc_file,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
annotations::{
|
annotations::{
|
||||||
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
|
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
|
||||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
||||||
},
|
},
|
||||||
array::Array,
|
array::Array,
|
||||||
build::{ToArgs, WriteArgs},
|
build::{ToArgs, WriteArgs},
|
||||||
|
@ -24,9 +24,9 @@ use crate::{
|
||||||
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
memory::{Mem, PortKind, PortName, ReadUnderWrite},
|
||||||
module::{
|
module::{
|
||||||
AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter,
|
AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter,
|
||||||
ExternModuleParameterValue, Module, ModuleBody, NameId, NameOptId, NormalModuleBody, Stmt,
|
ExternModuleParameterValue, Module, ModuleBody, ModuleIO, NameId, NameOptId,
|
||||||
StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg,
|
NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance,
|
||||||
StmtWire,
|
StmtMatch, StmtReg, StmtWire,
|
||||||
transform::{
|
transform::{
|
||||||
simplify_enums::{SimplifyEnumsError, SimplifyEnumsKind, simplify_enums},
|
simplify_enums::{SimplifyEnumsError, SimplifyEnumsKind, simplify_enums},
|
||||||
simplify_memories::simplify_memories,
|
simplify_memories::simplify_memories,
|
||||||
|
@ -53,7 +53,7 @@ use std::{
|
||||||
fs,
|
fs,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
io,
|
io,
|
||||||
ops::Range,
|
ops::{ControlFlow, Range},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
@ -405,10 +405,10 @@ impl TypeState {
|
||||||
self.next_type_name.set(id + 1);
|
self.next_type_name.set(id + 1);
|
||||||
Ident(Intern::intern_owned(format!("Ty{id}")))
|
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))
|
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| {
|
self.bundle_defs.get_or_make(ty, |&ty, definitions| {
|
||||||
let mut ns = Namespace::default();
|
let mut ns = Namespace::default();
|
||||||
let mut body = String::new();
|
let mut body = String::new();
|
||||||
|
@ -429,13 +429,13 @@ impl TypeState {
|
||||||
Ok((name, Rc::new(RefCell::new(ns))))
|
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)
|
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)
|
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| {
|
self.enum_defs.get_or_make(ty, |&ty, definitions| {
|
||||||
let mut variants = Namespace::default();
|
let mut variants = Namespace::default();
|
||||||
let mut body = String::new();
|
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)
|
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))
|
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() {
|
Ok(match ty.canonical() {
|
||||||
CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(),
|
CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(),
|
||||||
CanonicalType::Enum(ty) => self.enum_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::Reset(Reset {}) => "Reset".into(),
|
||||||
CanonicalType::PhantomConst(_) => "{}".into(),
|
CanonicalType::PhantomConst(_) => "{}".into(),
|
||||||
CanonicalType::DynSimOnly(_) => {
|
CanonicalType::DynSimOnly(_) => {
|
||||||
return Err(FirrtlError::SimOnlyValuesAreNotPermitted.into());
|
return Err(FirrtlError::SimOnlyValuesAreNotPermitted);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1904,6 +1904,7 @@ impl<'a> Exporter<'a> {
|
||||||
class: str::to_string(class),
|
class: str::to_string(class),
|
||||||
additional_fields: (*additional_fields).into(),
|
additional_fields: (*additional_fields).into(),
|
||||||
},
|
},
|
||||||
|
Annotation::XdcLocation(_) | Annotation::XdcIOStandard(_) => return,
|
||||||
};
|
};
|
||||||
self.annotations.push(FirrtlAnnotation {
|
self.annotations.push(FirrtlAnnotation {
|
||||||
data,
|
data,
|
||||||
|
@ -2674,21 +2675,12 @@ impl FileBackendTrait for TestBackend {
|
||||||
|
|
||||||
fn export_impl(
|
fn export_impl(
|
||||||
file_backend: &mut dyn WrappedFileBackendTrait,
|
file_backend: &mut dyn WrappedFileBackendTrait,
|
||||||
mut top_module: Interned<Module<Bundle>>,
|
top_module: Interned<Module<Bundle>>,
|
||||||
options: ExportOptions,
|
options: ExportOptions,
|
||||||
) -> Result<(), WrappedError> {
|
) -> Result<(), WrappedError> {
|
||||||
let ExportOptions {
|
let top_module = options
|
||||||
simplify_memories: do_simplify_memories,
|
.prepare_top_module(top_module)
|
||||||
simplify_enums: do_simplify_enums,
|
.map_err(|e| file_backend.simplify_enums_error(e))?;
|
||||||
__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 indent_depth = Cell::new(0);
|
let indent_depth = Cell::new(0);
|
||||||
let mut global_ns = Namespace::default();
|
let mut global_ns = Namespace::default();
|
||||||
let circuit_name = global_ns.get(top_module.name_id());
|
let circuit_name = global_ns.get(top_module.name_id());
|
||||||
|
@ -2856,6 +2848,29 @@ impl ExportOptions {
|
||||||
if f.alternate() { "\n}" } else { " }" }
|
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 {
|
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)]
|
#[doc(hidden)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_export_firrtl_impl<T: BundleType>(top_module: &Module<T>, expected: TestBackend) {
|
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>,
|
pub module_io: ModuleIO<CanonicalType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Copy for AnnotatedModuleIO {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum ModuleKind {
|
pub enum ModuleKind {
|
||||||
Extern,
|
Extern,
|
||||||
|
|
|
@ -1817,6 +1817,8 @@ impl_run_pass_copy!([] UInt);
|
||||||
impl_run_pass_copy!([] usize);
|
impl_run_pass_copy!([] usize);
|
||||||
impl_run_pass_copy!([] FormalKind);
|
impl_run_pass_copy!([] FormalKind);
|
||||||
impl_run_pass_copy!([] PhantomConst);
|
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 {
|
macro_rules! impl_run_pass_for_struct {
|
||||||
(
|
(
|
||||||
|
@ -2217,6 +2219,8 @@ impl_run_pass_for_enum! {
|
||||||
BlackBoxPath(v),
|
BlackBoxPath(v),
|
||||||
DocString(v),
|
DocString(v),
|
||||||
CustomFirrtl(v),
|
CustomFirrtl(v),
|
||||||
|
XdcLocation(v),
|
||||||
|
XdcIOStandard(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
||||||
},
|
},
|
||||||
array::ArrayType,
|
array::ArrayType,
|
||||||
|
build::vendor::xilinx::{XdcIOStandardAnnotation, XdcLocationAnnotation},
|
||||||
bundle::{Bundle, BundleField, BundleType},
|
bundle::{Bundle, BundleField, BundleType},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
enum_::{Enum, EnumType, EnumVariant},
|
enum_::{Enum, EnumType, EnumVariant},
|
||||||
|
|
|
@ -1176,7 +1176,9 @@
|
||||||
"BlackBoxInline": "Visible",
|
"BlackBoxInline": "Visible",
|
||||||
"BlackBoxPath": "Visible",
|
"BlackBoxPath": "Visible",
|
||||||
"DocString": "Visible",
|
"DocString": "Visible",
|
||||||
"CustomFirrtl": "Visible"
|
"CustomFirrtl": "Visible",
|
||||||
|
"XdcLocation": "Visible",
|
||||||
|
"XdcIOStandard": "Visible"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DontTouchAnnotation": {
|
"DontTouchAnnotation": {
|
||||||
|
@ -1214,6 +1216,16 @@
|
||||||
"$kind": "Opaque"
|
"$kind": "Opaque"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"XdcLocationAnnotation": {
|
||||||
|
"data": {
|
||||||
|
"$kind": "Opaque"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"XdcIOStandardAnnotation": {
|
||||||
|
"data": {
|
||||||
|
"$kind": "Opaque"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Target": {
|
"Target": {
|
||||||
"data": {
|
"data": {
|
||||||
"$kind": "Enum",
|
"$kind": "Enum",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue