Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions rtic-macros/src/codegen/idle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)*
Expand Down
22 changes: 14 additions & 8 deletions rtic-macros/src/codegen/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)*
}
Expand All @@ -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;

Expand Down
6 changes: 6 additions & 0 deletions rtic-macros/src/syntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
39 changes: 38 additions & 1 deletion rtic-macros/src/syntax/parse/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,42 @@ impl App {
if let ForeignItem::Fn(mut item) = item {
let span = item.sig.ident.span();
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"))
Expand Down Expand Up @@ -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 {
Expand Down
31 changes: 30 additions & 1 deletion rtic-macros/src/syntax/parse/idle.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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,
});
}
}
Expand Down
40 changes: 39 additions & 1 deletion rtic-macros/src/syntax/parse/init.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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,
});
}
}
Expand Down
1 change: 1 addition & 0 deletions rtic/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
57 changes: 31 additions & 26 deletions rtic/examples/extern_binds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,53 @@
#![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");
}

#[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);
}
Expand Down