aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rtic-macros/src/codegen/idle.rs24
-rw-r--r--rtic-macros/src/codegen/init.rs22
-rw-r--r--rtic-macros/src/syntax/ast.rs6
-rw-r--r--rtic-macros/src/syntax/parse/app.rs39
-rw-r--r--rtic-macros/src/syntax/parse/idle.rs31
-rw-r--r--rtic-macros/src/syntax/parse/init.rs40
-rw-r--r--rtic/CHANGELOG.md1
-rw-r--r--rtic/examples/extern_binds.rs57
8 files changed, 173 insertions, 47 deletions
diff --git a/rtic-macros/src/codegen/idle.rs b/rtic-macros/src/codegen/idle.rs
index 0c833ef3..9e608cb9 100644
--- a/rtic-macros/src/codegen/idle.rs
+++ b/rtic-macros/src/codegen/idle.rs
@@ -34,16 +34,20 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let attrs = &idle.attrs;
let context = &idle.context;
let stmts = &idle.stmts;
- let user_idle = Some(quote!(
- #(#attrs)*
- #[allow(non_snake_case)]
- fn #name(#context: #name::Context) -> ! {
- use rtic::Mutex as _;
- use rtic::mutex::prelude::*;
-
- #(#stmts)*
- }
- ));
+ let user_idle = if !idle.is_extern {
+ Some(quote!(
+ #(#attrs)*
+ #[allow(non_snake_case)]
+ fn #name(#context: #name::Context) -> ! {
+ use rtic::Mutex as _;
+ use rtic::mutex::prelude::*;
+
+ #(#stmts)*
+ }
+ ))
+ } else {
+ None
+ };
quote!(
#(#mod_app)*
diff --git a/rtic-macros/src/codegen/init.rs b/rtic-macros/src/codegen/init.rs
index b667ae0f..f6bb43d6 100644
--- a/rtic-macros/src/codegen/init.rs
+++ b/rtic-macros/src/codegen/init.rs
@@ -54,10 +54,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
.collect();
root_init.push(quote! {
+ #[doc = r"Shared resources"]
#shared_vis struct #shared {
#(#shared_resources)*
}
+ #[doc = r"Local resources"]
#local_vis struct #local {
#(#local_resources)*
}
@@ -67,14 +69,18 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let user_init_return = quote! {#shared, #local};
- let user_init = quote!(
- #(#attrs)*
- #[inline(always)]
- #[allow(non_snake_case)]
- fn #name(#context: #name::Context) -> (#user_init_return) {
- #(#stmts)*
- }
- );
+ let user_init = if !init.is_extern {
+ Some(quote!(
+ #(#attrs)*
+ #[inline(always)]
+ #[allow(non_snake_case)]
+ fn #name(#context: #name::Context) -> (#user_init_return) {
+ #(#stmts)*
+ }
+ ))
+ } else {
+ None
+ };
let mut mod_app = None;
diff --git a/rtic-macros/src/syntax/ast.rs b/rtic-macros/src/syntax/ast.rs
index 3f4956cc..f0067b8e 100644
--- a/rtic-macros/src/syntax/ast.rs
+++ b/rtic-macros/src/syntax/ast.rs
@@ -91,6 +91,9 @@ pub struct Init {
/// The name of the user provided local resources struct
pub user_local_struct: Ident,
+
+ /// The init function is declared externally
+ pub is_extern: bool,
}
/// `init` context metadata
@@ -127,6 +130,9 @@ pub struct Idle {
/// The statements that make up this `idle` function
pub stmts: Vec<Stmt>,
+
+ /// The idle function is declared externally
+ pub is_extern: bool,
}
/// `idle` context metadata
diff --git a/rtic-macros/src/syntax/parse/app.rs b/rtic-macros/src/syntax/parse/app.rs
index 2f2d8165..d75c8c60 100644
--- a/rtic-macros/src/syntax/parse/app.rs
+++ b/rtic-macros/src/syntax/parse/app.rs
@@ -367,6 +367,42 @@ impl App {
if let Some(pos) = item
.attrs
.iter()
+ .position(|attr| util::attr_eq(attr, "init"))
+ {
+ let args = InitArgs::parse(item.attrs.remove(pos).tokens)?;
+
+ // If an init function already exists, error
+ if init.is_some() {
+ return Err(parse::Error::new(
+ span,
+ "`#[init]` function must appear at most once",
+ ));
+ }
+
+ check_ident(&item.sig.ident)?;
+
+ init = Some(Init::parse_foreign(args, item)?);
+ } else if let Some(pos) = item
+ .attrs
+ .iter()
+ .position(|attr| util::attr_eq(attr, "idle"))
+ {
+ let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?;
+
+ // If an idle function already exists, error
+ if idle.is_some() {
+ return Err(parse::Error::new(
+ span,
+ "`#[idle]` function must appear at most once",
+ ));
+ }
+
+ check_ident(&item.sig.ident)?;
+
+ idle = Some(Idle::parse_foreign(args, item)?);
+ } else if let Some(pos) = item
+ .attrs
+ .iter()
.position(|attr| util::attr_eq(attr, "task"))
{
if hardware_tasks.contains_key(&item.sig.ident)
@@ -408,7 +444,8 @@ impl App {
} else {
return Err(parse::Error::new(
span,
- "`extern` task required `#[task(..)]` attribute",
+ "`extern` task, init or idle must have either `#[task(..)]`,
+ `#[init(..)]` or `#[idle(..)]` attribute",
));
}
} else {
diff --git a/rtic-macros/src/syntax/parse/idle.rs b/rtic-macros/src/syntax/parse/idle.rs
index 124c1366..a3d97155 100644
--- a/rtic-macros/src/syntax/parse/idle.rs
+++ b/rtic-macros/src/syntax/parse/idle.rs
@@ -1,5 +1,5 @@
use proc_macro2::TokenStream as TokenStream2;
-use syn::{parse, ItemFn};
+use syn::{parse, ForeignItemFn, ItemFn, Stmt};
use crate::syntax::{
ast::{Idle, IdleArgs},
@@ -29,6 +29,35 @@ impl Idle {
context,
name: item.sig.ident,
stmts: item.block.stmts,
+ is_extern: false,
+ });
+ }
+ }
+ }
+
+ Err(parse::Error::new(
+ item.sig.ident.span(),
+ format!("this `#[idle]` function must have signature `fn({name}::Context) -> !`"),
+ ))
+ }
+
+ pub(crate) fn parse_foreign(args: IdleArgs, item: ForeignItemFn) -> parse::Result<Self> {
+ let valid_signature = util::check_foreign_fn_signature(&item, false)
+ && item.sig.inputs.len() == 1
+ && util::type_is_bottom(&item.sig.output);
+
+ let name = item.sig.ident.to_string();
+
+ if valid_signature {
+ if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
+ if rest.is_empty() {
+ return Ok(Idle {
+ args,
+ attrs: item.attrs,
+ context,
+ name: item.sig.ident,
+ stmts: Vec::<Stmt>::new(),
+ is_extern: true,
});
}
}
diff --git a/rtic-macros/src/syntax/parse/init.rs b/rtic-macros/src/syntax/parse/init.rs
index 0aea20bd..59d00937 100644
--- a/rtic-macros/src/syntax/parse/init.rs
+++ b/rtic-macros/src/syntax/parse/init.rs
@@ -1,6 +1,6 @@
use proc_macro2::TokenStream as TokenStream2;
-use syn::{parse, ItemFn};
+use syn::{parse, ForeignItemFn, ItemFn, Stmt};
use crate::syntax::{
ast::{Init, InitArgs},
@@ -35,6 +35,44 @@ impl Init {
stmts: item.block.stmts,
user_shared_struct,
user_local_struct,
+ is_extern: false,
+ });
+ }
+ }
+ }
+ }
+
+ Err(parse::Error::new(
+ span,
+ format!(
+ "the `#[init]` function must have signature `fn({name}::Context) -> (Shared resources struct, Local resources struct)`"
+ ),
+ ))
+ }
+
+ pub(crate) fn parse_foreign(args: InitArgs, item: ForeignItemFn) -> parse::Result<Self> {
+ let valid_signature =
+ util::check_foreign_fn_signature(&item, false) && item.sig.inputs.len() == 1;
+
+ let span = item.sig.ident.span();
+
+ let name = item.sig.ident.to_string();
+
+ if valid_signature {
+ if let Ok((user_shared_struct, user_local_struct)) =
+ util::type_is_init_return(&item.sig.output)
+ {
+ if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
+ if rest.is_empty() {
+ return Ok(Init {
+ args,
+ attrs: item.attrs,
+ context,
+ name: item.sig.ident,
+ stmts: Vec::<Stmt>::new(),
+ user_shared_struct,
+ user_local_struct,
+ is_extern: true,
});
}
}
diff --git a/rtic/CHANGELOG.md b/rtic/CHANGELOG.md
index 2d0a392f..b500188a 100644
--- a/rtic/CHANGELOG.md
+++ b/rtic/CHANGELOG.md
@@ -8,6 +8,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## [Unreleased]
### Added
+- Allow #[init] and #[idle] to be defined externally
### Fixed
diff --git a/rtic/examples/extern_binds.rs b/rtic/examples/extern_binds.rs
index f03a8a9c..45939d29 100644
--- a/rtic/examples/extern_binds.rs
+++ b/rtic/examples/extern_binds.rs
@@ -6,9 +6,31 @@
#![deny(unsafe_code)]
#![deny(missing_docs)]
-use cortex_m_semihosting::hprintln;
+use cortex_m_semihosting::{debug, hprintln};
+use lm3s6965::Interrupt;
use panic_semihosting as _;
+// Free function implementing `init`.
+fn init(_: app::init::Context) -> (app::Shared, app::Local) {
+ rtic::pend(Interrupt::UART0);
+
+ hprintln!("init");
+
+ (app::Shared {}, app::Local {})
+}
+
+// Free function implementing `idle`.
+fn idle(_: app::idle::Context) -> ! {
+ hprintln!("idle");
+
+ rtic::pend(Interrupt::UART0);
+
+ loop {
+ cortex_m::asm::nop();
+ debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
+ }
+}
+
// Free function implementing the interrupt bound task `foo`.
fn foo(_: app::foo::Context) {
hprintln!("foo called");
@@ -16,38 +38,21 @@ fn foo(_: app::foo::Context) {
#[rtic::app(device = lm3s6965)]
mod app {
- use crate::foo;
- use cortex_m_semihosting::{debug, hprintln};
- use lm3s6965::Interrupt;
+ use crate::{foo, idle, init};
#[shared]
- struct Shared {}
+ pub struct Shared {}
#[local]
- struct Local {}
+ pub struct Local {}
- #[init]
- fn init(_: init::Context) -> (Shared, Local) {
- rtic::pend(Interrupt::UART0);
-
- hprintln!("init");
-
- (Shared {}, Local {})
- }
-
- #[idle]
- fn idle(_: idle::Context) -> ! {
- hprintln!("idle");
-
- rtic::pend(Interrupt::UART0);
+ extern "Rust" {
+ #[init]
+ fn init(_: init::Context) -> (Shared, Local);
- loop {
- cortex_m::asm::nop();
- debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
- }
- }
+ #[idle]
+ fn idle(_: idle::Context) -> !;
- extern "Rust" {
#[task(binds = UART0)]
fn foo(_: foo::Context);
}