svd2rust/generate/
interrupt.rs1use std::collections::HashMap;
2use std::fmt::Write;
3
4use crate::config::RustEdition;
5use crate::svd::Peripheral;
6use proc_macro2::{Span, TokenStream};
7use quote::quote;
8
9use crate::util::{self, ident};
10use crate::{Config, Target};
11use anyhow::Result;
12
13pub fn render(
15 target: Target,
16 peripherals: &[Peripheral],
17 device_x: &mut String,
18 config: &Config,
19) -> Result<TokenStream> {
20 let interrupts = peripherals
21 .iter()
22 .flat_map(|p| {
23 p.interrupt.iter().map(move |i| {
24 (i, p.group_name.clone(), {
25 match p {
26 Peripheral::Single(info) => info.name.clone(),
27 Peripheral::Array(info, dim_element) => {
28 svd_rs::array::names(info, dim_element).next().unwrap()
29 }
30 }
31 })
32 })
33 })
34 .map(|i| (i.0.value, (i.0, i.1, i.2)))
35 .collect::<HashMap<_, _>>();
36
37 let mut interrupts = interrupts.into_values().collect::<Vec<_>>();
38 interrupts.sort_by_key(|i| i.0.value);
39
40 let mut root = TokenStream::new();
41 let mut from_arms = TokenStream::new();
42 let mut elements = TokenStream::new();
43 let mut names = vec![];
44 let mut names_cfg_attr = vec![];
45 let mut variants = TokenStream::new();
46
47 let mut pos = 0;
49 let mut mod_items = TokenStream::new();
50 let span = Span::call_site();
51 let feature_format = config.ident_formats.get("peripheral_feature").unwrap();
52 for interrupt in &interrupts {
53 while pos < interrupt.0.value {
54 elements.extend(quote!(Vector { _reserved: 0 },));
55 pos += 1;
56 }
57 pos += 1;
58
59 let i_ty = ident(&interrupt.0.name, config, "interrupt", span);
60 let description = format!(
61 "{} - {}",
62 interrupt.0.value,
63 interrupt
64 .0
65 .description
66 .as_deref()
67 .map(util::respace)
68 .as_deref()
69 .map(util::escape_special_chars)
70 .unwrap_or_else(|| interrupt.0.name.as_str().into())
71 );
72
73 let value = util::unsuffixed(interrupt.0.value);
74
75 let mut feature_attribute_flag = false;
76 let mut feature_attribute = TokenStream::new();
77 let mut not_feature_attribute = TokenStream::new();
78 if config.feature_group && interrupt.1.is_some() {
79 let feature_name = feature_format.apply(interrupt.1.as_ref().unwrap());
80 feature_attribute_flag = true;
81 feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] });
82 not_feature_attribute.extend(quote! { feature = #feature_name, });
83 }
84 if config.feature_peripheral {
85 let feature_name = feature_format.apply(&interrupt.2);
86 feature_attribute_flag = true;
87 feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] });
88 not_feature_attribute.extend(quote! { feature = #feature_name, });
89 }
90 let not_feature_attribute = quote! { #[cfg(not(all(#not_feature_attribute)))] };
91
92 variants.extend(quote! {
93 #[doc = #description]
94 #feature_attribute
95 #i_ty = #value,
96 });
97
98 from_arms.extend(quote! {
99 #feature_attribute
100 #value => Ok(Interrupt::#i_ty),
101 });
102
103 if feature_attribute_flag {
104 elements.extend(quote! {
105 #not_feature_attribute
106 Vector { _reserved: 0 },
107 #feature_attribute
108 Vector { _handler: #i_ty },
109 });
110 } else {
111 elements.extend(quote!(Vector { _handler: #i_ty },));
112 }
113 names.push(i_ty);
114 names_cfg_attr.push(feature_attribute);
115 }
116
117 let (nomangle, xtern) = if config.edition >= RustEdition::E2024 {
118 (quote!(#[unsafe(no_mangle)]), quote!(unsafe extern))
119 } else {
120 (quote!(#[no_mangle]), quote!(extern))
121 };
122
123 let n = util::unsuffixed(pos);
124 match target {
125 Target::CortexM => {
126 for name in &names {
127 writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?;
128 }
129
130 let link_section_name = config
131 .interrupt_link_section
132 .as_deref()
133 .unwrap_or(".vector_table.interrupts");
134 let link_section_attr = if config.edition >= RustEdition::E2024 {
135 quote!(#[unsafe(link_section = #link_section_name)])
136 } else {
137 quote!(#[link_section = #link_section_name])
138 };
139
140 root.extend(quote! {
141 #[cfg(feature = "rt")]
142 #xtern "C" {
143 #(#names_cfg_attr fn #names();)*
144 }
145
146 #[doc(hidden)]
147 #[repr(C)]
148 pub union Vector {
149 _handler: unsafe extern "C" fn(),
150 _reserved: u32,
151 }
152
153 #[cfg(feature = "rt")]
154 #[doc(hidden)]
155 #link_section_attr
156 #nomangle
157 pub static __INTERRUPTS: [Vector; #n] = [
158 #elements
159 ];
160 });
161 }
162 Target::Msp430 => {
163 for name in &names {
164 writeln!(device_x, "PROVIDE({name} = DefaultHandler);").unwrap();
165 }
166
167 let link_section_name = config
168 .interrupt_link_section
169 .as_deref()
170 .unwrap_or(".vector_table.interrupts");
171 let link_section_attr = if config.edition >= RustEdition::E2024 {
172 quote!(#[unsafe(link_section = #link_section_name)])
173 } else {
174 quote!(#[link_section = #link_section_name])
175 };
176
177 root.extend(quote! {
178 #[cfg(feature = "rt")]
179 extern "msp430-interrupt" {
180 #(#names_cfg_attr fn #names();)*
181 }
182
183 #[doc(hidden)]
184 #[repr(C)]
185 pub union Vector {
186 _handler: unsafe extern "msp430-interrupt" fn(),
187 _reserved: u16,
188 }
189
190 #[cfg(feature = "rt")]
191 #[doc(hidden)]
192 #link_section_attr
193 #nomangle
194 #[used]
195 pub static __INTERRUPTS:
196 [Vector; #n] = [
197 #elements
198 ];
199 });
200 }
201 Target::RISCV => {
202 for name in &names {
203 writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?;
204 }
205
206 let link_section_attr = config.interrupt_link_section.as_ref().map(|section| {
207 if config.edition >= RustEdition::E2024 {
208 quote!(#[unsafe(link_section = #section)])
209 } else {
210 quote!(#[link_section = #section])
211 }
212 });
213
214 root.extend(quote! {
215 #[cfg(feature = "rt")]
216 #xtern "C" {
217 #(#names_cfg_attr fn #names();)*
218 }
219
220 #[doc(hidden)]
221 #[repr(C)]
222 pub union Vector {
223 pub _handler: unsafe extern "C" fn(),
224 pub _reserved: usize,
225 }
226
227 #[cfg(feature = "rt")]
228 #[doc(hidden)]
229 #link_section_attr
230 #nomangle
231 pub static __EXTERNAL_INTERRUPTS: [Vector; #n] = [
232 #elements
233 ];
234 });
235 }
236 Target::XtensaLX => {
237 for name in &names {
238 writeln!(device_x, "PROVIDE({name} = DefaultHandler);")?;
239 }
240
241 let link_section_attr = config.interrupt_link_section.as_ref().map(|section| {
242 if config.edition >= RustEdition::E2024 {
243 quote!(#[unsafe(link_section = #section)])
244 } else {
245 quote!(#[link_section = #section])
246 }
247 });
248
249 root.extend(quote! {
250 #[cfg(feature = "rt")]
251 #xtern "C" {
252 #(#names_cfg_attr fn #names();)*
253 }
254
255 #[doc(hidden)]
256 #[repr(C)]
257 pub union Vector {
258 pub _handler: unsafe extern "C" fn(),
259 _reserved: u32,
260 }
261
262 #[cfg(feature = "rt")]
263 #[doc(hidden)]
264 #link_section_attr
265 #nomangle
266 pub static __INTERRUPTS: [Vector; #n] = [
267 #elements
268 ];
269 });
270 }
271 Target::Mips => {}
272 Target::None => {}
273 }
274
275 let self_token = quote!(self);
276 let (enum_repr, nr_expr) = if variants.is_empty() {
277 (quote!(), quote!(match #self_token {}))
278 } else {
279 (quote!(#[repr(u16)]), quote!(#self_token as u16))
280 };
281
282 let defmt = config
283 .impl_defmt
284 .as_ref()
285 .map(|feature| quote!(#[cfg_attr(feature = #feature, derive(defmt::Format))]));
286
287 if target == Target::Msp430 {
288 let interrupt_enum = quote! {
289 #defmt
291 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
292 #enum_repr
293 pub enum Interrupt {
294 #variants
295 }
296 };
297
298 root.extend(interrupt_enum);
299 } else {
300 let interrupt_enum = quote! {
301 #defmt
303 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
304 #enum_repr
305 pub enum Interrupt {
306 #variants
307 }
308 };
309
310 match target {
311 Target::CortexM => {
312 root.extend(quote! {
313 #interrupt_enum
314
315 unsafe impl cortex_m::interrupt::InterruptNumber for Interrupt {
316 #[inline(always)]
317 fn number(#self_token) -> u16 {
318 #nr_expr
319 }
320 }
321 });
322 }
323 Target::XtensaLX => {
324 root.extend(quote! {
325 #interrupt_enum
326
327 #defmt
329 #[derive(Debug, Copy, Clone)]
330 pub struct TryFromInterruptError(());
331
332 impl Interrupt {
333
334 #[inline]
336 pub fn try_from(value: u16) -> Result<Self, TryFromInterruptError> {
337 match value {
338 #from_arms
339 _ => Err(TryFromInterruptError(())),
340 }
341 }
342 }
343 });
344 }
345 _ => {
346 mod_items.extend(quote! {
347 #interrupt_enum
348
349 #defmt
351 #[derive(Debug, Copy, Clone)]
352 pub struct TryFromInterruptError(());
353
354 impl Interrupt {
355
356 #[inline]
358 pub fn try_from(value: u8) -> Result<Self, TryFromInterruptError> {
359 match value {
360 #from_arms
361 _ => Err(TryFromInterruptError(())),
362 }
363 }
364 }
365 });
366 }
367 }
368 }
369
370 if target != Target::None {
371 let abi = match target {
372 Target::Msp430 => "msp430-interrupt",
373 _ => "C",
374 };
375
376 if target != Target::CortexM
377 && target != Target::Msp430
378 && target != Target::XtensaLX
379 && target != Target::Mips
380 {
381 mod_items.extend(quote! {
382 #[cfg(feature = "rt")]
383 #[macro_export]
384 macro_rules! interrupt {
420 ($NAME:ident, $path:path, locals: {
421 $($lvar:ident:$lty:ty = $lval:expr;)*
422 }) => {
423 #[allow(non_snake_case)]
424 mod $NAME {
425 pub struct Locals {
426 $(
427 pub $lvar: $lty,
428 )*
429 }
430 }
431
432 #[allow(non_snake_case)]
433 #nomangle
434 pub extern #abi fn $NAME() {
435 let _ = $crate::interrupt::Interrupt::$NAME;
437
438 static mut LOCALS: self::$NAME::Locals =
439 self::$NAME::Locals {
440 $(
441 $lvar: $lval,
442 )*
443 };
444
445 let f: fn(&mut self::$NAME::Locals) = $path;
447 f(unsafe { &mut LOCALS });
448 }
449 };
450 ($NAME:ident, $path:path) => {
451 #[allow(non_snake_case)]
452 #nomangle
453 pub extern #abi fn $NAME() {
454 let _ = $crate::interrupt::Interrupt::$NAME;
456
457 let f: fn() = $path;
459 f();
460 }
461 }
462 }
463 });
464 }
465 }
466
467 if !interrupts.is_empty()
468 && target != Target::CortexM
469 && target != Target::XtensaLX
470 && target != Target::Msp430
471 {
472 root.extend(quote! {
473 #[doc(hidden)]
474 pub mod interrupt {
475 #mod_items
476 }
477 });
478
479 root.extend(quote! {
480 pub use self::interrupt::Interrupt;
481 });
482 }
483
484 Ok(root)
485}