aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-02-12 00:11:15 +0100
committerDario Nieuwenhuis <[email protected]>2022-02-12 01:16:31 +0100
commit611961b499f8d4fee4f556c56606a2481e944791 (patch)
tree1380adfe37d29ccd89dc05f19d1999fd67a2f6ad
parent6c925b2342708266f24d58020e89786811531d47 (diff)
macros: cleanup, make work in stable.
-rw-r--r--embassy-macros/src/chip/nrf.rs11
-rw-r--r--embassy-macros/src/chip/rp.rs10
-rw-r--r--embassy-macros/src/chip/stm32.rs11
-rw-r--r--embassy-macros/src/lib.rs534
-rw-r--r--embassy-macros/src/macros/interrupt.rs66
-rw-r--r--embassy-macros/src/macros/interrupt_declare.rs37
-rw-r--r--embassy-macros/src/macros/interrupt_take.rs36
-rw-r--r--embassy-macros/src/macros/main.rs135
-rw-r--r--embassy-macros/src/macros/mod.rs5
-rw-r--r--embassy-macros/src/macros/task.rs90
-rw-r--r--embassy-macros/src/util/ctxt.rs72
-rw-r--r--embassy-macros/src/util/mod.rs2
-rw-r--r--embassy-macros/src/util/path.rs (renamed from embassy-macros/src/path.rs)0
13 files changed, 462 insertions, 547 deletions
diff --git a/embassy-macros/src/chip/nrf.rs b/embassy-macros/src/chip/nrf.rs
deleted file mode 100644
index 3ff6a74cf..000000000
--- a/embassy-macros/src/chip/nrf.rs
+++ /dev/null
@@ -1,11 +0,0 @@
1use crate::path::ModulePrefix;
2use proc_macro2::TokenStream;
3use quote::quote;
4
5pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream {
6 let embassy_nrf_path = embassy_prefix.append("embassy_nrf").path();
7
8 quote!(
9 let p = #embassy_nrf_path::init(#config);
10 )
11}
diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs
deleted file mode 100644
index ba0a97ada..000000000
--- a/embassy-macros/src/chip/rp.rs
+++ /dev/null
@@ -1,10 +0,0 @@
1use crate::path::ModulePrefix;
2use proc_macro2::TokenStream;
3use quote::quote;
4
5pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream {
6 let embassy_rp_path = embassy_prefix.append("embassy_rp").path();
7 quote!(
8 let p = #embassy_rp_path::init(#config);
9 )
10}
diff --git a/embassy-macros/src/chip/stm32.rs b/embassy-macros/src/chip/stm32.rs
deleted file mode 100644
index c6938836c..000000000
--- a/embassy-macros/src/chip/stm32.rs
+++ /dev/null
@@ -1,11 +0,0 @@
1use crate::path::ModulePrefix;
2use proc_macro2::TokenStream;
3use quote::quote;
4
5pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream {
6 let embassy_stm32_path = embassy_prefix.append("embassy_stm32").path();
7
8 quote!(
9 let p = #embassy_stm32_path::init(#config);
10 )
11}
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs
index 44a8d3b93..085f7889d 100644
--- a/embassy-macros/src/lib.rs
+++ b/embassy-macros/src/lib.rs
@@ -1,228 +1,39 @@
1#![feature(proc_macro_diagnostic)]
2
3extern crate proc_macro; 1extern crate proc_macro;
4 2
5use darling::FromMeta;
6use proc_macro::TokenStream; 3use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::{format_ident, quote};
9use std::iter;
10use syn::spanned::Spanned;
11use syn::{parse, Type, Visibility};
12use syn::{ItemFn, ReturnType};
13
14mod path;
15
16use path::ModulePrefix;
17 4
18#[derive(Debug, FromMeta)] 5mod macros;
19struct TaskArgs { 6mod util;
20 #[darling(default)] 7use macros::*;
21 pool_size: Option<usize>,
22 #[darling(default)]
23 send: bool,
24 #[darling(default)]
25 embassy_prefix: ModulePrefix,
26}
27 8
28#[proc_macro_attribute] 9#[proc_macro_attribute]
29pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { 10pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
30 let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); 11 let args = syn::parse_macro_input!(args as syn::AttributeArgs);
31 let mut task_fn = syn::parse_macro_input!(item as syn::ItemFn); 12 let f = syn::parse_macro_input!(item as syn::ItemFn);
32
33 let macro_args = match TaskArgs::from_list(&macro_args) {
34 Ok(v) => v,
35 Err(e) => {
36 return TokenStream::from(e.write_errors());
37 }
38 };
39
40 let embassy_prefix = macro_args.embassy_prefix.append("embassy");
41 let embassy_path = embassy_prefix.path();
42
43 let pool_size: usize = macro_args.pool_size.unwrap_or(1);
44
45 let mut fail = false;
46 if task_fn.sig.asyncness.is_none() {
47 task_fn
48 .sig
49 .span()
50 .unwrap()
51 .error("task functions must be async")
52 .emit();
53 fail = true;
54 }
55 if !task_fn.sig.generics.params.is_empty() {
56 task_fn
57 .sig
58 .span()
59 .unwrap()
60 .error("task functions must not be generic")
61 .emit();
62 fail = true;
63 }
64 if pool_size < 1 {
65 return parse::Error::new(Span::call_site(), "pool_size must be 1 or greater")
66 .to_compile_error()
67 .into();
68 }
69
70 let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> =
71 syn::punctuated::Punctuated::new();
72 let mut args = task_fn.sig.inputs.clone();
73
74 for arg in args.iter_mut() {
75 match arg {
76 syn::FnArg::Receiver(_) => {
77 arg.span()
78 .unwrap()
79 .error("task functions must not have receiver arguments")
80 .emit();
81 fail = true;
82 }
83 syn::FnArg::Typed(t) => match t.pat.as_mut() {
84 syn::Pat::Ident(i) => {
85 arg_names.push(i.ident.clone());
86 i.mutability = None;
87 }
88 _ => {
89 arg.span()
90 .unwrap()
91 .error("pattern matching in task arguments is not yet supporteds")
92 .emit();
93 fail = true;
94 }
95 },
96 }
97 }
98
99 if fail {
100 return TokenStream::new();
101 }
102 13
103 let name = task_fn.sig.ident.clone(); 14 task::run(args, f).unwrap_or_else(|x| x).into()
104
105 let visibility = &task_fn.vis;
106 task_fn.sig.ident = format_ident!("task");
107 let impl_ty = if macro_args.send {
108 quote!(impl ::core::future::Future + Send + 'static)
109 } else {
110 quote!(impl ::core::future::Future + 'static)
111 };
112
113 let attrs = &task_fn.attrs;
114
115 let result = quote! {
116 #(#attrs)*
117 #visibility fn #name(#args) -> #embassy_path::executor::SpawnToken<#impl_ty> {
118 use #embassy_path::executor::raw::TaskStorage;
119 #task_fn
120 type F = #impl_ty;
121 #[allow(clippy::declare_interior_mutable_const)]
122 const NEW_TASK: TaskStorage<F> = TaskStorage::new();
123 static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size];
124 unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) }
125 }
126 };
127 result.into()
128} 15}
129 16
130#[proc_macro_attribute] 17#[proc_macro_attribute]
131pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { 18pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
132 let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); 19 let args = syn::parse_macro_input!(args as syn::AttributeArgs);
133 20 let f = syn::parse_macro_input!(item as syn::ItemFn);
134 if !args.is_empty() { 21 main::run(args, f).unwrap_or_else(|x| x).into()
135 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") 22}
136 .to_compile_error()
137 .into();
138 }
139
140 let fspan = f.span();
141 let ident = f.sig.ident.clone();
142 let ident_s = ident.to_string();
143
144 // XXX should we blacklist other attributes?
145
146 let valid_signature = f.sig.constness.is_none()
147 && f.vis == Visibility::Inherited
148 && f.sig.abi.is_none()
149 && f.sig.inputs.is_empty()
150 && f.sig.generics.params.is_empty()
151 && f.sig.generics.where_clause.is_none()
152 && f.sig.variadic.is_none()
153 && match f.sig.output {
154 ReturnType::Default => true,
155 ReturnType::Type(_, ref ty) => match **ty {
156 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
157 Type::Never(..) => true,
158 _ => false,
159 },
160 };
161
162 if !valid_signature {
163 return parse::Error::new(
164 fspan,
165 "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
166 )
167 .to_compile_error()
168 .into();
169 }
170
171 f.block.stmts = iter::once(
172 syn::parse2(quote! {{
173 // Check that this interrupt actually exists
174 let __irq_exists_check: interrupt::#ident;
175 }})
176 .unwrap(),
177 )
178 .chain(f.block.stmts)
179 .collect();
180 23
181 quote!( 24#[proc_macro_attribute]
182 #[doc(hidden)] 25pub fn interrupt(args: TokenStream, item: TokenStream) -> TokenStream {
183 #[export_name = #ident_s] 26 let args = syn::parse_macro_input!(args as syn::AttributeArgs);
184 #[allow(non_snake_case)] 27 let f = syn::parse_macro_input!(item as syn::ItemFn);
185 #f 28 interrupt::run(args, f).unwrap_or_else(|x| x).into()
186 )
187 .into()
188} 29}
189 30
190#[proc_macro] 31#[proc_macro]
191pub fn interrupt_declare(item: TokenStream) -> TokenStream { 32pub fn interrupt_declare(item: TokenStream) -> TokenStream {
192 let name = syn::parse_macro_input!(item as syn::Ident); 33 let name = syn::parse_macro_input!(item as syn::Ident);
193 let name = format_ident!("{}", name); 34 interrupt_declare::run(name).unwrap_or_else(|x| x).into()
194 let name_interrupt = format_ident!("{}", name);
195 let name_handler = format!("__EMBASSY_{}_HANDLER", name);
196
197 let result = quote! {
198 #[allow(non_camel_case_types)]
199 pub struct #name_interrupt(());
200 unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt {
201 type Priority = crate::interrupt::Priority;
202 fn number(&self) -> u16 {
203 use cortex_m::interrupt::InterruptNumber;
204 let irq = InterruptEnum::#name;
205 irq.number() as u16
206 }
207 unsafe fn steal() -> Self {
208 Self(())
209 }
210 unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler {
211 #[export_name = #name_handler]
212 static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new();
213 &HANDLER
214 }
215 }
216
217 unsafe impl ::embassy::util::Unborrow for #name_interrupt {
218 type Target = #name_interrupt;
219 unsafe fn unborrow(self) -> #name_interrupt {
220 self
221 }
222 }
223 };
224 result.into()
225} 35}
36
226/// # interrupt_take procedural macro 37/// # interrupt_take procedural macro
227/// 38///
228/// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. 39/// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation.
@@ -231,312 +42,5 @@ pub fn interrupt_declare(item: TokenStream) -> TokenStream {
231#[proc_macro] 42#[proc_macro]
232pub fn interrupt_take(item: TokenStream) -> TokenStream { 43pub fn interrupt_take(item: TokenStream) -> TokenStream {
233 let name = syn::parse_macro_input!(item as syn::Ident); 44 let name = syn::parse_macro_input!(item as syn::Ident);
234 let name = format!("{}", name); 45 interrupt_take::run(name).unwrap_or_else(|x| x).into()
235 let name_interrupt = format_ident!("{}", name);
236 let name_handler = format!("__EMBASSY_{}_HANDLER", name);
237
238 let result = quote! {
239 {
240 #[allow(non_snake_case)]
241 #[export_name = #name]
242 pub unsafe extern "C" fn trampoline() {
243 extern "C" {
244 #[link_name = #name_handler]
245 static HANDLER: ::embassy::interrupt::Handler;
246 }
247
248 let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed);
249 let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed);
250 let func: fn(*mut ()) = ::core::mem::transmute(func);
251 func(ctx)
252 }
253
254 static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false);
255
256 if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() {
257 core::panic!("IRQ Already taken");
258 }
259
260 let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) };
261 irq
262 }
263 };
264 result.into()
265}
266
267#[cfg(feature = "stm32")]
268#[path = "chip/stm32.rs"]
269mod chip;
270
271#[cfg(feature = "nrf")]
272#[path = "chip/nrf.rs"]
273mod chip;
274
275#[cfg(feature = "rp")]
276#[path = "chip/rp.rs"]
277mod chip;
278
279#[cfg(any(
280 feature = "nrf",
281 feature = "rp",
282 feature = "stm32",
283 feature = "wasm",
284 feature = "std"
285))]
286#[derive(Debug, FromMeta)]
287struct MainArgs {
288 #[darling(default)]
289 embassy_prefix: ModulePrefix,
290
291 #[allow(unused)]
292 #[darling(default)]
293 config: Option<syn::LitStr>,
294}
295
296#[cfg(any(feature = "nrf", feature = "rp", feature = "stm32"))]
297#[proc_macro_attribute]
298pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
299 let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
300 let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
301
302 let macro_args = match MainArgs::from_list(&macro_args) {
303 Ok(v) => v,
304 Err(e) => {
305 return TokenStream::from(e.write_errors());
306 }
307 };
308
309 let mut fail = false;
310 if task_fn.sig.asyncness.is_none() {
311 task_fn
312 .sig
313 .span()
314 .unwrap()
315 .error("task functions must be async")
316 .emit();
317 fail = true;
318 }
319 if !task_fn.sig.generics.params.is_empty() {
320 task_fn
321 .sig
322 .span()
323 .unwrap()
324 .error("main function must not be generic")
325 .emit();
326 fail = true;
327 }
328
329 let args = task_fn.sig.inputs.clone();
330
331 if args.len() != 2 {
332 task_fn
333 .sig
334 .span()
335 .unwrap()
336 .error("main function must have 2 arguments")
337 .emit();
338 fail = true;
339 }
340
341 if fail {
342 return TokenStream::new();
343 }
344
345 let embassy_prefix = macro_args.embassy_prefix;
346 let embassy_prefix_lit = embassy_prefix.literal();
347 let embassy_path = embassy_prefix.append("embassy").path();
348 let task_fn_body = task_fn.block;
349
350 let config = macro_args
351 .config
352 .map(|s| s.parse::<syn::Expr>().unwrap())
353 .unwrap_or_else(|| {
354 syn::Expr::Verbatim(quote! {
355 Default::default()
356 })
357 });
358
359 let chip_setup = chip::generate(&embassy_prefix, config);
360
361 let result = quote! {
362 #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
363 async fn __embassy_main(#args) {
364 #task_fn_body
365 }
366
367 #[cortex_m_rt::entry]
368 fn main() -> ! {
369 unsafe fn make_static<T>(t: &mut T) -> &'static mut T {
370 ::core::mem::transmute(t)
371 }
372
373 #chip_setup
374
375 let mut executor = #embassy_path::executor::Executor::new();
376 let executor = unsafe { make_static(&mut executor) };
377
378 executor.run(|spawner| {
379 spawner.must_spawn(__embassy_main(spawner, p));
380 })
381 }
382 };
383 result.into()
384}
385
386#[cfg(feature = "std")]
387#[proc_macro_attribute]
388pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
389 let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
390 let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
391
392 let macro_args = match MainArgs::from_list(&macro_args) {
393 Ok(v) => v,
394 Err(e) => {
395 return TokenStream::from(e.write_errors());
396 }
397 };
398
399 let embassy_path = macro_args.embassy_prefix.append("embassy");
400
401 let mut fail = false;
402 if task_fn.sig.asyncness.is_none() {
403 task_fn
404 .sig
405 .span()
406 .unwrap()
407 .error("task functions must be async")
408 .emit();
409 fail = true;
410 }
411 if !task_fn.sig.generics.params.is_empty() {
412 task_fn
413 .sig
414 .span()
415 .unwrap()
416 .error("main function must not be generic")
417 .emit();
418 fail = true;
419 }
420
421 let args = task_fn.sig.inputs.clone();
422
423 if args.len() != 1 {
424 task_fn
425 .sig
426 .span()
427 .unwrap()
428 .error("main function must have one argument")
429 .emit();
430 fail = true;
431 }
432
433 if fail {
434 return TokenStream::new();
435 }
436
437 let task_fn_body = task_fn.block.clone();
438
439 let embassy_path = embassy_path.path();
440 let embassy_prefix_lit = macro_args.embassy_prefix.literal();
441
442 let result = quote! {
443 #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
444 async fn __embassy_main(#args) {
445 #task_fn_body
446 }
447
448 fn main() -> ! {
449 unsafe fn make_static<T>(t: &mut T) -> &'static mut T {
450 ::core::mem::transmute(t)
451 }
452
453 let mut executor = #embassy_path::executor::Executor::new();
454 let executor = unsafe { make_static(&mut executor) };
455
456 executor.run(|spawner| {
457 spawner.spawn(__embassy_main(spawner)).unwrap();
458 })
459
460 }
461 };
462 result.into()
463}
464
465#[cfg(feature = "wasm")]
466#[proc_macro_attribute]
467pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
468 let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
469 let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
470
471 let macro_args = match MainArgs::from_list(&macro_args) {
472 Ok(v) => v,
473 Err(e) => {
474 return TokenStream::from(e.write_errors());
475 }
476 };
477
478 let embassy_path = macro_args.embassy_prefix.append("embassy");
479
480 let mut fail = false;
481 if task_fn.sig.asyncness.is_none() {
482 task_fn
483 .sig
484 .span()
485 .unwrap()
486 .error("task functions must be async")
487 .emit();
488 fail = true;
489 }
490 if !task_fn.sig.generics.params.is_empty() {
491 task_fn
492 .sig
493 .span()
494 .unwrap()
495 .error("main function must not be generic")
496 .emit();
497 fail = true;
498 }
499
500 let args = task_fn.sig.inputs.clone();
501
502 if args.len() != 1 {
503 task_fn
504 .sig
505 .span()
506 .unwrap()
507 .error("main function must have one argument")
508 .emit();
509 fail = true;
510 }
511
512 if fail {
513 return TokenStream::new();
514 }
515
516 let task_fn_body = task_fn.block.clone();
517
518 let embassy_path = embassy_path.path();
519 let embassy_prefix_lit = macro_args.embassy_prefix.literal();
520
521 let result = quote! {
522 #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
523 async fn __embassy_main(#args) {
524 #task_fn_body
525 }
526
527 use wasm_bindgen::prelude::*;
528
529 #[wasm_bindgen(start)]
530 pub fn main() -> Result<(), JsValue> {
531 static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new();
532 let executor = EXECUTOR.put(#embassy_path::executor::Executor::new());
533
534 executor.start(|spawner| {
535 spawner.spawn(__embassy_main(spawner)).unwrap();
536 });
537
538 Ok(())
539 }
540 };
541 result.into()
542} 46}
diff --git a/embassy-macros/src/macros/interrupt.rs b/embassy-macros/src/macros/interrupt.rs
new file mode 100644
index 000000000..32cc0e010
--- /dev/null
+++ b/embassy-macros/src/macros/interrupt.rs
@@ -0,0 +1,66 @@
1use darling::FromMeta;
2use proc_macro2::TokenStream;
3use quote::quote;
4use std::iter;
5use syn::ReturnType;
6use syn::{Type, Visibility};
7
8use crate::util::ctxt::Ctxt;
9
10#[derive(Debug, FromMeta)]
11struct Args {}
12
13pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
14 let _args = Args::from_list(&args).map_err(|e| e.write_errors())?;
15
16 let ident = f.sig.ident.clone();
17 let ident_s = ident.to_string();
18
19 // XXX should we blacklist other attributes?
20
21 let valid_signature = f.sig.constness.is_none()
22 && f.vis == Visibility::Inherited
23 && f.sig.abi.is_none()
24 && f.sig.inputs.is_empty()
25 && f.sig.generics.params.is_empty()
26 && f.sig.generics.where_clause.is_none()
27 && f.sig.variadic.is_none()
28 && match f.sig.output {
29 ReturnType::Default => true,
30 ReturnType::Type(_, ref ty) => match **ty {
31 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
32 Type::Never(..) => true,
33 _ => false,
34 },
35 };
36
37 let ctxt = Ctxt::new();
38
39 if !valid_signature {
40 ctxt.error_spanned_by(
41 &f.sig,
42 "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
43 );
44 }
45
46 ctxt.check()?;
47
48 f.block.stmts = iter::once(
49 syn::parse2(quote! {{
50 // Check that this interrupt actually exists
51 let __irq_exists_check: interrupt::#ident;
52 }})
53 .unwrap(),
54 )
55 .chain(f.block.stmts)
56 .collect();
57
58 let result = quote!(
59 #[doc(hidden)]
60 #[export_name = #ident_s]
61 #[allow(non_snake_case)]
62 #f
63 );
64
65 Ok(result)
66}
diff --git a/embassy-macros/src/macros/interrupt_declare.rs b/embassy-macros/src/macros/interrupt_declare.rs
new file mode 100644
index 000000000..0059936d9
--- /dev/null
+++ b/embassy-macros/src/macros/interrupt_declare.rs
@@ -0,0 +1,37 @@
1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3
4pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> {
5 let name = format_ident!("{}", name);
6 let name_interrupt = format_ident!("{}", name);
7 let name_handler = format!("__EMBASSY_{}_HANDLER", name);
8
9 let result = quote! {
10 #[allow(non_camel_case_types)]
11 pub struct #name_interrupt(());
12 unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt {
13 type Priority = crate::interrupt::Priority;
14 fn number(&self) -> u16 {
15 use cortex_m::interrupt::InterruptNumber;
16 let irq = InterruptEnum::#name;
17 irq.number() as u16
18 }
19 unsafe fn steal() -> Self {
20 Self(())
21 }
22 unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler {
23 #[export_name = #name_handler]
24 static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new();
25 &HANDLER
26 }
27 }
28
29 unsafe impl ::embassy::util::Unborrow for #name_interrupt {
30 type Target = #name_interrupt;
31 unsafe fn unborrow(self) -> #name_interrupt {
32 self
33 }
34 }
35 };
36 Ok(result)
37}
diff --git a/embassy-macros/src/macros/interrupt_take.rs b/embassy-macros/src/macros/interrupt_take.rs
new file mode 100644
index 000000000..230b9c741
--- /dev/null
+++ b/embassy-macros/src/macros/interrupt_take.rs
@@ -0,0 +1,36 @@
1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3
4pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> {
5 let name = format!("{}", name);
6 let name_interrupt = format_ident!("{}", name);
7 let name_handler = format!("__EMBASSY_{}_HANDLER", name);
8
9 let result = quote! {
10 {
11 #[allow(non_snake_case)]
12 #[export_name = #name]
13 pub unsafe extern "C" fn trampoline() {
14 extern "C" {
15 #[link_name = #name_handler]
16 static HANDLER: ::embassy::interrupt::Handler;
17 }
18
19 let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed);
20 let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed);
21 let func: fn(*mut ()) = ::core::mem::transmute(func);
22 func(ctx)
23 }
24
25 static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false);
26
27 if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() {
28 core::panic!("IRQ Already taken");
29 }
30
31 let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) };
32 irq
33 }
34 };
35 Ok(result)
36}
diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs
new file mode 100644
index 000000000..01e302921
--- /dev/null
+++ b/embassy-macros/src/macros/main.rs
@@ -0,0 +1,135 @@
1use darling::FromMeta;
2use proc_macro2::TokenStream;
3use quote::quote;
4
5use crate::util::ctxt::Ctxt;
6use crate::util::path::ModulePrefix;
7
8#[cfg(feature = "stm32")]
9const HAL: Option<&str> = Some("embassy_stm32");
10#[cfg(feature = "nrf")]
11const HAL: Option<&str> = Some("embassy_nrf");
12#[cfg(feature = "rp")]
13const HAL: Option<&str> = Some("embassy_rp");
14#[cfg(not(any(feature = "stm32", feature = "nrf", feature = "rp")))]
15const HAL: Option<&str> = None;
16
17#[derive(Debug, FromMeta)]
18struct Args {
19 #[darling(default)]
20 embassy_prefix: ModulePrefix,
21
22 #[allow(unused)]
23 #[darling(default)]
24 config: Option<syn::LitStr>,
25}
26
27pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
28 let args = Args::from_list(&args).map_err(|e| e.write_errors())?;
29
30 let fargs = f.sig.inputs.clone();
31
32 let ctxt = Ctxt::new();
33
34 if f.sig.asyncness.is_none() {
35 ctxt.error_spanned_by(&f.sig, "task functions must be async");
36 }
37 if !f.sig.generics.params.is_empty() {
38 ctxt.error_spanned_by(&f.sig, "task functions must not be generic");
39 }
40
41 if HAL.is_some() && fargs.len() != 2 {
42 ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments");
43 }
44 if HAL.is_none() && fargs.len() != 1 {
45 ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments");
46 }
47
48 ctxt.check()?;
49
50 let embassy_prefix = args.embassy_prefix;
51 let embassy_prefix_lit = embassy_prefix.literal();
52 let embassy_path = embassy_prefix.append("embassy").path();
53 let f_body = f.block;
54
55 #[cfg(feature = "wasm")]
56 let main = quote! {
57 #[wasm_bindgen::prelude::wasm_bindgen(start)]
58 pub fn main() -> Result<(), wasm_bindgen::JsValue> {
59 static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new();
60 let executor = EXECUTOR.put(#embassy_path::executor::Executor::new());
61
62 executor.start(|spawner| {
63 spawner.spawn(__embassy_main(spawner)).unwrap();
64 });
65
66 Ok(())
67 }
68 };
69
70 #[cfg(all(feature = "std", not(feature = "wasm")))]
71 let main = quote! {
72 fn main() -> ! {
73 let mut executor = #embassy_path::executor::Executor::new();
74 let executor = unsafe { __make_static(&mut executor) };
75
76 executor.run(|spawner| {
77 spawner.must_spawn(__embassy_main(spawner));
78 })
79 }
80 };
81
82 #[cfg(all(not(feature = "std"), not(feature = "wasm")))]
83 let main = {
84 let config = args
85 .config
86 .map(|s| s.parse::<syn::Expr>().unwrap())
87 .unwrap_or_else(|| {
88 syn::Expr::Verbatim(quote! {
89 Default::default()
90 })
91 });
92
93 let (hal_setup, peris_arg) = match HAL {
94 Some(hal) => {
95 let embassy_hal_path = embassy_prefix.append(hal).path();
96 (
97 quote!(
98 let p = #embassy_hal_path::init(#config);
99 ),
100 quote!(p),
101 )
102 }
103 None => (quote!(), quote!()),
104 };
105
106 quote! {
107 #[cortex_m_rt::entry]
108 fn main() -> ! {
109 #hal_setup
110
111 let mut executor = #embassy_path::executor::Executor::new();
112 let executor = unsafe { __make_static(&mut executor) };
113
114 executor.run(|spawner| {
115 spawner.must_spawn(__embassy_main(spawner, #peris_arg));
116 })
117 }
118 }
119 };
120
121 let result = quote! {
122 #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
123 async fn __embassy_main(#fargs) {
124 #f_body
125 }
126
127 unsafe fn __make_static<T>(t: &mut T) -> &'static mut T {
128 ::core::mem::transmute(t)
129 }
130
131 #main
132 };
133
134 Ok(result)
135}
diff --git a/embassy-macros/src/macros/mod.rs b/embassy-macros/src/macros/mod.rs
new file mode 100644
index 000000000..4350f229f
--- /dev/null
+++ b/embassy-macros/src/macros/mod.rs
@@ -0,0 +1,5 @@
1pub mod interrupt;
2pub mod interrupt_declare;
3pub mod interrupt_take;
4pub mod main;
5pub mod task;
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs
new file mode 100644
index 000000000..f0c78c596
--- /dev/null
+++ b/embassy-macros/src/macros/task.rs
@@ -0,0 +1,90 @@
1use darling::FromMeta;
2use proc_macro2::TokenStream;
3use quote::{format_ident, quote};
4
5use crate::util::ctxt::Ctxt;
6use crate::util::path::ModulePrefix;
7
8#[derive(Debug, FromMeta)]
9struct Args {
10 #[darling(default)]
11 pool_size: Option<usize>,
12 #[darling(default)]
13 send: bool,
14 #[darling(default)]
15 embassy_prefix: ModulePrefix,
16}
17
18pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
19 let args = Args::from_list(&args).map_err(|e| e.write_errors())?;
20
21 let embassy_prefix = args.embassy_prefix.append("embassy");
22 let embassy_path = embassy_prefix.path();
23
24 let pool_size: usize = args.pool_size.unwrap_or(1);
25
26 let ctxt = Ctxt::new();
27
28 if f.sig.asyncness.is_none() {
29 ctxt.error_spanned_by(&f.sig, "task functions must be async");
30 }
31 if !f.sig.generics.params.is_empty() {
32 ctxt.error_spanned_by(&f.sig, "task functions must not be generic");
33 }
34 if pool_size < 1 {
35 ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater");
36 }
37
38 let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> =
39 syn::punctuated::Punctuated::new();
40 let mut fargs = f.sig.inputs.clone();
41
42 for arg in fargs.iter_mut() {
43 match arg {
44 syn::FnArg::Receiver(_) => {
45 ctxt.error_spanned_by(arg, "task functions must not have receiver arguments");
46 }
47 syn::FnArg::Typed(t) => match t.pat.as_mut() {
48 syn::Pat::Ident(i) => {
49 arg_names.push(i.ident.clone());
50 i.mutability = None;
51 }
52 _ => {
53 ctxt.error_spanned_by(
54 arg,
55 "pattern matching in task arguments is not yet supporteds",
56 );
57 }
58 },
59 }
60 }
61
62 ctxt.check()?;
63
64 let name = f.sig.ident.clone();
65
66 let visibility = &f.vis;
67 f.sig.ident = format_ident!("task");
68 let impl_ty = if args.send {
69 quote!(impl ::core::future::Future + Send + 'static)
70 } else {
71 quote!(impl ::core::future::Future + 'static)
72 };
73
74 let attrs = &f.attrs;
75
76 let result = quote! {
77 #(#attrs)*
78 #visibility fn #name(#fargs) -> #embassy_path::executor::SpawnToken<#impl_ty> {
79 use #embassy_path::executor::raw::TaskStorage;
80 #f
81 type F = #impl_ty;
82 #[allow(clippy::declare_interior_mutable_const)]
83 const NEW_TASK: TaskStorage<F> = TaskStorage::new();
84 static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size];
85 unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) }
86 }
87 };
88
89 Ok(result)
90}
diff --git a/embassy-macros/src/util/ctxt.rs b/embassy-macros/src/util/ctxt.rs
new file mode 100644
index 000000000..d668ae780
--- /dev/null
+++ b/embassy-macros/src/util/ctxt.rs
@@ -0,0 +1,72 @@
1// nifty utility borrowed from serde :)
2// https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/ctxt.rs
3
4use proc_macro2::TokenStream;
5use quote::{quote, ToTokens};
6use std::cell::RefCell;
7use std::fmt::Display;
8use std::thread;
9use syn;
10
11/// A type to collect errors together and format them.
12///
13/// Dropping this object will cause a panic. It must be consumed using `check`.
14///
15/// References can be shared since this type uses run-time exclusive mut checking.
16#[derive(Default)]
17pub struct Ctxt {
18 // The contents will be set to `None` during checking. This is so that checking can be
19 // enforced.
20 errors: RefCell<Option<Vec<syn::Error>>>,
21}
22
23impl Ctxt {
24 /// Create a new context object.
25 ///
26 /// This object contains no errors, but will still trigger a panic if it is not `check`ed.
27 pub fn new() -> Self {
28 Ctxt {
29 errors: RefCell::new(Some(Vec::new())),
30 }
31 }
32
33 /// Add an error to the context object with a tokenenizable object.
34 ///
35 /// The object is used for spanning in error messages.
36 pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) {
37 self.errors
38 .borrow_mut()
39 .as_mut()
40 .unwrap()
41 // Curb monomorphization from generating too many identical methods.
42 .push(syn::Error::new_spanned(obj.into_token_stream(), msg));
43 }
44
45 /// Add one of Syn's parse errors.
46 #[allow(unused)]
47 pub fn syn_error(&self, err: syn::Error) {
48 self.errors.borrow_mut().as_mut().unwrap().push(err);
49 }
50
51 /// Consume this object, producing a formatted error string if there are errors.
52 pub fn check(self) -> Result<(), TokenStream> {
53 let errors = self.errors.borrow_mut().take().unwrap();
54 match errors.len() {
55 0 => Ok(()),
56 _ => Err(to_compile_errors(errors)),
57 }
58 }
59}
60
61fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
62 let compile_errors = errors.iter().map(syn::Error::to_compile_error);
63 quote!(#(#compile_errors)*)
64}
65
66impl Drop for Ctxt {
67 fn drop(&mut self) {
68 if !thread::panicking() && self.errors.borrow().is_some() {
69 panic!("forgot to check for errors");
70 }
71 }
72}
diff --git a/embassy-macros/src/util/mod.rs b/embassy-macros/src/util/mod.rs
new file mode 100644
index 000000000..c2f2dfd65
--- /dev/null
+++ b/embassy-macros/src/util/mod.rs
@@ -0,0 +1,2 @@
1pub mod ctxt;
2pub mod path;
diff --git a/embassy-macros/src/path.rs b/embassy-macros/src/util/path.rs
index 00fca7bdb..00fca7bdb 100644
--- a/embassy-macros/src/path.rs
+++ b/embassy-macros/src/util/path.rs