1use proc_macro::TokenStream;
4use quote::quote;
5use syn::{DeriveInput, ItemFn, parse_macro_input};
6
7#[proc_macro_attribute]
10pub fn module(_attr: TokenStream, item: TokenStream) -> TokenStream {
11 let input = parse_macro_input!(item as DeriveInput);
12 let name = &input.ident;
13 let vis = &input.vis;
14
15 let syn::Data::Struct(data) = &input.data else {
16 return syn::Error::new_spanned(&input, "rhdl::module only supports structs")
17 .to_compile_error()
18 .into();
19 };
20
21 let mod_name = name.to_string();
22 let field_defs = data.fields.iter().map(|f| {
23 let id = f.ident.as_ref().unwrap();
24 let ty = &f.ty;
25 let fvis = &f.vis;
26 quote! { #fvis #id: #ty }
27 });
28
29 let port_stmts = data.fields.iter().map(|field| {
30 let Some(ident) = &field.ident else {
31 return quote! {
32 compile_error!("tuple structs are not supported by rhdl::module");
33 };
34 };
35 let ty = &field.ty;
36 let name_str = ident.to_string();
37 quote! {
38 {
39 type __PortTy = #ty;
40 let (__dir, __gt) = <__PortTy as ::bitloom_prelude::PortField>::describe();
41 match __dir {
42 ::bitloom_prelude::PortDir::Input => {
43 __session.add_input(#name_str, __gt, ::bitloom_prelude::Span::default());
44 }
45 ::bitloom_prelude::PortDir::Output => {
46 __session.add_output(#name_str, __gt, ::bitloom_prelude::Span::default());
47 }
48 }
49 }
50 }
51 });
52
53 TokenStream::from(quote! {
54 #vis struct #name {
55 #(#field_defs),*
56 }
57
58 impl ::bitloom_prelude::Elaboratable for #name {
59 fn elaborate() -> ::core::result::Result<
60 ::bitloom_prelude::FrozenHir,
61 ::bitloom_prelude::Diagnostics,
62 > {
63 let mut __session = ::bitloom_prelude::ElaborateSession::new(#mod_name);
64 __session.begin_module(#mod_name, ::bitloom_prelude::Span::default());
65 #(#port_stmts)*
66 __session.end_module();
67 __session.finish()
68 }
69 }
70 })
71}
72
73#[proc_macro_attribute]
76pub fn combinational(_attr: TokenStream, item: TokenStream) -> TokenStream {
77 let input = parse_macro_input!(item as ItemFn);
78 let vis = &input.vis;
79 let sig = &input.sig;
80 let block = &input.block;
81 let attrs = &input.attrs;
82 TokenStream::from(quote! {
83 #(#attrs)*
84 #vis #sig {
85 const _: () = ();
87 let __rhdl_process_kind = ::bitloom_prelude::ProcessKindMark::Combinational;
88 let _ = __rhdl_process_kind;
89 #block
90 }
91 })
92}
93
94#[proc_macro_attribute]
96pub fn sequential(_attr: TokenStream, item: TokenStream) -> TokenStream {
97 let input = parse_macro_input!(item as ItemFn);
98 let vis = &input.vis;
99 let sig = &input.sig;
100 let block = &input.block;
101 let attrs = &input.attrs;
102 TokenStream::from(quote! {
103 #(#attrs)*
104 #vis #sig {
105 const _: () = ();
106 let __rhdl_process_kind = ::bitloom_prelude::ProcessKindMark::Sequential;
107 let _ = __rhdl_process_kind;
108 #block
109 }
110 })
111}
112
113#[proc_macro_attribute]
115pub fn process(_attr: TokenStream, item: TokenStream) -> TokenStream {
116 let input = parse_macro_input!(item as ItemFn);
117 syn::Error::new_spanned(
118 input.sig.ident,
119 "hardware processes must use #[combinational] or #[sequential]; bare #[process] is forbidden",
120 )
121 .to_compile_error()
122 .into()
123}
124
125#[proc_macro_attribute]
127pub fn functional_model(_attr: TokenStream, item: TokenStream) -> TokenStream {
128 host_only_view(item, "FunctionalModel")
129}
130
131#[proc_macro_attribute]
134pub fn bridge(_attr: TokenStream, item: TokenStream) -> TokenStream {
135 host_only_view(item, "Bridge")
136}
137
138#[proc_macro_attribute]
140pub fn abstraction(_attr: TokenStream, item: TokenStream) -> TokenStream {
141 host_only_view(item, "Abstraction")
142}
143
144#[proc_macro_attribute]
146pub fn both(_attr: TokenStream, item: TokenStream) -> TokenStream {
147 host_only_view(item, "Both")
148}
149
150#[proc_macro_attribute]
152pub fn hls(_attr: TokenStream, item: TokenStream) -> TokenStream {
153 let input = parse_macro_input!(item as ItemFn);
154 let vis = &input.vis;
155 let sig = &input.sig;
156 let block = &input.block;
157 let attrs = &input.attrs;
158 TokenStream::from(quote! {
159 #(#attrs)*
160 #vis #sig {
161 const _: () = ();
162 let __rhdl_hls = ::bitloom_prelude::HlsMark;
163 let _ = __rhdl_hls;
164 #block
165 }
166 })
167}
168
169fn host_only_view(item: TokenStream, kind: &str) -> TokenStream {
170 let input = parse_macro_input!(item as DeriveInput);
171 let name = &input.ident;
172 let kind_ident = syn::Ident::new(kind, name.span());
173 TokenStream::from(quote! {
174 #input
175
176 impl ::bitloom_prelude::HostView for #name {
177 const KIND: ::bitloom_prelude::ViewKind = ::bitloom_prelude::ViewKind::#kind_ident;
178 }
179 })
181}
182
183#[proc_macro_attribute]
185pub fn top(_attr: TokenStream, item: TokenStream) -> TokenStream {
186 let input = parse_macro_input!(item as DeriveInput);
187 let name = &input.ident;
188 TokenStream::from(quote! {
189 #input
190
191 impl #name {
192 pub const RHDL_TOP: bool = true;
194 }
195 })
196}