From f3d6528f5b1e23cb3e9a1ef338c477738c7d7b08 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 19:54:17 -0700 Subject: [PATCH] make annotations easier to use --- crates/fayalite/src/annotations.rs | 138 ++++++++++++++++-- crates/fayalite/src/firrtl.rs | 4 +- crates/fayalite/src/module/transform/visit.rs | 2 +- crates/fayalite/src/prelude.rs | 5 +- crates/fayalite/tests/module.rs | 58 ++++---- crates/fayalite/visit_types.json | 7 +- 6 files changed, 163 insertions(+), 51 deletions(-) diff --git a/crates/fayalite/src/annotations.rs b/crates/fayalite/src/annotations.rs index 6b96d01..8eff4a0 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use std::{ fmt, hash::{Hash, Hasher}, + iter::FusedIterator, ops::Deref, }; @@ -118,6 +119,9 @@ pub struct CustomFirrtlAnnotation { pub additional_fields: CustomFirrtlAnnotationFields, } +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +pub struct DontTouchAnnotation; + #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] pub struct SVAttributeAnnotation { pub text: Interned, @@ -139,15 +143,63 @@ pub struct DocStringAnnotation { pub text: Interned, } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[non_exhaustive] -pub enum Annotation { - DontTouch, - SVAttribute(SVAttributeAnnotation), - BlackBoxInline(BlackBoxInlineAnnotation), - BlackBoxPath(BlackBoxPathAnnotation), - DocString(DocStringAnnotation), - CustomFirrtl(CustomFirrtlAnnotation), +macro_rules! make_annotation_enum { + ( + $(#[$meta:meta])* + $vis:vis enum $Annotation:ident { + $($Variant:ident($T:ident),)* + } + ) => { + $(#[$meta])* + $vis enum $Annotation { + $($Variant($T),)* + } + + $(impl IntoAnnotations for $T { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(self)] + } + } + + impl IntoAnnotations for &'_ $T { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(*self)] + } + } + + impl IntoAnnotations for &'_ mut $T { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(*self)] + } + } + + impl IntoAnnotations for Box<$T> { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(*self)] + } + })* + }; +} + +make_annotation_enum! { + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + #[non_exhaustive] + pub enum Annotation { + DontTouch(DontTouchAnnotation), + SVAttribute(SVAttributeAnnotation), + BlackBoxInline(BlackBoxInlineAnnotation), + BlackBoxPath(BlackBoxPathAnnotation), + DocString(DocStringAnnotation), + CustomFirrtl(CustomFirrtlAnnotation), + } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -212,10 +264,70 @@ impl IntoAnnotations for &'_ mut Annotation { } } -impl> IntoAnnotations for T { - type IntoAnnotations = Self; +pub struct IterIntoAnnotations> { + outer: T, + inner: Option<<::IntoAnnotations as IntoIterator>::IntoIter>, +} - fn into_annotations(self) -> Self::IntoAnnotations { - self +impl> Iterator for IterIntoAnnotations { + type Item = Annotation; + + fn next(&mut self) -> Option { + loop { + if let Some(inner) = &mut self.inner { + let Some(retval) = inner.next() else { + self.inner = None; + continue; + }; + return Some(retval); + } else { + self.inner = Some(self.outer.next()?.into_annotations().into_iter()); + } + } + } + + fn size_hint(&self) -> (usize, Option) { + if let (0, Some(0)) = self.outer.size_hint() { + self.inner + .as_ref() + .map(|v| v.size_hint()) + .unwrap_or((0, Some(0))) + } else { + ( + self.inner.as_ref().map(|v| v.size_hint().0).unwrap_or(0), + None, + ) + } + } + + fn fold(self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + self.inner + .into_iter() + .chain(self.outer.map(|v| v.into_annotations().into_iter())) + .flatten() + .fold(init, f) + } +} + +impl< + T: FusedIterator< + Item: IntoAnnotations>, + >, + > FusedIterator for IterIntoAnnotations +{ +} + +impl> IntoAnnotations for T { + type IntoAnnotations = IterIntoAnnotations; + + fn into_annotations(self) -> Self::IntoAnnotations { + IterIntoAnnotations { + outer: self.into_iter(), + inner: None, + } } } diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 0be61b5..5a2cc00 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -4,7 +4,7 @@ use crate::{ annotations::{ Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, SVAttributeAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, }, array::Array, bundle::{Bundle, BundleField, BundleType}, @@ -1805,7 +1805,7 @@ impl<'a> Exporter<'a> { } fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) { let data = match annotation { - Annotation::DontTouch => AnnotationData::DontTouch, + Annotation::DontTouch(DontTouchAnnotation {}) => AnnotationData::DontTouch, Annotation::SVAttribute(SVAttributeAnnotation { text }) => { AnnotationData::AttributeAnnotation { description: *text } } diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 77079dd..1165a46 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -4,7 +4,7 @@ use crate::{ annotations::{ Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, SVAttributeAnnotation, TargetedAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation, }, array::ArrayType, bundle::{Bundle, BundleField, BundleType}, diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 16dccb9..c793775 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information pub use crate::{ - annotations::Annotation, + annotations::{ + BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, + }, array::{Array, ArrayType}, cli::Cli, clock::{Clock, ClockDomain, ToClock}, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 70b3f56..856cbf0 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,16 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - annotations::{ - BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, SVAttributeAnnotation, - }, - assert_export_firrtl, - firrtl::ExportOptions, - intern::Intern, - module::transform::simplify_enums::SimplifyEnumsKind, - prelude::*, - ty::StaticType, + assert_export_firrtl, firrtl::ExportOptions, intern::Intern, + module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, ty::StaticType, }; use serde_json::json; @@ -3057,30 +3049,30 @@ circuit check_memory_of_array_of_enum: #[hdl_module(outline_generated)] pub fn check_annotations() { - m.annotate_module(Annotation::CustomFirrtl(CustomFirrtlAnnotation { + m.annotate_module(CustomFirrtlAnnotation { class: "the.annotation.Example".intern(), additional_fields: json!({ "bar": "a nice module!", }) .try_into() .unwrap(), - })); - m.annotate_module(Annotation::DocString(DocStringAnnotation { + }); + m.annotate_module(DocStringAnnotation { text: r"This module is used as a test that fayalite's firrtl backend properly emits annotations. Testing... " .intern(), - })); + }); #[hdl] let raddr: UInt<8> = m.input(); - annotate(raddr, Annotation::DontTouch); + annotate(raddr, DontTouchAnnotation); #[hdl] let rdata: Array, 2> = m.output(); annotate( rdata, - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "the.annotation.ExampleClass".intern(), additional_fields: json!({ "foo": "bar", @@ -3088,7 +3080,7 @@ Testing... }) .try_into() .unwrap(), - }), + }, ); #[hdl] let waddr: UInt<8> = m.input(); @@ -3098,21 +3090,21 @@ Testing... let wmask: Array = m.input(); annotate( wmask[1], - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "some.annotation.Class".intern(), additional_fields: json!({ "baz": "second mask bit", }) .try_into() .unwrap(), - }), + }, ); #[hdl] let clk: Clock = m.input(); #[hdl] let mut mem = memory(); mem.depth(0x100); - mem.annotate(Annotation::CustomFirrtl(CustomFirrtlAnnotation { + mem.annotate(CustomFirrtlAnnotation { class: "the.annotation.ExampleClass2".intern(), additional_fields: json!({ "bar": "foo", @@ -3120,18 +3112,18 @@ Testing... }) .try_into() .unwrap(), - })); + }); let read_port = mem.new_read_port(); annotate( read_port, - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "the.annotation.ExampleClass3".intern(), additional_fields: json!({ "foo": "my read port", }) .try_into() .unwrap(), - }), + }, ); connect_any(read_port.addr, raddr); connect(read_port.en, true); @@ -3140,14 +3132,14 @@ Testing... let write_port = mem.new_write_port(); annotate( write_port.data[0], - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "some.annotation.Class".intern(), additional_fields: json!({ "baz": "first mask bit", }) .try_into() .unwrap(), - }), + }, ); connect_any(write_port.addr, waddr); connect(write_port.en, true); @@ -3157,35 +3149,35 @@ Testing... #[hdl_module(extern)] fn black_box1() { m.verilog_name("BlackBox1"); - m.annotate_module(Annotation::BlackBoxInline(BlackBoxInlineAnnotation { + m.annotate_module(BlackBoxInlineAnnotation { path: "black_box1.v".intern(), text: r"(* blackbox *) module BlackBox1(); endmodule " .intern(), - })); + }); } #[hdl] let black_box1_instance = instance(black_box1()); - annotate(black_box1_instance, Annotation::DontTouch); + annotate(black_box1_instance, DontTouchAnnotation); #[hdl_module(extern)] fn black_box2() { m.verilog_name("BlackBox2"); - m.annotate_module(Annotation::BlackBoxPath(BlackBoxPathAnnotation { + m.annotate_module(BlackBoxPathAnnotation { path: "black_box2.v".intern(), - })); + }); } #[hdl] let black_box2_instance = instance(black_box2()); - annotate(black_box2_instance, Annotation::DontTouch); + annotate(black_box2_instance, DontTouchAnnotation); #[hdl] let a_wire: (SInt<1>, Bool) = wire(); annotate( a_wire.1, - Annotation::SVAttribute(SVAttributeAnnotation { + SVAttributeAnnotation { text: "custom_sv_attr = \"abc\"".intern(), - }), + }, ); connect(a_wire, (0_hdl_i1, false)); } diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 7064df2..09ae23f 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1163,7 +1163,7 @@ "Annotation": { "data": { "$kind": "Enum", - "DontTouch": null, + "DontTouch": "Visible", "SVAttribute": "Visible", "BlackBoxInline": "Visible", "BlackBoxPath": "Visible", @@ -1171,6 +1171,11 @@ "CustomFirrtl": "Visible" } }, + "DontTouchAnnotation": { + "data": { + "$kind": "Struct" + } + }, "SVAttributeAnnotation": { "data": { "$kind": "Struct",