marlin_verilog_macro_builder/
lib.rs1use marlin_verilator::PortDirection;
8use proc_macro2::TokenStream;
9use quote::{format_ident, quote};
10
11pub struct MacroArgs {
12 pub source_path: syn::LitStr,
13 pub name: syn::LitStr,
14
15 pub clock_port: Option<syn::LitStr>,
16 pub reset_port: Option<syn::LitStr>,
17}
18
19impl syn::parse::Parse for MacroArgs {
20 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
21 syn::custom_keyword!(src);
22 syn::custom_keyword!(name);
23
24 syn::custom_keyword!(clock);
25 syn::custom_keyword!(reset);
26 input.parse::<src>()?;
27 input.parse::<syn::Token![=]>()?;
28 let source_path = input.parse::<syn::LitStr>()?;
29
30 input.parse::<syn::Token![,]>()?;
31
32 input.parse::<name>()?;
33 input.parse::<syn::Token![=]>()?;
34 let name = input.parse::<syn::LitStr>()?;
35
36 let mut clock_port = None;
37 let mut reset_port = None;
38 while input.peek(syn::Token![,]) {
39 input.parse::<syn::Token![,]>()?;
40
41 let lookahead = input.lookahead1();
42 if lookahead.peek(clock) {
43 input.parse::<clock>()?;
44 input.parse::<syn::Token![=]>()?;
45 clock_port = Some(input.parse::<syn::LitStr>()?);
46 } else if lookahead.peek(reset) {
47 input.parse::<reset>()?;
48 input.parse::<syn::Token![=]>()?;
49 reset_port = Some(input.parse::<syn::LitStr>()?);
50 } else {
51 return Err(lookahead.error());
52 }
53 }
54
55 Ok(Self {
56 source_path,
57 name,
58 clock_port,
59 reset_port,
60 })
61 }
62}
63
64pub fn build_verilated_struct(
65 macro_name: &str,
66 top_name: syn::LitStr,
67 source_path: syn::LitStr,
68 verilog_ports: Vec<(String, usize, usize, PortDirection)>,
69 clock_port: Option<syn::LitStr>,
70 reset_port: Option<syn::LitStr>,
71 item: TokenStream,
72) -> TokenStream {
73 let crate_name = format_ident!("{}", macro_name);
74 let item = match syn::parse::<syn::ItemStruct>(item.into()) {
75 Ok(item) => item,
76 Err(error) => {
77 return error.into_compile_error();
78 }
79 };
80
81 let mut struct_members = vec![];
82
83 let mut preeval_impl = vec![];
84 let mut posteval_impl = vec![];
85
86 let mut other_impl = vec![];
87
88 let mut verilated_model_ports_impl = vec![];
89 let mut verilated_model_init_impl = vec![];
90 let mut verilated_model_init_self = vec![];
91
92 verilated_model_init_impl.push(quote! {
93 let new_model: extern "C" fn() -> *mut #crate_name::__reexports::libc::c_void =
94 *unsafe { library.get(concat!("ffi_new_V", #top_name).as_bytes()) }
95 .expect("failed to get symbol");
96 let model = (new_model)();
97
98
99 let delete_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) =
100 *unsafe { library.get(concat!("ffi_delete_V", #top_name).as_bytes()) }
101 .expect("failed to get symbol");
102
103 let eval_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) =
104 *unsafe { library.get(concat!("ffi_V", #top_name, "_eval").as_bytes()) }
105 .expect("failed to get symbol");
106 });
107 verilated_model_init_self.push(quote! {
108 drop_model: delete_model,
109 eval_model,
110 model,
111 _phantom: std::marker::PhantomData
112 });
113
114 for (port_name, port_msb, port_lsb, port_direction) in verilog_ports {
115 if port_name.chars().any(|c| c == '\\' || c == ' ') {
116 return syn::Error::new_spanned(
117 top_name,
118 "Escaped module names are not supported",
119 )
120 .into_compile_error();
121 }
122
123 let port_width = port_msb + 1 - port_lsb;
124
125 let port_type = if port_width <= 8 {
126 quote! { #crate_name::__reexports::verilator::types::CData }
127 } else if port_width <= 16 {
128 quote! { #crate_name::__reexports::verilator::types::SData }
129 } else if port_width <= 32 {
130 quote! { #crate_name::__reexports::verilator::types::IData }
131 } else if port_width <= 64 {
132 quote! { #crate_name::__reexports::verilator::types::QData }
133 } else {
134 return syn::Error::new_spanned(
135 source_path,
136 format!(
137 "Port `{}` is wider than supported right now",
138 port_name
139 ),
140 )
141 .into_compile_error();
142 };
143
144 let port_name_ident = format_ident!("{}", port_name);
145 struct_members.push(quote! {
146 pub #port_name_ident: #port_type
147 });
148 verilated_model_init_self.push(quote! {
149 #port_name_ident: 0 as _
150 });
151
152 match port_direction {
153 PortDirection::Input => {
154 let setter = format_ident!("pin_{}", port_name);
155 struct_members.push(quote! {
156 #setter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void, #port_type)
157 });
158 preeval_impl.push(quote! {
159 (self.#setter)(self.model, self.#port_name_ident);
160 });
161
162 if let Some(clock_port) = &clock_port {
163 if clock_port.value().as_str() == port_name {
164 other_impl.push(quote! {
165 pub fn tick(&mut self) {
166 self.#port_name = 1 as _;
167 self.eval();
168 self.#port_name = 0 as _;
169 self.eval();
170 }
171 });
172 }
173 }
174
175 if let Some(_reset_port) = &reset_port {
176 todo!("reset ports");
177 }
178
179 verilated_model_init_impl.push(quote! {
180 let #setter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void, #port_type) =
181 *unsafe { library.get(concat!("ffi_V", #top_name, "_pin_", #port_name).as_bytes()) }
182 .expect("failed to get symbol");
183 });
184 verilated_model_init_self.push(quote! { #setter });
185 }
186 PortDirection::Output => {
187 let getter = format_ident!("read_{}", port_name);
188 struct_members.push(quote! {
189 #getter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) -> #port_type
190 });
191 posteval_impl.push(quote! {
192 self.#port_name_ident = (self.#getter)(self.model);
193 });
194
195 verilated_model_init_impl.push(quote! {
196 let #getter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) -> #port_type =
197 *unsafe { library.get(concat!("ffi_V", #top_name, "_read_", #port_name).as_bytes()) }
198 .expect("failed to get symbol");
199 });
200 verilated_model_init_self.push(quote! { #getter });
201 }
202 _ => todo!("Unhandled port direction"),
203 }
204
205 let verilated_model_port_direction = match port_direction {
206 PortDirection::Input => {
207 quote! { #crate_name::__reexports::verilator::PortDirection::Input }
208 }
209 PortDirection::Output => {
210 quote! { #crate_name::__reexports::verilator::PortDirection::Output }
211 }
212 _ => todo!("Other port directions"),
213 };
214
215 verilated_model_ports_impl.push(quote! {
216 (#port_name, #port_msb, #port_lsb, #verilated_model_port_direction)
217 });
218 }
219
220 struct_members.push(quote! {
221 drop_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void),
222 eval_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void)
223 });
224
225 let struct_name = item.ident;
226 let vis = item.vis;
227 let port_count = verilated_model_ports_impl.len();
228 quote! {
229 #vis struct #struct_name<'ctx> {
230 #(#struct_members),*,
231 #[doc = "# Safety\nThe Rust binding to the model will not outlive the dynamic library context (with lifetime `'ctx`) and is dropped when this struct is."]
232 model: *mut #crate_name::__reexports::libc::c_void,
233 _phantom: std::marker::PhantomData<&'ctx ()>
234 }
235
236 impl #struct_name<'_> {
237 pub fn eval(&mut self) {
238 #(#preeval_impl)*
239 (self.eval_model)(self.model);
240 #(#posteval_impl)*
241 }
242
243 #(#other_impl)*
244 }
245
246 impl<'ctx> std::ops::Drop for #struct_name<'ctx> {
247 fn drop(&mut self) {
248 (self.drop_model)(self.model);
249 self.model = std::ptr::null_mut();
250 }
251 }
252
253 impl<'ctx> #crate_name::__reexports::verilator::VerilatedModel for #struct_name<'ctx> {
254 fn name() -> &'static str {
255 #top_name
256 }
257
258 fn source_path() -> &'static str {
259 #source_path
260 }
261
262 fn ports() -> &'static [(&'static str, usize, usize, #crate_name::__reexports::verilator::PortDirection)] {
263 static PORTS: [(&'static str, usize, usize, #crate_name::__reexports::verilator::PortDirection); #port_count] = [#(#verilated_model_ports_impl),*];
264 &PORTS
265 }
266
267 fn init_from(library: &#crate_name::__reexports::libloading::Library) -> Self {
268 #(#verilated_model_init_impl)*
269 Self {
270 #(#verilated_model_init_self),*
271 }
272 }
273 }
274 }
275}