marlin_verilog_macro_builder/
lib.rs1use std::{collections::HashMap, path::Path};
8
9use marlin_verilator::PortDirection;
10use proc_macro2::TokenStream;
11use quote::{format_ident, quote};
12use sv_parser::{self as sv, Locate, RefNode, unwrap_node};
13
14mod util;
15
16pub struct MacroArgs {
17 pub source_path: syn::LitStr,
18 pub name: syn::LitStr,
19
20 pub clock_port: Option<syn::LitStr>,
21 pub reset_port: Option<syn::LitStr>,
22}
23
24impl syn::parse::Parse for MacroArgs {
25 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
26 syn::custom_keyword!(src);
27 syn::custom_keyword!(name);
28
29 syn::custom_keyword!(clock);
30 syn::custom_keyword!(reset);
31 input.parse::<src>()?;
32 input.parse::<syn::Token![=]>()?;
33 let source_path = input.parse::<syn::LitStr>()?;
34
35 input.parse::<syn::Token![,]>()?;
36
37 input.parse::<name>()?;
38 input.parse::<syn::Token![=]>()?;
39 let name = input.parse::<syn::LitStr>()?;
40
41 let mut clock_port = None;
42 let mut reset_port = None;
43 while input.peek(syn::Token![,]) {
44 input.parse::<syn::Token![,]>()?;
45
46 let lookahead = input.lookahead1();
47 if lookahead.peek(clock) {
48 input.parse::<clock>()?;
49 input.parse::<syn::Token![=]>()?;
50 clock_port = Some(input.parse::<syn::LitStr>()?);
51 } else if lookahead.peek(reset) {
52 input.parse::<reset>()?;
53 input.parse::<syn::Token![=]>()?;
54 reset_port = Some(input.parse::<syn::LitStr>()?);
55 } else {
56 return Err(lookahead.error());
57 }
58 }
59
60 Ok(Self {
61 source_path,
62 name,
63 clock_port,
64 reset_port,
65 })
66 }
67}
68
69pub fn build_verilated_struct(
70 macro_name: &str,
71 top_name: syn::LitStr,
72 source_path: syn::LitStr,
73 verilog_ports: Vec<(String, usize, usize, PortDirection)>,
74 clock_port: Option<syn::LitStr>,
75 reset_port: Option<syn::LitStr>,
76 item: TokenStream,
77) -> TokenStream {
78 let crate_name = format_ident!("{}", macro_name);
79 let item = match syn::parse::<syn::ItemStruct>(item.into()) {
80 Ok(item) => item,
81 Err(error) => {
82 return error.into_compile_error();
83 }
84 };
85
86 let mut struct_members = vec![];
87
88 let mut preeval_impl = vec![];
89 let mut posteval_impl = vec![];
90
91 let mut other_impl = vec![];
92
93 let mut verilated_model_ports_impl = vec![];
94 let mut verilated_model_init_impl = vec![];
95 let mut verilated_model_init_self = vec![];
96
97 verilated_model_init_impl.push(quote! {
98 let new_model: extern "C" fn() -> *mut #crate_name::__reexports::libc::c_void =
99 *unsafe { library.get(concat!("ffi_new_V", #top_name).as_bytes()) }
100 .expect("failed to get symbol");
101 let model = (new_model)();
102
103
104 let delete_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) =
105 *unsafe { library.get(concat!("ffi_delete_V", #top_name).as_bytes()) }
106 .expect("failed to get symbol");
107
108 let eval_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) =
109 *unsafe { library.get(concat!("ffi_V", #top_name, "_eval").as_bytes()) }
110 .expect("failed to get symbol");
111 });
112 verilated_model_init_self.push(quote! {
113 drop_model: delete_model,
114 eval_model,
115 model,
116 _phantom: std::marker::PhantomData
117 });
118
119 for (port_name, port_msb, port_lsb, port_direction) in verilog_ports {
120 if port_name.chars().any(|c| c == '\\' || c == ' ') {
121 return syn::Error::new_spanned(
122 top_name,
123 "Escaped module names are not supported",
124 )
125 .into_compile_error();
126 }
127
128 let port_width = port_msb + 1 - port_lsb;
129
130 let port_type = if port_width <= 8 {
131 quote! { #crate_name::__reexports::verilator::types::CData }
132 } else if port_width <= 16 {
133 quote! { #crate_name::__reexports::verilator::types::SData }
134 } else if port_width <= 32 {
135 quote! { #crate_name::__reexports::verilator::types::IData }
136 } else if port_width <= 64 {
137 quote! { #crate_name::__reexports::verilator::types::QData }
138 } else {
139 return syn::Error::new_spanned(
140 source_path,
141 format!(
142 "Port `{}` is wider than supported right now",
143 port_name
144 ),
145 )
146 .into_compile_error();
147 };
148
149 let port_name_ident = format_ident!("{}", port_name);
150 struct_members.push(quote! {
151 pub #port_name_ident: #port_type
152 });
153 verilated_model_init_self.push(quote! {
154 #port_name_ident: 0 as _
155 });
156
157 match port_direction {
158 PortDirection::Input => {
159 let setter = format_ident!("pin_{}", port_name);
160 struct_members.push(quote! {
161 #setter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void, #port_type)
162 });
163 preeval_impl.push(quote! {
164 (self.#setter)(self.model, self.#port_name_ident);
165 });
166
167 if let Some(clock_port) = &clock_port {
168 if clock_port.value().as_str() == port_name {
169 other_impl.push(quote! {
170 pub fn tick(&mut self) {
171 self.#port_name = 1 as _;
172 self.eval();
173 self.#port_name = 0 as _;
174 self.eval();
175 }
176 });
177 }
178 }
179
180 if let Some(_reset_port) = &reset_port {
181 todo!("reset ports");
182 }
183
184 verilated_model_init_impl.push(quote! {
185 let #setter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void, #port_type) =
186 *unsafe { library.get(concat!("ffi_V", #top_name, "_pin_", #port_name).as_bytes()) }
187 .expect("failed to get symbol");
188 });
189 verilated_model_init_self.push(quote! { #setter });
190 }
191 PortDirection::Output => {
192 let getter = format_ident!("read_{}", port_name);
193 struct_members.push(quote! {
194 #getter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) -> #port_type
195 });
196 posteval_impl.push(quote! {
197 self.#port_name_ident = (self.#getter)(self.model);
198 });
199
200 verilated_model_init_impl.push(quote! {
201 let #getter: extern "C" fn(*mut #crate_name::__reexports::libc::c_void) -> #port_type =
202 *unsafe { library.get(concat!("ffi_V", #top_name, "_read_", #port_name).as_bytes()) }
203 .expect("failed to get symbol");
204 });
205 verilated_model_init_self.push(quote! { #getter });
206 }
207 _ => todo!("Unhandled port direction"),
208 }
209
210 let verilated_model_port_direction = match port_direction {
211 PortDirection::Input => {
212 quote! { #crate_name::__reexports::verilator::PortDirection::Input }
213 }
214 PortDirection::Output => {
215 quote! { #crate_name::__reexports::verilator::PortDirection::Output }
216 }
217 _ => todo!("Other port directions"),
218 };
219
220 verilated_model_ports_impl.push(quote! {
221 (#port_name, #port_msb, #port_lsb, #verilated_model_port_direction)
222 });
223 }
224
225 struct_members.push(quote! {
226 drop_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void),
227 eval_model: extern "C" fn(*mut #crate_name::__reexports::libc::c_void)
228 });
229
230 let struct_name = item.ident;
231 let vis = item.vis;
232 let port_count = verilated_model_ports_impl.len();
233 quote! {
234 #vis struct #struct_name<'ctx> {
235 #(#struct_members),*,
236 #[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."]
237 model: *mut #crate_name::__reexports::libc::c_void,
238 _phantom: std::marker::PhantomData<&'ctx ()>
239 }
240
241 impl #struct_name<'_> {
242 pub fn eval(&mut self) {
243 #(#preeval_impl)*
244 (self.eval_model)(self.model);
245 #(#posteval_impl)*
246 }
247
248 #(#other_impl)*
249 }
250
251 impl<'ctx> std::ops::Drop for #struct_name<'ctx> {
252 fn drop(&mut self) {
253 (self.drop_model)(self.model);
254 self.model = std::ptr::null_mut();
255 }
256 }
257
258 impl<'ctx> #crate_name::__reexports::verilator::VerilatedModel for #struct_name<'ctx> {
259 fn name() -> &'static str {
260 #top_name
261 }
262
263 fn source_path() -> &'static str {
264 #source_path
265 }
266
267 fn ports() -> &'static [(&'static str, usize, usize, #crate_name::__reexports::verilator::PortDirection)] {
268 static PORTS: [(&'static str, usize, usize, #crate_name::__reexports::verilator::PortDirection); #port_count] = [#(#verilated_model_ports_impl),*];
269 &PORTS
270 }
271
272 fn init_from(library: &#crate_name::__reexports::libloading::Library) -> Self {
273 #(#verilated_model_init_impl)*
274 Self {
275 #(#verilated_model_init_self),*
276 }
277 }
278 }
279 }
280}
281
282pub fn parse_verilog_ports(
283 top_name: &syn::LitStr,
284 source_path: &syn::LitStr,
285 verilog_source_path: &Path,
286) -> Result<Vec<(String, usize, usize, PortDirection)>, proc_macro2::TokenStream>
287{
288 let defines = HashMap::new();
289 let (ast, _) =
290 match sv::parse_sv(verilog_source_path, &defines, &["."], false, false)
291 {
292 Ok(result) => result,
293 Err(error) => {
294 return Err(syn::Error::new_spanned(
295 source_path,
296 error.to_string(),
297 )
298 .into_compile_error());
299 }
300 };
301
302 let Some(module) = (&ast).into_iter().find_map(|node| match node {
303 RefNode::ModuleDeclarationAnsi(module) => {
304 fn get_identifier(node: RefNode) -> Option<Locate> {
306 match unwrap_node!(node, SimpleIdentifier, EscapedIdentifier) {
307 Some(RefNode::SimpleIdentifier(x)) => Some(x.nodes.0),
308 Some(RefNode::EscapedIdentifier(x)) => Some(x.nodes.0),
309 _ => None,
310 }
311 }
312
313 let id = unwrap_node!(module, ModuleIdentifier).unwrap();
314 let id = get_identifier(id).unwrap();
315 let id = ast.get_str_trim(&id).unwrap();
316 if id == top_name.value().as_str() {
317 Some(module)
318 } else {
319 None
320 }
321 }
322 _ => None,
323 }) else {
324 return Err(syn::Error::new_spanned(
325 top_name,
326 format!(
327 "Could not find module declaration for `{}` in {}",
328 top_name.value(),
329 source_path.value()
330 ),
331 )
332 .into_compile_error());
333 };
334
335 let port_declarations_list = module
336 .nodes
337 .0
338 .nodes
339 .6
340 .as_ref()
341 .and_then(|list| list.nodes.0.nodes.1.as_ref())
342 .map(|list| list.contents())
343 .unwrap_or(vec![]);
344
345 let mut ports = vec![];
346 for (_, port) in port_declarations_list {
347 match port {
348 sv::AnsiPortDeclaration::Net(net) => {
349 let port_name = ast.get_str_trim(&net.nodes.1.nodes.0).expect(
350 "Port identifier could not be traced back to source code",
351 );
352
353 if port_name.chars().any(|c| c == '\\' || c == ' ') {
354 return Err(syn::Error::new_spanned(
355 top_name,
356 "Escaped module names are not supported",
357 )
358 .into_compile_error());
359 }
360
361 let Some((port_direction, port_type ))= net.nodes.0.as_ref().and_then(|maybe_net_header| match maybe_net_header {
362 sv::NetPortHeaderOrInterfacePortHeader::NetPortHeader(net_port_header) => {
363 net_port_header.nodes.0.as_ref().map(|port_direction| (port_direction, &net_port_header.nodes.1))
364 },
365 _ => todo!("Other port header")
366 }) else {
367 return Err(syn::Error::new_spanned(
368 source_path,
369 format!(
370 "Port `{}` has no supported direction (`input` or `output`)",
371 port_name
372 ),
373 )
374 .into_compile_error())
375 };
376
377 let port_dimensions = match port_type {
378 sv::NetPortType::DataType(net_port_type_data_type) => {
379 match &net_port_type_data_type.nodes.1 {
380 sv::DataTypeOrImplicit::DataType(data_type) => {
381 match &**data_type {
382 sv::DataType::Vector(data_type_vector) => {
383 &data_type_vector.nodes.2
384 }
385 other => todo!(
386 "Unsupported data type {:?}",
387 other
388 ),
389 }
390 }
391 sv::DataTypeOrImplicit::ImplicitDataType(
392 implicit_data_type,
393 ) => &implicit_data_type.nodes.1,
394 }
395 }
396 sv::NetPortType::NetTypeIdentifier(
397 _net_type_identifier,
398 ) => todo!("bklk"),
399 sv::NetPortType::Interconnect(
400 _net_port_type_interconnect,
401 ) => todo!("ckl"),
402 };
403
404 let (port_msb, port_lsb) = match port_dimensions.len() {
405 0 => (0, 0),
406 1 => match &port_dimensions[0] {
407 sv::PackedDimension::Range(packed_dimension_range) => {
408 let range =
409 &packed_dimension_range.nodes.0.nodes.1.nodes;
410 (
411 util::evaluate_numeric_constant_expression(
412 &ast, &range.0,
413 ),
414 util::evaluate_numeric_constant_expression(
415 &ast, &range.2,
416 ),
417 )
418 }
419 _ => todo!(),
420 },
421 _ => todo!("Don't support multidimensional ports yet"),
422 };
423
424 let port_direction = match port_direction {
425 sv::PortDirection::Input(_) => PortDirection::Input,
426 sv::PortDirection::Output(_) => PortDirection::Output,
427 sv::PortDirection::Inout(_) => PortDirection::Inout,
428 sv::PortDirection::Ref(_) => todo!(),
429 };
430
431 ports.push((
432 port_name.to_string(),
433 port_msb,
434 port_lsb,
435 port_direction,
436 ));
437 }
438 _ => todo!("Other types of ports"),
439 }
440 }
441
442 Ok(ports)
443}