From 81275bfa4f41e2066770087f3a33cad4227eab41 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 13 Jun 2019 23:56:59 +0200 Subject: rtfm-syntax refactor + heterogeneous multi-core support --- macros/src/codegen/assertions.rs | 26 +++ macros/src/codegen/dispatchers.rs | 178 ++++++++++++++++++ macros/src/codegen/hardware_tasks.rs | 121 ++++++++++++ macros/src/codegen/idle.rs | 92 +++++++++ macros/src/codegen/init.rs | 112 +++++++++++ macros/src/codegen/locals.rs | 94 ++++++++++ macros/src/codegen/module.rs | 328 +++++++++++++++++++++++++++++++++ macros/src/codegen/post_init.rs | 139 ++++++++++++++ macros/src/codegen/pre_init.rs | 150 +++++++++++++++ macros/src/codegen/resources.rs | 115 ++++++++++++ macros/src/codegen/resources_struct.rs | 178 ++++++++++++++++++ macros/src/codegen/schedule.rs | 95 ++++++++++ macros/src/codegen/schedule_body.rs | 61 ++++++ macros/src/codegen/software_tasks.rs | 169 +++++++++++++++++ macros/src/codegen/spawn.rs | 127 +++++++++++++ macros/src/codegen/spawn_body.rs | 81 ++++++++ macros/src/codegen/timer_queue.rs | 147 +++++++++++++++ macros/src/codegen/util.rs | 253 +++++++++++++++++++++++++ 18 files changed, 2466 insertions(+) create mode 100644 macros/src/codegen/assertions.rs create mode 100644 macros/src/codegen/dispatchers.rs create mode 100644 macros/src/codegen/hardware_tasks.rs create mode 100644 macros/src/codegen/idle.rs create mode 100644 macros/src/codegen/init.rs create mode 100644 macros/src/codegen/locals.rs create mode 100644 macros/src/codegen/module.rs create mode 100644 macros/src/codegen/post_init.rs create mode 100644 macros/src/codegen/pre_init.rs create mode 100644 macros/src/codegen/resources.rs create mode 100644 macros/src/codegen/resources_struct.rs create mode 100644 macros/src/codegen/schedule.rs create mode 100644 macros/src/codegen/schedule_body.rs create mode 100644 macros/src/codegen/software_tasks.rs create mode 100644 macros/src/codegen/spawn.rs create mode 100644 macros/src/codegen/spawn_body.rs create mode 100644 macros/src/codegen/timer_queue.rs create mode 100644 macros/src/codegen/util.rs (limited to 'macros/src/codegen') diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs new file mode 100644 index 0000000..95268a2 --- /dev/null +++ b/macros/src/codegen/assertions.rs @@ -0,0 +1,26 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::analyze::Analysis; + +/// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits +pub fn codegen(core: u8, analysis: &Analysis) -> Vec { + let mut stmts = vec![]; + + // we don't generate *all* assertions on all cores because the user could conditionally import a + // type only on some core (e.g. `#[cfg(core = "0")] use some::Type;`) + + if let Some(types) = analysis.send_types.get(&core) { + for ty in types { + stmts.push(quote!(rtfm::export::assert_send::<#ty>();)); + } + } + + if let Some(types) = analysis.sync_types.get(&core) { + for ty in types { + stmts.push(quote!(rtfm::export::assert_sync::<#ty>();)); + } + } + + stmts +} diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs new file mode 100644 index 0000000..65d25c7 --- /dev/null +++ b/macros/src/codegen/dispatchers.rs @@ -0,0 +1,178 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::ast::App; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +/// Generates task dispatchers +pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { + let mut items = vec![]; + + for (&receiver, dispatchers) in &analysis.channels { + let interrupts = &analysis.interrupts[&receiver]; + + for (&level, channels) in dispatchers { + let mut stmts = vec![]; + + for (&sender, channel) in channels { + let cfg_sender = util::cfg_core(sender, app.args.cores); + + let variants = channel + .tasks + .iter() + .map(|name| { + let cfgs = &app.software_tasks[name].cfgs; + + quote!( + #(#cfgs)* + #name + ) + }) + .collect::>(); + + let doc = format!( + "Software tasks spawned from core #{} to be dispatched at priority level {} by core #{}", + sender, level, receiver, + ); + let t = util::spawn_t_ident(receiver, level, sender); + items.push(quote!( + #[allow(non_camel_case_types)] + #[derive(Clone, Copy)] + #[doc = #doc] + enum #t { + #(#variants,)* + } + )); + + let n = util::capacity_typenum(channel.capacity, true); + let rq = util::rq_ident(receiver, level, sender); + let (rq_attr, rq_ty, rq_expr) = if sender == receiver { + ( + cfg_sender.clone(), + quote!(rtfm::export::SCRQ<#t, #n>), + quote!(rtfm::export::Queue(unsafe { + rtfm::export::iQueue::u8_sc() + })), + ) + } else { + ( + Some(quote!(#[rtfm::export::shared])), + quote!(rtfm::export::MCRQ<#t, #n>), + quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), + ) + }; + + let doc = format!( + "Queue of tasks sent by core #{} ready to be dispatched by core #{} at priority level {}", + sender, + receiver, + level + ); + items.push(quote!( + #[doc = #doc] + #rq_attr + static mut #rq: #rq_ty = #rq_expr; + )); + + if let Some(ceiling) = channel.ceiling { + items.push(quote!( + #cfg_sender + struct #rq<'a> { + priority: &'a rtfm::export::Priority, + } + )); + + items.push(util::impl_mutex( + extra, + &[], + cfg_sender.as_ref(), + false, + &rq, + rq_ty, + ceiling, + quote!(&mut #rq), + )); + } + + let arms = channel + .tasks + .iter() + .map(|name| { + let task = &app.software_tasks[name]; + let cfgs = &task.cfgs; + let fq = util::fq_ident(name, sender); + let inputs = util::inputs_ident(name, sender); + let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); + + let (let_instant, instant) = if app.uses_schedule(receiver) { + let instants = util::instants_ident(name, sender); + + ( + quote!( + let instant = + #instants.get_unchecked(usize::from(index)).as_ptr().read(); + ), + quote!(, instant), + ) + } else { + (quote!(), quote!()) + }; + + let locals_new = if task.locals.is_empty() { + quote!() + } else { + quote!(#name::Locals::new(),) + }; + + quote!( + #(#cfgs)* + #t::#name => { + let #tupled = + #inputs.get_unchecked(usize::from(index)).as_ptr().read(); + #let_instant + #fq.split().0.enqueue_unchecked(index); + let priority = &rtfm::export::Priority::new(PRIORITY); + #name( + #locals_new + #name::Context::new(priority #instant) + #(,#pats)* + ) + } + ) + }) + .collect::>(); + + stmts.push(quote!( + while let Some((task, index)) = #rq.split().1.dequeue() { + match task { + #(#arms)* + } + } + )); + } + + let doc = format!( + "Interrupt handler used by core #{} to dispatch tasks at priority {}", + receiver, level + ); + let cfg_receiver = util::cfg_core(receiver, app.args.cores); + let interrupt = &interrupts[&level]; + items.push(quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #[no_mangle] + #cfg_receiver + unsafe fn #interrupt() { + /// The priority of this interrupt handler + const PRIORITY: u8 = #level; + + rtfm::export::run(PRIORITY, || { + #(#stmts)* + }); + } + )); + } + } + + items +} diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs new file mode 100644 index 0000000..e65bad5 --- /dev/null +++ b/macros/src/codegen/hardware_tasks.rs @@ -0,0 +1,121 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{ + analyze::Analysis, + check::Extra, + codegen::{locals, module, resources_struct, util}, +}; + +/// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s) +pub fn codegen( + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> ( + // const_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors + Vec, + // root_hardware_tasks -- items that must be placed in the root of the crate: + // - `${task}Locals` structs + // - `${task}Resources` structs + // - `${task}` modules + Vec, + // user_hardware_tasks -- the `#[task]` functions written by the user + Vec, +) { + let mut const_app = vec![]; + let mut root = vec![]; + let mut user_tasks = vec![]; + + for (name, task) in &app.hardware_tasks { + let core = task.args.core; + let cfg_core = util::cfg_core(core, app.args.cores); + + let (let_instant, instant) = if app.uses_schedule(core) { + let m = extra.monotonic(); + + ( + Some(quote!(let instant = <#m as rtfm::Monotonic>::now();)), + Some(quote!(, instant)), + ) + } else { + (None, None) + }; + + let locals_new = if task.locals.is_empty() { + quote!() + } else { + quote!(#name::Locals::new(),) + }; + + let symbol = task.args.binds(name); + let priority = task.args.priority; + + const_app.push(quote!( + #[allow(non_snake_case)] + #[no_mangle] + #cfg_core + unsafe fn #symbol() { + const PRIORITY: u8 = #priority; + + #let_instant + + rtfm::export::run(PRIORITY, || { + crate::#name( + #locals_new + #name::Context::new(&rtfm::export::Priority::new(PRIORITY) #instant) + ) + }); + } + )); + + let mut needs_lt = false; + + // `${task}Resources` + if !task.args.resources.is_empty() { + let (item, constructor) = resources_struct::codegen( + Context::HardwareTask(name), + priority, + &mut needs_lt, + app, + analysis, + ); + + root.push(item); + + const_app.push(constructor); + } + + root.push(module::codegen( + Context::HardwareTask(name), + needs_lt, + app, + extra, + )); + + // `${task}Locals` + let mut locals_pat = None; + if !task.locals.is_empty() { + let (struct_, pat) = locals::codegen(Context::HardwareTask(name), &task.locals, app); + + root.push(struct_); + locals_pat = Some(pat); + } + + let attrs = &task.attrs; + let context = &task.context; + let stmts = &task.stmts; + user_tasks.push(quote!( + #(#attrs)* + #[allow(non_snake_case)] + fn #name(#(#locals_pat,)* #context: #name::Context) { + use rtfm::Mutex as _; + + #(#stmts)* + } + )); + } + + (const_app, root, user_tasks) +} diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs new file mode 100644 index 0000000..7af01c9 --- /dev/null +++ b/macros/src/codegen/idle.rs @@ -0,0 +1,92 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{ + analyze::Analysis, + check::Extra, + codegen::{locals, module, resources_struct, util}, +}; + +/// Generates support code for `#[idle]` functions +pub fn codegen( + core: u8, + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> ( + // const_app_idle -- the `${idle}Resources` constructor + Option, + // root_idle -- items that must be placed in the root of the crate: + // - the `${idle}Locals` struct + // - the `${idle}Resources` struct + // - the `${idle}` module, which contains types like `${idle}::Context` + Vec, + // user_idle + Option, + // call_idle + TokenStream2, +) { + if let Some(idle) = app.idles.get(&core) { + let mut needs_lt = false; + let mut const_app = None; + let mut root_idle = vec![]; + let mut locals_pat = None; + let mut locals_new = None; + + if !idle.args.resources.is_empty() { + let (item, constructor) = + resources_struct::codegen(Context::Idle(core), 0, &mut needs_lt, app, analysis); + + root_idle.push(item); + const_app = Some(constructor); + } + + let name = &idle.name; + if !idle.locals.is_empty() { + let (locals, pat) = locals::codegen(Context::Idle(core), &idle.locals, app); + + locals_new = Some(quote!(#name::Locals::new())); + locals_pat = Some(pat); + root_idle.push(locals); + } + + root_idle.push(module::codegen(Context::Idle(core), needs_lt, app, extra)); + + let cfg_core = util::cfg_core(core, app.args.cores); + let attrs = &idle.attrs; + let context = &idle.context; + let stmts = &idle.stmts; + let user_idle = Some(quote!( + #cfg_core + #(#attrs)* + #[allow(non_snake_case)] + fn #name(#(#locals_pat,)* #context: #name::Context) -> ! { + use rtfm::Mutex as _; + + #(#stmts)* + } + )); + + let call_idle = quote!(#name( + #(#locals_new,)* + #name::Context::new(&rtfm::export::Priority::new(0)) + )); + + ( + const_app, + root_idle, + user_idle, + call_idle, + ) + } else { + ( + None, + vec![], + None, + quote!(loop { + rtfm::export::wfi() + }), + ) + } +} diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs new file mode 100644 index 0000000..271be94 --- /dev/null +++ b/macros/src/codegen/init.rs @@ -0,0 +1,112 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{ + analyze::Analysis, + check::Extra, + codegen::{locals, module, resources_struct, util}, +}; + +/// Generates support code for `#[init]` functions +pub fn codegen( + core: u8, + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> ( + // const_app_idle -- the `${init}Resources` constructor + Option, + // root_init -- items that must be placed in the root of the crate: + // - the `${init}Locals` struct + // - the `${init}Resources` struct + // - the `${init}LateResources` struct + // - the `${init}` module, which contains types like `${init}::Context` + Vec, + // user_init -- the `#[init]` function written by the user + Option, + // call_init -- the call to the user `#[init]` if there's one + Option, +) { + if let Some(init) = app.inits.get(&core) { + let cfg_core = util::cfg_core(core, app.args.cores); + let mut needs_lt = false; + let name = &init.name; + + let mut root_init = vec![]; + + let ret = { + let late_fields = analysis + .late_resources + .get(&core) + .map(|resources| { + resources + .iter() + .map(|name| { + let ty = &app.late_resources[name].ty; + + quote!(pub #name: #ty) + }) + .collect::>() + }) + .unwrap_or(vec![]); + + if !late_fields.is_empty() { + let late_resources = util::late_resources_ident(&name); + + root_init.push(quote!( + /// Resources initialized at runtime + #cfg_core + #[allow(non_snake_case)] + pub struct #late_resources { + #(#late_fields),* + } + )); + + Some(quote!(-> #name::LateResources)) + } else { + None + } + }; + + let mut locals_pat = None; + let mut locals_new = None; + if !init.locals.is_empty() { + let (struct_, pat) = locals::codegen(Context::Init(core), &init.locals, app); + + locals_new = Some(quote!(#name::Locals::new())); + locals_pat = Some(pat); + root_init.push(struct_); + } + + let context = &init.context; + let attrs = &init.attrs; + let stmts = &init.stmts; + let user_init = Some(quote!( + #(#attrs)* + #cfg_core + #[allow(non_snake_case)] + fn #name(#(#locals_pat,)* #context: #name::Context) #ret { + #(#stmts)* + } + )); + + let mut const_app = None; + if !init.args.resources.is_empty() { + let (item, constructor) = + resources_struct::codegen(Context::Init(core), 0, &mut needs_lt, app, analysis); + + root_init.push(item); + const_app = Some(constructor); + } + + let call_init = + Some(quote!(let late = #name(#(#locals_new,)* #name::Context::new(core.into()));)); + + root_init.push(module::codegen(Context::Init(core), needs_lt, app, extra)); + + (const_app, root_init, user_init, call_init) + } else { + (None, vec![], None, None) + } +} diff --git a/macros/src/codegen/locals.rs b/macros/src/codegen/locals.rs new file mode 100644 index 0000000..9663563 --- /dev/null +++ b/macros/src/codegen/locals.rs @@ -0,0 +1,94 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ + ast::{App, Local}, + Context, Map, +}; + +use crate::codegen::util; + +pub fn codegen( + ctxt: Context, + locals: &Map, + app: &App, +) -> ( + // locals + TokenStream2, + // pat + TokenStream2, +) { + assert!(!locals.is_empty()); + + let runs_once = ctxt.runs_once(); + let ident = util::locals_ident(ctxt, app); + + let mut lt = None; + let mut fields = vec![]; + let mut items = vec![]; + let mut names = vec![]; + let mut values = vec![]; + let mut pats = vec![]; + let mut has_cfgs = false; + + for (name, local) in locals { + let lt = if runs_once { + quote!('static) + } else { + lt = Some(quote!('a)); + quote!('a) + }; + + let cfgs = &local.cfgs; + has_cfgs |= !cfgs.is_empty(); + + let expr = &local.expr; + let ty = &local.ty; + fields.push(quote!( + #(#cfgs)* + #name: &#lt mut #ty + )); + items.push(quote!( + #(#cfgs)* + static mut #name: #ty = #expr + )); + values.push(quote!( + #(#cfgs)* + #name: &mut #name + )); + names.push(name); + pats.push(quote!( + #(#cfgs)* + #name + )); + } + + if lt.is_some() && has_cfgs { + fields.push(quote!(__marker__: core::marker::PhantomData<&'a mut ()>)); + values.push(quote!(__marker__: core::marker::PhantomData)); + } + + let locals = quote!( + #[allow(non_snake_case)] + #[doc(hidden)] + pub struct #ident<#lt> { + #(#fields),* + } + + impl<#lt> #ident<#lt> { + #[inline(always)] + unsafe fn new() -> Self { + #(#items;)* + + #ident { + #(#values),* + } + } + } + ); + + let ident = ctxt.ident(app); + ( + locals, + quote!(#ident::Locals { #(#pats,)* .. }: #ident::Locals), + ) +} diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs new file mode 100644 index 0000000..5f077a2 --- /dev/null +++ b/macros/src/codegen/module.rs @@ -0,0 +1,328 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{check::Extra, codegen::util}; + +pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) -> TokenStream2 { + let mut items = vec![]; + let mut fields = vec![]; + let mut values = vec![]; + + let name = ctxt.ident(app); + + let core = ctxt.core(app); + let mut needs_instant = false; + let mut lt = None; + match ctxt { + Context::Init(core) => { + if app.uses_schedule(core) { + let m = extra.monotonic(); + + fields.push(quote!( + /// System start time = `Instant(0 /* cycles */)` + pub start: <#m as rtfm::Monotonic>::Instant + )); + + values.push(quote!(start: <#m as rtfm::Monotonic>::zero())); + + fields.push(quote!( + /// Core (Cortex-M) peripherals minus the SysTick + pub core: rtfm::Peripherals + )); + } else { + fields.push(quote!( + /// Core (Cortex-M) peripherals + pub core: rtfm::export::Peripherals + )); + } + + if extra.peripherals == Some(core) { + let device = extra.device; + + fields.push(quote!( + /// Device peripherals + pub device: #device::Peripherals + )); + + values.push(quote!(device: #device::Peripherals::steal())); + } + + values.push(quote!(core)); + } + + Context::Idle(..) => {} + + Context::HardwareTask(..) => { + if app.uses_schedule(core) { + let m = extra.monotonic(); + + fields.push(quote!( + /// Time at which this handler started executing + pub start: <#m as rtfm::Monotonic>::Instant + )); + + values.push(quote!(start: instant)); + + needs_instant = true; + } + } + + Context::SoftwareTask(..) => { + if app.uses_schedule(core) { + let m = extra.monotonic(); + + fields.push(quote!( + /// The time at which this task was scheduled to run + pub scheduled: <#m as rtfm::Monotonic>::Instant + )); + + values.push(quote!(scheduled: instant)); + + needs_instant = true; + } + } + } + + if ctxt.has_locals(app) { + let ident = util::locals_ident(ctxt, app); + items.push(quote!( + #[doc(inline)] + pub use super::#ident as Locals; + )); + } + + if ctxt.has_resources(app) { + let ident = util::resources_ident(ctxt, app); + let lt = if resources_tick { + lt = Some(quote!('a)); + Some(quote!('a)) + } else { + None + }; + + items.push(quote!( + #[doc(inline)] + pub use super::#ident as Resources; + )); + + fields.push(quote!( + /// Resources this task has access to + pub resources: Resources<#lt> + )); + + let priority = if ctxt.is_init() { + None + } else { + Some(quote!(priority)) + }; + values.push(quote!(resources: Resources::new(#priority))); + } + + if ctxt.uses_schedule(app) { + let doc = "Tasks that can be `schedule`-d from this context"; + if ctxt.is_init() { + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Schedule { + _not_send: core::marker::PhantomData<*mut ()>, + } + )); + + fields.push(quote!( + #[doc = #doc] + pub schedule: Schedule + )); + + values.push(quote!( + schedule: Schedule { _not_send: core::marker::PhantomData } + )); + } else { + lt = Some(quote!('a)); + + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Schedule<'a> { + priority: &'a rtfm::export::Priority, + } + + impl<'a> Schedule<'a> { + #[doc(hidden)] + #[inline(always)] + pub unsafe fn priority(&self) -> &rtfm::export::Priority { + &self.priority + } + } + )); + + fields.push(quote!( + #[doc = #doc] + pub schedule: Schedule<'a> + )); + + values.push(quote!( + schedule: Schedule { priority } + )); + } + } + + if ctxt.uses_spawn(app) { + let doc = "Tasks that can be `spawn`-ed from this context"; + if ctxt.is_init() { + fields.push(quote!( + #[doc = #doc] + pub spawn: Spawn + )); + + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Spawn { + _not_send: core::marker::PhantomData<*mut ()>, + } + )); + + values.push(quote!(spawn: Spawn { _not_send: core::marker::PhantomData })); + } else { + lt = Some(quote!('a)); + + fields.push(quote!( + #[doc = #doc] + pub spawn: Spawn<'a> + )); + + let mut instant_method = None; + if ctxt.is_idle() { + items.push(quote!( + #[doc = #doc] + #[derive(Clone, Copy)] + pub struct Spawn<'a> { + priority: &'a rtfm::export::Priority, + } + )); + + values.push(quote!(spawn: Spawn { priority })); + } else { + let instant_field = if app.uses_schedule(core) { + let m = extra.monotonic(); + + needs_instant = true; + instant_method = Some(quote!( + pub unsafe fn instant(&self) -> <#m as rtfm::Monotonic>::Instant { + self.instant + } + )); + Some(quote!(instant: <#m as rtfm::Monotonic>::Instant,)) + } else { + None + }; + + items.push(quote!( + /// Tasks that can be spawned from this context + #[derive(Clone, Copy)] + pub struct Spawn<'a> { + #instant_field + priority: &'a rtfm::export::Priority, + } + )); + + let _instant = if needs_instant { + Some(quote!(, instant)) + } else { + None + }; + values.push(quote!( + spawn: Spawn { priority #_instant } + )); + } + + items.push(quote!( + impl<'a> Spawn<'a> { + #[doc(hidden)] + #[inline(always)] + pub unsafe fn priority(&self) -> &rtfm::export::Priority { + self.priority + } + + #instant_method + } + )); + } + } + + if let Context::Init(core) = ctxt { + let init = &app.inits[&core]; + if init.returns_late_resources { + let late_resources = util::late_resources_ident(&init.name); + + items.push(quote!( + #[doc(inline)] + pub use super::#late_resources as LateResources; + )); + } + } + + let doc = match ctxt { + Context::Idle(_) => "Idle loop", + Context::Init(_) => "Initialization function", + Context::HardwareTask(_) => "Hardware task", + Context::SoftwareTask(_) => "Software task", + }; + + let core = if ctxt.is_init() { + if app.uses_schedule(core) { + Some(quote!(core: rtfm::Peripherals,)) + } else { + Some(quote!(core: rtfm::export::Peripherals,)) + } + } else { + None + }; + + let priority = if ctxt.is_init() { + None + } else { + Some(quote!(priority: &#lt rtfm::export::Priority)) + }; + + let instant = if needs_instant { + let m = extra.monotonic(); + + Some(quote!(, instant: <#m as rtfm::Monotonic>::Instant)) + } else { + None + }; + + items.push(quote!( + /// Execution context + pub struct Context<#lt> { + #(#fields,)* + } + + impl<#lt> Context<#lt> { + #[inline(always)] + pub unsafe fn new(#core #priority #instant) -> Self { + Context { + #(#values,)* + } + } + } + )); + + if !items.is_empty() { + let cfg_core = util::cfg_core(ctxt.core(app), app.args.cores); + + quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #cfg_core + pub mod #name { + #(#items)* + } + ) + } else { + quote!() + } +} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs new file mode 100644 index 0000000..f492d31 --- /dev/null +++ b/macros/src/codegen/post_init.rs @@ -0,0 +1,139 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +/// Generates code that runs after `#[init]` returns +pub fn codegen( + core: u8, + analysis: &Analysis, + extra: &Extra, +) -> (Vec, Vec) { + let mut const_app = vec![]; + let mut stmts = vec![]; + + // initialize late resources + if let Some(late_resources) = analysis.late_resources.get(&core) { + for name in late_resources { + // if it's live + if analysis.locations.get(name).is_some() { + stmts.push(quote!(#name.as_mut_ptr().write(late.#name);)); + } + } + } + + if analysis.timer_queues.is_empty() { + // cross-initialization barriers -- notify *other* cores that their resources have been + // initialized + if analysis.initialization_barriers.contains_key(&core) { + let ib = util::init_barrier(core); + + const_app.push(quote!( + #[rtfm::export::shared] + static #ib: rtfm::export::Barrier = rtfm::export::Barrier::new(); + )); + + stmts.push(quote!( + #ib.release(); + )); + } + + // then wait until the other cores have initialized *our* resources + for (&initializer, users) in &analysis.initialization_barriers { + if users.contains(&core) { + let ib = util::init_barrier(initializer); + + stmts.push(quote!( + #ib.wait(); + )); + } + } + + // cross-spawn barriers: wait until other cores are ready to receive messages + for (&receiver, senders) in &analysis.spawn_barriers { + if senders.get(&core) == Some(&false) { + let sb = util::spawn_barrier(receiver); + + stmts.push(quote!( + #sb.wait(); + )); + } + } + } else { + // if the `schedule` API is used then we'll synchronize all cores to leave the + // `init`-ialization phase at the same time. In this case the rendezvous barrier makes the + // cross-initialization and spawn barriers unnecessary + + let m = extra.monotonic(); + + if analysis.timer_queues.len() == 1 { + // reset the monotonic timer / counter + stmts.push(quote!( + <#m as rtfm::Monotonic>::reset(); + )); + } else { + // in the multi-core case we need a rendezvous (RV) barrier between *all* the cores that + // use the `schedule` API; otherwise one of the cores could observe the before-reset + // value of the monotonic counter + // (this may be easier to implement with `AtomicU8.fetch_sub` but that API is not + // available on ARMv6-M) + + // this core will reset the monotonic counter + const FIRST: u8 = 0; + + if core == FIRST { + for &i in analysis.timer_queues.keys() { + let rv = util::rendezvous_ident(i); + + const_app.push(quote!( + #[rtfm::export::shared] + static #rv: rtfm::export::Barrier = rtfm::export::Barrier::new(); + )); + + // wait until all the other cores have reached the RV point + if i != FIRST { + stmts.push(quote!( + #rv.wait(); + )); + } + } + + let rv = util::rendezvous_ident(core); + stmts.push(quote!( + // the compiler fences are used to prevent `reset` from being re-ordering wrt to + // the atomic operations -- we don't know if `reset` contains load or store + // operations + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + + // reset the counter + <#m as rtfm::Monotonic>::reset(); + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + + // now unblock all the other cores + #rv.release(); + )); + } else { + let rv = util::rendezvous_ident(core); + + // let the first core know that we have reached the RV point + stmts.push(quote!( + #rv.release(); + )); + + let rv = util::rendezvous_ident(FIRST); + + // wait until the first core has reset the monotonic timer + stmts.push(quote!( + #rv.wait(); + )); + } + } + } + + // enable the interrupts -- this completes the `init`-ialization phase + stmts.push(quote!(rtfm::export::interrupt::enable();)); + + (const_app, stmts) +} diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs new file mode 100644 index 0000000..3ba17dc --- /dev/null +++ b/macros/src/codegen/pre_init.rs @@ -0,0 +1,150 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::ast::{App, HardwareTaskKind}; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +/// Generates code that runs before `#[init]` +pub fn codegen( + core: u8, + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> ( + // `const_app_pre_init` -- `static` variables for barriers + Vec, + // `pre_init_stmts` + Vec, +) { + let mut const_app = vec![]; + let mut stmts = vec![]; + + // disable interrupts -- `init` must run with interrupts disabled + stmts.push(quote!(rtfm::export::interrupt::disable();)); + + // populate this core `FreeQueue`s + for (name, senders) in &analysis.free_queues { + let task = &app.software_tasks[name]; + let cap = task.args.capacity; + + for &sender in senders.keys() { + if sender == core { + let fq = util::fq_ident(name, sender); + + stmts.push(quote!( + (0..#cap).for_each(|i| #fq.enqueue_unchecked(i)); + )); + } + } + } + + stmts.push(quote!( + let mut core = rtfm::export::Peripherals::steal(); + )); + + let device = extra.device; + let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); + + // unmask interrupts and set their priorities + for (&priority, name) in analysis + .interrupts + .get(&core) + .iter() + .flat_map(|interrupts| *interrupts) + .chain(app.hardware_tasks.iter().flat_map(|(name, task)| { + if task.kind == HardwareTaskKind::Interrupt { + Some((&task.args.priority, task.args.binds(name))) + } else { + // we do exceptions in another pass + None + } + })) + { + // compile time assert that this priority is supported by the device + stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); + + // NOTE this also checks that the interrupt exists in the `Interrupt` enumeration + stmts.push(quote!( + core.NVIC.set_priority( + #device::Interrupt::#name, + rtfm::export::logical2hw(#priority, #nvic_prio_bits), + ); + )); + + // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended + // interrupt is implementation defined + stmts.push(quote!(core.NVIC.enable(#device::Interrupt::#name);)); + } + + // cross-spawn barriers: now that priorities have been set and the interrupts have been unmasked + // we are ready to receive messages from *other* cores + if analysis.spawn_barriers.contains_key(&core) { + let sb = util::spawn_barrier(core); + + const_app.push(quote!( + #[rtfm::export::shared] + static #sb: rtfm::export::Barrier = rtfm::export::Barrier::new(); + )); + + // unblock cores that may send us a message + stmts.push(quote!( + #sb.release(); + )); + } + + // set exception priorities + for (name, priority) in app.hardware_tasks.iter().filter_map(|(name, task)| { + if task.kind == HardwareTaskKind::Exception { + Some((task.args.binds(name), task.args.priority)) + } else { + None + } + }) { + // compile time assert that this priority is supported by the device + stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); + + stmts.push(quote!(core.SCB.set_priority( + rtfm::export::SystemHandler::#name, + rtfm::export::logical2hw(#priority, #nvic_prio_bits), + );)); + } + + // initialize the SysTick + if let Some(tq) = analysis.timer_queues.get(&core) { + let priority = tq.priority; + + // compile time assert that this priority is supported by the device + stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); + + stmts.push(quote!(core.SCB.set_priority( + rtfm::export::SystemHandler::SysTick, + rtfm::export::logical2hw(#priority, #nvic_prio_bits), + );)); + + stmts.push(quote!( + core.SYST.set_clock_source(rtfm::export::SystClkSource::Core); + core.SYST.enable_counter(); + core.DCB.enable_trace(); + )); + } + + // if there's no user `#[idle]` then optimize returning from interrupt handlers + if app.idles.get(&core).is_none() { + // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR + stmts.push(quote!(core.SCB.scr.modify(|r| r | 1 << 1);)); + } + + // cross-spawn barriers: wait until other cores are ready to receive messages + for (&receiver, senders) in &analysis.spawn_barriers { + // only block here if `init` can send messages to `receiver` + if senders.get(&core) == Some(&true) { + let sb = util::spawn_barrier(receiver); + + stmts.push(quote!( + #sb.wait(); + )); + } + } + + (const_app, stmts) +} diff --git a/macros/src/codegen/resources.rs b/macros/src/codegen/resources.rs new file mode 100644 index 0000000..2dd10ea --- /dev/null +++ b/macros/src/codegen/resources.rs @@ -0,0 +1,115 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ + analyze::{Location, Ownership}, + ast::App, +}; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +/// Generates `static [mut]` variables and resource proxies +pub fn codegen( + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> ( + // const_app -- the `static [mut]` variables behind the proxies + Vec, + // mod_resources -- the `resources` module + TokenStream2, +) { + let mut const_app = vec![]; + let mut mod_resources = vec![]; + + for (name, res, expr, loc) in app.resources(analysis) { + let cfgs = &res.cfgs; + let ty = &res.ty; + + { + let loc_attr = match loc { + Location::Owned { + core, + cross_initialized: false, + } => util::cfg_core(*core, app.args.cores), + + // shared `static`s and cross-initialized resources need to be in `.shared` memory + _ => Some(quote!(#[rtfm::export::shared])), + }; + + let (ty, expr) = if let Some(expr) = expr { + (quote!(#ty), quote!(#expr)) + } else { + ( + quote!(core::mem::MaybeUninit<#ty>), + quote!(core::mem::MaybeUninit::uninit()), + ) + }; + + let attrs = &res.attrs; + const_app.push(quote!( + #loc_attr + #(#attrs)* + #(#cfgs)* + static mut #name: #ty = #expr; + )); + } + + // generate a resource proxy if needed + if res.mutability.is_some() { + if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) { + let cfg_core = util::cfg_core(loc.core().expect("UNREACHABLE"), app.args.cores); + + mod_resources.push(quote!( + #(#cfgs)* + #cfg_core + pub struct #name<'a> { + priority: &'a Priority, + } + + #(#cfgs)* + #cfg_core + impl<'a> #name<'a> { + #[inline(always)] + pub unsafe fn new(priority: &'a Priority) -> Self { + #name { priority } + } + + #[inline(always)] + pub unsafe fn priority(&self) -> &Priority { + self.priority + } + } + )); + + let ptr = if expr.is_none() { + quote!(#name.as_mut_ptr()) + } else { + quote!(&mut #name) + }; + + const_app.push(util::impl_mutex( + extra, + cfgs, + cfg_core.as_ref(), + true, + name, + quote!(#ty), + *ceiling, + ptr, + )); + } + } + } + + let mod_resources = if mod_resources.is_empty() { + quote!() + } else { + quote!(mod resources { + use rtfm::export::Priority; + + #(#mod_resources)* + }) + }; + + (const_app, mod_resources) +} diff --git a/macros/src/codegen/resources_struct.rs b/macros/src/codegen/resources_struct.rs new file mode 100644 index 0000000..0248f19 --- /dev/null +++ b/macros/src/codegen/resources_struct.rs @@ -0,0 +1,178 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{analyze::Analysis, codegen::util}; + +pub fn codegen( + ctxt: Context, + priority: u8, + needs_lt: &mut bool, + app: &App, + analysis: &Analysis, +) -> (TokenStream2, TokenStream2) { + let mut lt = None; + + let resources = match ctxt { + Context::Init(core) => &app.inits[&core].args.resources, + Context::Idle(core) => &app.idles[&core].args.resources, + Context::HardwareTask(name) => &app.hardware_tasks[name].args.resources, + Context::SoftwareTask(name) => &app.software_tasks[name].args.resources, + }; + + let mut fields = vec![]; + let mut values = vec![]; + let mut has_cfgs = false; + + for name in resources { + let (res, expr) = app.resource(name).expect("UNREACHABLE"); + + let cfgs = &res.cfgs; + has_cfgs |= !cfgs.is_empty(); + + let mut_ = res.mutability; + let ty = &res.ty; + + if ctxt.is_init() { + if !analysis.ownerships.contains_key(name) { + // owned by `init` + fields.push(quote!( + #(#cfgs)* + pub #name: &'static #mut_ #ty + )); + + values.push(quote!( + #(#cfgs)* + #name: &#mut_ #name + )); + } else { + // owned by someone else + lt = Some(quote!('a)); + + fields.push(quote!( + #(#cfgs)* + pub #name: &'a mut #ty + )); + + values.push(quote!( + #(#cfgs)* + #name: &mut #name + )); + } + } else { + let ownership = &analysis.ownerships[name]; + + if ownership.needs_lock(priority) { + if mut_.is_none() { + lt = Some(quote!('a)); + + fields.push(quote!( + #(#cfgs)* + pub #name: &'a #ty + )); + } else { + // resource proxy + lt = Some(quote!('a)); + + fields.push(quote!( + #(#cfgs)* + pub #name: resources::#name<'a> + )); + + values.push(quote!( + #(#cfgs)* + #name: resources::#name::new(priority) + + )); + + continue; + } + } else { + let lt = if ctxt.runs_once() { + quote!('static) + } else { + lt = Some(quote!('a)); + quote!('a) + }; + + if ownership.is_owned() || mut_.is_none() { + fields.push(quote!( + #(#cfgs)* + pub #name: &#lt #mut_ #ty + )); + } else { + fields.push(quote!( + #(#cfgs)* + pub #name: &#lt mut #ty + )); + } + } + + let is_late = expr.is_none(); + if is_late { + let expr = if mut_.is_some() { + quote!(&mut *#name.as_mut_ptr()) + } else { + quote!(&*#name.as_ptr()) + }; + + values.push(quote!( + #(#cfgs)* + #name: #expr + )); + } else { + values.push(quote!( + #(#cfgs)* + #name: &#mut_ #name + )); + } + } + } + + if lt.is_some() { + *needs_lt = true; + + // the struct could end up empty due to `cfg`s leading to an error due to `'a` being unused + if has_cfgs { + fields.push(quote!( + #[doc(hidden)] + pub __marker__: core::marker::PhantomData<&'a ()> + )); + + values.push(quote!(__marker__: core::marker::PhantomData)) + } + } + + let core = ctxt.core(app); + let cores = app.args.cores; + let cfg_core = util::cfg_core(core, cores); + let doc = format!("Resources `{}` has access to", ctxt.ident(app)); + let ident = util::resources_ident(ctxt, app); + let item = quote!( + #cfg_core + #[allow(non_snake_case)] + #[doc = #doc] + pub struct #ident<#lt> { + #(#fields,)* + } + ); + + let arg = if ctxt.is_init() { + None + } else { + Some(quote!(priority: &#lt rtfm::export::Priority)) + }; + let constructor = quote!( + #cfg_core + impl<#lt> #ident<#lt> { + #[inline(always)] + unsafe fn new(#arg) -> Self { + #ident { + #(#values,)* + } + } + } + ); + + (item, constructor) +} diff --git a/macros/src/codegen/schedule.rs b/macros/src/codegen/schedule.rs new file mode 100644 index 0000000..57f01a2 --- /dev/null +++ b/macros/src/codegen/schedule.rs @@ -0,0 +1,95 @@ +use std::collections::{BTreeMap, HashSet}; + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::ast::App; + +use crate::{ + check::Extra, + codegen::{schedule_body, util}, +}; + +/// Generates all `${ctxt}::Schedule` methods +pub fn codegen(app: &App, extra: &Extra) -> Vec { + let mut items = vec![]; + + let mut seen = BTreeMap::>::new(); + for (scheduler, schedulees) in app.schedule_callers() { + let m = extra.monotonic(); + let instant = quote!(<#m as rtfm::Monotonic>::Instant); + + let sender = scheduler.core(app); + let cfg_sender = util::cfg_core(sender, app.args.cores); + let seen = seen.entry(sender).or_default(); + let mut methods = vec![]; + + for name in schedulees { + let schedulee = &app.software_tasks[name]; + let cfgs = &schedulee.cfgs; + let (args, _, untupled, ty) = util::regroup_inputs(&schedulee.inputs); + let args = &args; + + if scheduler.is_init() { + // `init` uses a special `schedule` implementation; it doesn't use the + // `schedule_${name}` functions which are shared by other contexts + + let body = schedule_body::codegen(scheduler, &name, app); + + methods.push(quote!( + #(#cfgs)* + fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> { + #body + } + )); + } else { + let schedule = util::schedule_ident(name, sender); + + if !seen.contains(name) { + // generate a `schedule_${name}_S${sender}` function + seen.insert(name); + + let body = schedule_body::codegen(scheduler, &name, app); + + items.push(quote!( + #cfg_sender + #(#cfgs)* + unsafe fn #schedule( + priority: &rtfm::export::Priority, + instant: #instant + #(,#args)* + ) -> Result<(), #ty> { + #body + } + )); + } + + methods.push(quote!( + #(#cfgs)* + #[inline(always)] + fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> { + unsafe { + #schedule(self.priority(), instant #(,#untupled)*) + } + } + )); + } + } + + let lt = if scheduler.is_init() { + None + } else { + Some(quote!('a)) + }; + + let scheduler = scheduler.ident(app); + debug_assert!(!methods.is_empty()); + items.push(quote!( + #cfg_sender + impl<#lt> #scheduler::Schedule<#lt> { + #(#methods)* + } + )); + } + + items +} diff --git a/macros/src/codegen/schedule_body.rs b/macros/src/codegen/schedule_body.rs new file mode 100644 index 0000000..208fd0b --- /dev/null +++ b/macros/src/codegen/schedule_body.rs @@ -0,0 +1,61 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; +use syn::Ident; + +use crate::codegen::util; + +pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 { + let sender = scheduler.core(app); + let schedulee = &app.software_tasks[name]; + let receiver = schedulee.args.core; + + let fq = util::fq_ident(name, sender); + let tq = util::tq_ident(sender); + let (dequeue, enqueue) = if scheduler.is_init() { + (quote!(#fq.dequeue()), quote!(#tq.enqueue_unchecked(nr);)) + } else { + ( + quote!((#fq { priority }).lock(|fq| fq.split().1.dequeue())), + quote!((#tq { priority }).lock(|tq| tq.enqueue_unchecked(nr));), + ) + }; + + let write_instant = if app.uses_schedule(receiver) { + let instants = util::instants_ident(name, sender); + + Some(quote!( + #instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant); + )) + } else { + None + }; + + let (_, tupled, _, _) = util::regroup_inputs(&schedulee.inputs); + let inputs = util::inputs_ident(name, sender); + let t = util::schedule_t_ident(sender); + quote!( + unsafe { + use rtfm::Mutex as _; + + let input = #tupled; + if let Some(index) = #dequeue { + #inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(input); + + #write_instant + + let nr = rtfm::export::NotReady { + instant, + index, + task: #t::#name, + }; + + #enqueue + + Ok(()) + } else { + Err(input) + } + } + ) +} diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs new file mode 100644 index 0000000..8b2c0cd --- /dev/null +++ b/macros/src/codegen/software_tasks.rs @@ -0,0 +1,169 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; + +use crate::{ + analyze::Analysis, + check::Extra, + codegen::{locals, module, resources_struct, util}, +}; + +pub fn codegen( + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> ( + // const_app_software_tasks -- free queues, buffers and `${task}Resources` constructors + Vec, + // root_software_tasks -- items that must be placed in the root of the crate: + // - `${task}Locals` structs + // - `${task}Resources` structs + // - `${task}` modules + Vec, + // user_software_tasks -- the `#[task]` functions written by the user + Vec, +) { + let mut const_app = vec![]; + let mut root = vec![]; + let mut user_tasks = vec![]; + + for (name, task) in &app.software_tasks { + let receiver = task.args.core; + + let inputs = &task.inputs; + let (_, _, _, input_ty) = util::regroup_inputs(inputs); + + let cap = task.args.capacity; + let cap_lit = util::capacity_literal(cap); + let cap_ty = util::capacity_typenum(cap, true); + + // create free queues and inputs / instants buffers + if let Some(free_queues) = analysis.free_queues.get(name) { + for (&sender, &ceiling) in free_queues { + let cfg_sender = util::cfg_core(sender, app.args.cores); + let fq = util::fq_ident(name, sender); + + let (loc, fq_ty, fq_expr) = if receiver == sender { + ( + cfg_sender.clone(), + quote!(rtfm::export::SCFQ<#cap_ty>), + quote!(rtfm::export::Queue(unsafe { + rtfm::export::iQueue::u8_sc() + })), + ) + } else { + ( + Some(quote!(#[rtfm::export::shared])), + quote!(rtfm::export::MCFQ<#cap_ty>), + quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), + ) + }; + let loc = &loc; + + const_app.push(quote!( + /// Queue version of a free-list that keeps track of empty slots in + /// the following buffers + #loc + static mut #fq: #fq_ty = #fq_expr; + )); + + // Generate a resource proxy if needed + if let Some(ceiling) = ceiling { + const_app.push(quote!( + #cfg_sender + struct #fq<'a> { + priority: &'a rtfm::export::Priority, + } + )); + + const_app.push(util::impl_mutex( + extra, + &[], + cfg_sender.as_ref(), + false, + &fq, + fq_ty, + ceiling, + quote!(&mut #fq), + )); + } + + let ref elems = (0..cap) + .map(|_| quote!(core::mem::MaybeUninit::uninit())) + .collect::>(); + + if app.uses_schedule(receiver) { + let m = extra.monotonic(); + let instants = util::instants_ident(name, sender); + + const_app.push(quote!( + #loc + /// Buffer that holds the instants associated to the inputs of a task + static mut #instants: + [core::mem::MaybeUninit<<#m as rtfm::Monotonic>::Instant>; #cap_lit] = + [#(#elems,)*]; + )); + } + + let inputs = util::inputs_ident(name, sender); + const_app.push(quote!( + #loc + /// Buffer that holds the inputs of a task + static mut #inputs: [core::mem::MaybeUninit<#input_ty>; #cap_lit] = + [#(#elems,)*]; + )); + } + } + + // `${task}Resources` + let mut needs_lt = false; + if !task.args.resources.is_empty() { + let (item, constructor) = resources_struct::codegen( + Context::SoftwareTask(name), + task.args.priority, + &mut needs_lt, + app, + analysis, + ); + + root.push(item); + + const_app.push(constructor); + } + + // `${task}Locals` + let mut locals_pat = None; + if !task.locals.is_empty() { + let (struct_, pat) = locals::codegen(Context::SoftwareTask(name), &task.locals, app); + + locals_pat = Some(pat); + root.push(struct_); + } + + let cfg_receiver = util::cfg_core(receiver, app.args.cores); + let context = &task.context; + let attrs = &task.attrs; + let cfgs = &task.cfgs; + let stmts = &task.stmts; + user_tasks.push(quote!( + #(#attrs)* + #(#cfgs)* + #cfg_receiver + #[allow(non_snake_case)] + fn #name(#(#locals_pat,)* #context: #name::Context #(,#inputs)*) { + use rtfm::Mutex as _; + + #(#stmts)* + } + )); + + root.push(module::codegen( + Context::SoftwareTask(name), + needs_lt, + app, + extra, + )); + } + + (const_app, root, user_tasks) +} diff --git a/macros/src/codegen/spawn.rs b/macros/src/codegen/spawn.rs new file mode 100644 index 0000000..1539e27 --- /dev/null +++ b/macros/src/codegen/spawn.rs @@ -0,0 +1,127 @@ +use std::collections::{BTreeMap, HashSet}; + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::ast::App; + +use crate::{ + analyze::Analysis, + check::Extra, + codegen::{spawn_body, util}, +}; + +/// Generates all `${ctxt}::Spawn` methods +pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { + let mut items = vec![]; + + let mut seen = BTreeMap::>::new(); + for (spawner, spawnees) in app.spawn_callers() { + let sender = spawner.core(app); + let cfg_sender = util::cfg_core(sender, app.args.cores); + let seen = seen.entry(sender).or_default(); + let mut methods = vec![]; + + for name in spawnees { + let spawnee = &app.software_tasks[name]; + let receiver = spawnee.args.core; + let cfgs = &spawnee.cfgs; + let (args, _, untupled, ty) = util::regroup_inputs(&spawnee.inputs); + let args = &args; + + if spawner.is_init() { + // `init` uses a special spawn implementation; it doesn't use the `spawn_${name}` + // functions which are shared by other contexts + + let body = spawn_body::codegen(spawner, &name, app, analysis, extra); + + let let_instant = if app.uses_schedule(receiver) { + let m = extra.monotonic(); + + Some(quote!(let instant = unsafe { <#m as rtfm::Monotonic>::zero() };)) + } else { + None + }; + + methods.push(quote!( + #(#cfgs)* + fn #name(&self #(,#args)*) -> Result<(), #ty> { + #let_instant + #body + } + )); + } else { + let spawn = util::spawn_ident(name, sender); + + if !seen.contains(name) { + // generate a `spawn_${name}_S${sender}` function + seen.insert(name); + + let instant = if app.uses_schedule(receiver) { + let m = extra.monotonic(); + + Some(quote!(, instant: <#m as rtfm::Monotonic>::Instant)) + } else { + None + }; + + let body = spawn_body::codegen(spawner, &name, app, analysis, extra); + + items.push(quote!( + #cfg_sender + #(#cfgs)* + unsafe fn #spawn( + priority: &rtfm::export::Priority + #instant + #(,#args)* + ) -> Result<(), #ty> { + #body + } + )); + } + + let (let_instant, instant) = if app.uses_schedule(receiver) { + let m = extra.monotonic(); + + ( + Some(if spawner.is_idle() { + quote!(let instant = <#m as rtfm::Monotonic>::now();) + } else { + quote!(let instant = self.instant();) + }), + Some(quote!(, instant)), + ) + } else { + (None, None) + }; + + methods.push(quote!( + #(#cfgs)* + #[inline(always)] + fn #name(&self #(,#args)*) -> Result<(), #ty> { + unsafe { + #let_instant + #spawn(self.priority() #instant #(,#untupled)*) + } + } + )); + } + } + + let lt = if spawner.is_init() { + None + } else { + Some(quote!('a)) + }; + + let spawner = spawner.ident(app); + debug_assert!(!methods.is_empty()); + items.push(quote!( + #cfg_sender + impl<#lt> #spawner::Spawn<#lt> { + #(#methods)* + } + )); + } + + items +} diff --git a/macros/src/codegen/spawn_body.rs b/macros/src/codegen/spawn_body.rs new file mode 100644 index 0000000..83cb5c0 --- /dev/null +++ b/macros/src/codegen/spawn_body.rs @@ -0,0 +1,81 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::{ast::App, Context}; +use syn::Ident; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +pub fn codegen( + spawner: Context, + name: &Ident, + app: &App, + analysis: &Analysis, + extra: &Extra, +) -> TokenStream2 { + let sender = spawner.core(app); + let spawnee = &app.software_tasks[name]; + let priority = spawnee.args.priority; + let receiver = spawnee.args.core; + + let write_instant = if app.uses_schedule(receiver) { + let instants = util::instants_ident(name, sender); + + Some(quote!( + #instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant); + )) + } else { + None + }; + + let t = util::spawn_t_ident(receiver, priority, sender); + let fq = util::fq_ident(name, sender); + let rq = util::rq_ident(receiver, priority, sender); + let (dequeue, enqueue) = if spawner.is_init() { + ( + quote!(#fq.dequeue()), + quote!(#rq.enqueue_unchecked((#t::#name, index));), + ) + } else { + ( + quote!((#fq { priority }.lock(|fq| fq.split().1.dequeue()))), + quote!((#rq { priority }.lock(|rq| { + rq.split().0.enqueue_unchecked((#t::#name, index)) + }));), + ) + }; + + let device = extra.device; + let interrupt = &analysis.interrupts[&receiver][&priority]; + let pend = if sender != receiver { + quote!( + #device::xpend(#receiver, #device::Interrupt::#interrupt); + ) + } else { + quote!( + rtfm::pend(#device::Interrupt::#interrupt); + ) + }; + + let (_, tupled, _, _) = util::regroup_inputs(&spawnee.inputs); + let inputs = util::inputs_ident(name, sender); + quote!( + unsafe { + use rtfm::Mutex as _; + + let input = #tupled; + if let Some(index) = #dequeue { + #inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(input); + + #write_instant + + #enqueue + + #pend + + Ok(()) + } else { + Err(input) + } + } + ) +} diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs new file mode 100644 index 0000000..cb84577 --- /dev/null +++ b/macros/src/codegen/timer_queue.rs @@ -0,0 +1,147 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use rtfm_syntax::ast::App; + +use crate::{analyze::Analysis, check::Extra, codegen::util}; + +/// Generates timer queues and timer queue handlers +pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { + let mut items = vec![]; + + for (&sender, timer_queue) in &analysis.timer_queues { + let cfg_sender = util::cfg_core(sender, app.args.cores); + let t = util::schedule_t_ident(sender); + + // Enumeration of `schedule`-able tasks + { + let variants = timer_queue + .tasks + .iter() + .map(|name| { + let cfgs = &app.software_tasks[name].cfgs; + + quote!( + #(#cfgs)* + #name + ) + }) + .collect::>(); + + let doc = format!("Tasks that can be scheduled from core #{}", sender); + items.push(quote!( + #cfg_sender + #[doc = #doc] + #[allow(non_camel_case_types)] + #[derive(Clone, Copy)] + enum #t { + #(#variants,)* + } + )); + } + + let tq = util::tq_ident(sender); + + // Static variable and resource proxy + { + let doc = format!("Core #{} timer queue", sender); + let m = extra.monotonic(); + let n = util::capacity_typenum(timer_queue.capacity, false); + let tq_ty = quote!(rtfm::export::TimerQueue<#m, #t, #n>); + + items.push(quote!( + #cfg_sender + #[doc = #doc] + static mut #tq: #tq_ty = rtfm::export::TimerQueue( + rtfm::export::BinaryHeap( + rtfm::export::iBinaryHeap::new() + ) + ); + + #cfg_sender + struct #tq<'a> { + priority: &'a rtfm::export::Priority, + } + )); + + items.push(util::impl_mutex( + extra, + &[], + cfg_sender.as_ref(), + false, + &tq, + tq_ty, + timer_queue.ceiling, + quote!(&mut #tq), + )); + } + + // Timer queue handler + { + let device = extra.device; + let arms = timer_queue + .tasks + .iter() + .map(|name| { + let task = &app.software_tasks[name]; + + let cfgs = &task.cfgs; + let priority = task.args.priority; + let receiver = task.args.core; + let rq = util::rq_ident(receiver, priority, sender); + let rqt = util::spawn_t_ident(receiver, priority, sender); + let interrupt = &analysis.interrupts[&receiver][&priority]; + + let pend = if sender != receiver { + quote!( + #device::xpend(#receiver, #device::Interrupt::#interrupt); + ) + } else { + quote!( + rtfm::pend(#device::Interrupt::#interrupt); + ) + }; + + quote!( + #(#cfgs)* + #t::#name => { + (#rq { priority: &rtfm::export::Priority::new(PRIORITY) }).lock(|rq| { + rq.split().0.enqueue_unchecked((#rqt::#name, index)) + }); + + #pend + } + ) + }) + .collect::>(); + + let priority = timer_queue.priority; + items.push(quote!( + #cfg_sender + #[no_mangle] + unsafe fn SysTick() { + use rtfm::Mutex as _; + + /// The priority of this handler + const PRIORITY: u8 = #priority; + + rtfm::export::run(PRIORITY, || { + while let Some((task, index)) = (#tq { + // NOTE dynamic priority is always the static priority at this point + priority: &rtfm::export::Priority::new(PRIORITY), + }) + // NOTE `inline(always)` produces faster and smaller code + .lock(#[inline(always)] + |tq| tq.dequeue()) + { + match task { + #(#arms)* + } + } + }); + } + )); + } + } + + items +} diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs new file mode 100644 index 0000000..203fcee --- /dev/null +++ b/macros/src/codegen/util.rs @@ -0,0 +1,253 @@ +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::quote; +use rtfm_syntax::{ast::App, Context, Core}; +use syn::{ArgCaptured, Attribute, Ident, IntSuffix, LitInt}; + +use crate::check::Extra; + +/// Turns `capacity` into an unsuffixed integer literal +pub fn capacity_literal(capacity: u8) -> LitInt { + LitInt::new(u64::from(capacity), IntSuffix::None, Span::call_site()) +} + +/// Turns `capacity` into a type-level (`typenum`) integer +pub fn capacity_typenum(capacity: u8, round_up_to_power_of_two: bool) -> TokenStream2 { + let capacity = if round_up_to_power_of_two { + capacity.checked_next_power_of_two().expect("UNREACHABLE") + } else { + capacity + }; + + let ident = Ident::new(&format!("U{}", capacity), Span::call_site()); + + quote!(rtfm::export::consts::#ident) +} + +/// Generates a `#[cfg(core = "0")]` attribute if we are in multi-core mode +pub fn cfg_core(core: Core, cores: u8) -> Option { + if cores == 1 { + None + } else { + let core = core.to_string(); + Some(quote!(#[cfg(core = #core)])) + } +} + +/// Identifier for the free queue +/// +/// There may be more than one free queue per task because we need one for each sender core so we +/// include the sender (e.g. `S0`) in the name +pub fn fq_ident(task: &Ident, sender: Core) -> Ident { + Ident::new( + &format!("{}_S{}_FQ", task.to_string(), sender), + Span::call_site(), + ) +} + +/// Generates a `Mutex` implementation +pub fn impl_mutex( + extra: &Extra, + cfgs: &[Attribute], + cfg_core: Option<&TokenStream2>, + resources_prefix: bool, + name: &Ident, + ty: TokenStream2, + ceiling: u8, + ptr: TokenStream2, +) -> TokenStream2 { + let (path, priority) = if resources_prefix { + (quote!(resources::#name), quote!(self.priority())) + } else { + (quote!(#name), quote!(self.priority)) + }; + + let device = extra.device; + quote!( + #(#cfgs)* + #cfg_core + impl<'a> rtfm::Mutex for #path<'a> { + type T = #ty; + + #[inline(always)] + fn lock(&mut self, f: impl FnOnce(&mut #ty) -> R) -> R { + /// Priority ceiling + const CEILING: u8 = #ceiling; + + unsafe { + rtfm::export::lock( + #ptr, + #priority, + CEILING, + #device::NVIC_PRIO_BITS, + f, + ) + } + } + } + ) +} + +/// Generates an identifier for a cross-initialization barrier +pub fn init_barrier(initializer: Core) -> Ident { + Ident::new(&format!("IB{}", initializer), Span::call_site()) +} + +/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API) +pub fn inputs_ident(task: &Ident, sender: Core) -> Ident { + Ident::new(&format!("{}_S{}_INPUTS", task, sender), Span::call_site()) +} + +/// Generates an identifier for the `INSTANTS` buffer (`schedule` API) +pub fn instants_ident(task: &Ident, sender: Core) -> Ident { + Ident::new(&format!("{}_S{}_INSTANTS", task, sender), Span::call_site()) +} + +/// Generates a pre-reexport identifier for the "late resources" struct +pub fn late_resources_ident(init: &Ident) -> Ident { + Ident::new( + &format!("{}LateResources", init.to_string()), + Span::call_site(), + ) +} + +/// Generates a pre-reexport identifier for the "locals" struct +pub fn locals_ident(ctxt: Context, app: &App) -> Ident { + let mut s = match ctxt { + Context::Init(core) => app.inits[&core].name.to_string(), + Context::Idle(core) => app.idles[&core].name.to_string(), + Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), + }; + + s.push_str("Locals"); + + Ident::new(&s, Span::call_site()) +} + +/// Generates an identifier for a rendezvous barrier +pub fn rendezvous_ident(core: Core) -> Ident { + Ident::new(&format!("RV{}", core), Span::call_site()) +} + +// Regroups the inputs of a task +// +// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] +pub fn regroup_inputs( + inputs: &[ArgCaptured], +) -> ( + // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] + Vec, + // tupled e.g. `_0`, `(_0, _1)` + TokenStream2, + // untupled e.g. &[`_0`], &[`_0`, `_1`] + Vec, + // ty e.g. `Foo`, `(i32, i64)` + TokenStream2, +) { + if inputs.len() == 1 { + let ty = &inputs[0].ty; + + ( + vec![quote!(_0: #ty)], + quote!(_0), + vec![quote!(_0)], + quote!(#ty), + ) + } else { + let mut args = vec![]; + let mut pats = vec![]; + let mut tys = vec![]; + + for (i, input) in inputs.iter().enumerate() { + let i = Ident::new(&format!("_{}", i), Span::call_site()); + let ty = &input.ty; + + args.push(quote!(#i: #ty)); + + pats.push(quote!(#i)); + + tys.push(quote!(#ty)); + } + + let tupled = { + let pats = pats.clone(); + quote!((#(#pats,)*)) + }; + let ty = quote!((#(#tys,)*)); + (args, tupled, pats, ty) + } +} + +/// Generates a pre-reexport identifier for the "resources" struct +pub fn resources_ident(ctxt: Context, app: &App) -> Ident { + let mut s = match ctxt { + Context::Init(core) => app.inits[&core].name.to_string(), + Context::Idle(core) => app.idles[&core].name.to_string(), + Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), + }; + + s.push_str("Resources"); + + Ident::new(&s, Span::call_site()) +} + +/// Generates an identifier for a ready queue +/// +/// Each core may have several task dispatchers, one for each priority level. Each task dispatcher +/// in turn may use more than one ready queue because the queues are SPSC queues so one is needed +/// per sender core. +pub fn rq_ident(receiver: Core, priority: u8, sender: Core) -> Ident { + Ident::new( + &format!("R{}_P{}_S{}_RQ", receiver, priority, sender), + Span::call_site(), + ) +} + +/// Generates an identifier for a "schedule" function +/// +/// The methods of the `Schedule` structs invoke these functions. As one task may be `schedule`-ed +/// by different cores we need one "schedule" function per possible task-sender pair +pub fn schedule_ident(name: &Ident, sender: Core) -> Ident { + Ident::new( + &format!("schedule_{}_S{}", name.to_string(), sender), + Span::call_site(), + ) +} + +/// Generates an identifier for the `enum` of `schedule`-able tasks +pub fn schedule_t_ident(core: Core) -> Ident { + Ident::new(&format!("T{}", core), Span::call_site()) +} + +/// Generates an identifier for a cross-spawn barrier +pub fn spawn_barrier(receiver: Core) -> Ident { + Ident::new(&format!("SB{}", receiver), Span::call_site()) +} + +/// Generates an identifier for a "spawn" function +/// +/// The methods of the `Spawn` structs invoke these functions. As one task may be `spawn`-ed by +/// different cores we need one "spawn" function per possible task-sender pair +pub fn spawn_ident(name: &Ident, sender: Core) -> Ident { + Ident::new( + &format!("spawn_{}_S{}", name.to_string(), sender), + Span::call_site(), + ) +} + +/// Generates an identifier for the `enum` of `spawn`-able tasks +/// +/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue +/// for each of these `T` enums +pub fn spawn_t_ident(receiver: Core, priority: u8, sender: Core) -> Ident { + Ident::new( + &format!("R{}_P{}_S{}_T", receiver, priority, sender), + Span::call_site(), + ) +} + +/// Generates an identifier for a timer queue +/// +/// At most there's one timer queue per core +pub fn tq_ident(core: Core) -> Ident { + Ident::new(&format!("TQ{}", core), Span::call_site()) +} -- cgit v1.2.3 From 9897728709528a02545523bea72576abce89dc4c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 18 Jun 2019 10:31:31 +0200 Subject: add homogeneous multi-core support --- Cargo.toml | 4 +- ci/script.sh | 4 +- heterogeneous/Cargo.toml | 18 +++++++ heterogeneous/README.md | 1 + heterogeneous/examples/smallest.rs | 7 +++ heterogeneous/examples/x-init-2.rs | 39 ++++++++++++++ heterogeneous/examples/x-init.rs | 26 ++++++++++ heterogeneous/examples/x-schedule.rs | 36 +++++++++++++ heterogeneous/examples/x-spawn.rs | 20 ++++++++ heterogeneous/src/lib.rs | 94 ++++++++++++++++++++++++++++++++++ homogeneous/Cargo.toml | 17 +++++++ homogeneous/README.md | 1 + homogeneous/examples/smallest.rs | 7 +++ homogeneous/examples/x-init-2.rs | 39 ++++++++++++++ homogeneous/examples/x-init.rs | 26 ++++++++++ homogeneous/examples/x-schedule.rs | 36 +++++++++++++ homogeneous/examples/x-spawn.rs | 20 ++++++++ homogeneous/src/lib.rs | 94 ++++++++++++++++++++++++++++++++++ macros/Cargo.toml | 1 + macros/src/check.rs | 22 ++++++++ macros/src/codegen.rs | 3 +- macros/src/codegen/dispatchers.rs | 10 +++- macros/src/codegen/hardware_tasks.rs | 6 ++- macros/src/codegen/post_init.rs | 18 ++++++- macros/src/codegen/pre_init.rs | 17 +++++-- macros/src/codegen/resources.rs | 8 ++- macros/src/codegen/software_tasks.rs | 8 ++- macros/src/codegen/spawn_body.rs | 5 +- macros/src/codegen/timer_queue.rs | 8 +-- macros/src/codegen/util.rs | 23 ++++++++- macros/src/lib.rs | 2 +- mc/Cargo.toml | 18 ------- mc/README.md | 1 - mc/examples/smallest.rs | 7 --- mc/examples/x-init-2.rs | 39 -------------- mc/examples/x-init.rs | 26 ---------- mc/examples/x-schedule.rs | 36 ------------- mc/examples/x-spawn.rs | 20 -------- mc/src/lib.rs | 99 ------------------------------------ src/lib.rs | 2 +- 40 files changed, 600 insertions(+), 268 deletions(-) create mode 100644 heterogeneous/Cargo.toml create mode 100644 heterogeneous/README.md create mode 100644 heterogeneous/examples/smallest.rs create mode 100644 heterogeneous/examples/x-init-2.rs create mode 100644 heterogeneous/examples/x-init.rs create mode 100644 heterogeneous/examples/x-schedule.rs create mode 100644 heterogeneous/examples/x-spawn.rs create mode 100644 heterogeneous/src/lib.rs create mode 100644 homogeneous/Cargo.toml create mode 100644 homogeneous/README.md create mode 100644 homogeneous/examples/smallest.rs create mode 100644 homogeneous/examples/x-init-2.rs create mode 100644 homogeneous/examples/x-init.rs create mode 100644 homogeneous/examples/x-schedule.rs create mode 100644 homogeneous/examples/x-spawn.rs create mode 100644 homogeneous/src/lib.rs delete mode 100644 mc/Cargo.toml delete mode 100644 mc/README.md delete mode 100644 mc/examples/smallest.rs delete mode 100644 mc/examples/x-init-2.rs delete mode 100644 mc/examples/x-init.rs delete mode 100644 mc/examples/x-schedule.rs delete mode 100644 mc/examples/x-spawn.rs delete mode 100644 mc/src/lib.rs (limited to 'macros/src/codegen') diff --git a/Cargo.toml b/Cargo.toml index 81ca256..ef45be8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ compiletest_rs = "0.3.22" [features] heterogeneous = ["cortex-m-rtfm-macros/heterogeneous", "microamp"] +homogeneous = ["cortex-m-rtfm-macros/homogeneous", "microamp"] # used for testing this crate; do not use in applications __v7 =[] @@ -83,6 +84,7 @@ lto = true [workspace] members = [ + "heterogeneous", + "homogeneous", "macros", - "mc", ] diff --git a/ci/script.sh b/ci/script.sh index a6485cf..1b3d561 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -43,7 +43,7 @@ main() { cargo test --test multi --features heterogeneous --target $T # multi-core compile-pass tests - pushd mc + pushd heterogeneous local exs=( smallest x-init-2 @@ -91,6 +91,8 @@ main() { cargo check --target $T --examples --features __v7 fi + cargo check -p homogeneous --target $T --examples + # run-pass tests case $T in thumbv6m-none-eabi | thumbv7m-none-eabi) diff --git a/heterogeneous/Cargo.toml b/heterogeneous/Cargo.toml new file mode 100644 index 0000000..fd05d07 --- /dev/null +++ b/heterogeneous/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["Jorge Aparicio "] +edition = "2018" +name = "heterogeneous" +# this crate is only used for testing +publish = false +version = "0.0.0-alpha.0" + +[dependencies] +bare-metal = "0.2.4" + +[dependencies.cortex-m-rtfm] +path = ".." +features = ["heterogeneous"] + +[dev-dependencies] +panic-halt = "0.2.0" +microamp = "0.1.0-alpha.1" diff --git a/heterogeneous/README.md b/heterogeneous/README.md new file mode 100644 index 0000000..8e49ff8 --- /dev/null +++ b/heterogeneous/README.md @@ -0,0 +1 @@ +This directory contains *heterogeneous* multi-core compile pass tests. diff --git a/heterogeneous/examples/smallest.rs b/heterogeneous/examples/smallest.rs new file mode 100644 index 0000000..9b6bb82 --- /dev/null +++ b/heterogeneous/examples/smallest.rs @@ -0,0 +1,7 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = {}; diff --git a/heterogeneous/examples/x-init-2.rs b/heterogeneous/examples/x-init-2.rs new file mode 100644 index 0000000..b9c3919 --- /dev/null +++ b/heterogeneous/examples/x-init-2.rs @@ -0,0 +1,39 @@ +//! [compile-pass] Cross initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = { + extern "C" { + // owned by core #1 but initialized by core #0 + static mut X: u32; + + // owned by core #0 but initialized by core #1 + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[idle(core = 0, resources = [Y])] + fn b(_: b::Context) -> ! { + loop {} + } + + #[init(core = 1)] + fn c(_: c::Context) -> c::LateResources { + c::LateResources { Y: 0 } + } + + #[idle(core = 1, resources = [X])] + fn d(_: d::Context) -> ! { + loop {} + } +}; diff --git a/heterogeneous/examples/x-init.rs b/heterogeneous/examples/x-init.rs new file mode 100644 index 0000000..53e7380 --- /dev/null +++ b/heterogeneous/examples/x-init.rs @@ -0,0 +1,26 @@ +//! [compile-pass] Split initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = { + extern "C" { + static mut X: u32; + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[init(core = 1)] + fn b(_: b::Context) -> b::LateResources { + b::LateResources { Y: 0 } + } +}; diff --git a/heterogeneous/examples/x-schedule.rs b/heterogeneous/examples/x-schedule.rs new file mode 100644 index 0000000..cbfc01f --- /dev/null +++ b/heterogeneous/examples/x-schedule.rs @@ -0,0 +1,36 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous, monotonic = heterogeneous::MT)] +const APP: () = { + #[init(core = 0, spawn = [ping])] + fn init(c: init::Context) { + c.spawn.ping().ok(); + } + + #[task(core = 0, schedule = [ping])] + fn pong(c: pong::Context) { + c.schedule.ping(c.scheduled + 1_000_000).ok(); + } + + #[task(core = 1, schedule = [pong])] + fn ping(c: ping::Context) { + c.schedule.pong(c.scheduled + 1_000_000).ok(); + } + + extern "C" { + #[core = 0] + fn I0(); + + #[core = 0] + fn I1(); + + #[core = 1] + fn I0(); + + #[core = 1] + fn I1(); + } +}; diff --git a/heterogeneous/examples/x-spawn.rs b/heterogeneous/examples/x-spawn.rs new file mode 100644 index 0000000..3fc64f6 --- /dev/null +++ b/heterogeneous/examples/x-spawn.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = heterogeneous)] +const APP: () = { + #[init(core = 0, spawn = [foo])] + fn init(c: init::Context) { + c.spawn.foo().ok(); + } + + #[task(core = 1)] + fn foo(_: foo::Context) {} + + extern "C" { + #[core = 1] + fn I0(); + } +}; diff --git a/heterogeneous/src/lib.rs b/heterogeneous/src/lib.rs new file mode 100644 index 0000000..a4f0ec5 --- /dev/null +++ b/heterogeneous/src/lib.rs @@ -0,0 +1,94 @@ +//! Fake multi-core PAC + +#![no_std] + +use core::{ + cmp::Ordering, + ops::{Add, Sub}, +}; + +use bare_metal::Nr; +use rtfm::Monotonic; + +// both cores have the exact same interrupts +pub use Interrupt_0 as Interrupt_1; + +// Fake priority bits +pub const NVIC_PRIO_BITS: u8 = 3; + +pub fn xpend(_core: u8, _interrupt: impl Nr) {} + +/// Fake monotonic timer +pub struct MT; + +unsafe impl Monotonic for MT { + type Instant = Instant; + + fn ratio() -> u32 { + 1 + } + + unsafe fn reset() { + (0xE0001004 as *mut u32).write_volatile(0) + } + + fn now() -> Instant { + unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) } + } + + fn zero() -> Instant { + Instant(0) + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Instant(i32); + +impl Add for Instant { + type Output = Instant; + + fn add(self, rhs: u32) -> Self { + Instant(self.0.wrapping_add(rhs as i32)) + } +} + +impl Sub for Instant { + type Output = u32; + + fn sub(self, rhs: Self) -> u32 { + self.0.checked_sub(rhs.0).unwrap() as u32 + } +} + +impl Ord for Instant { + fn cmp(&self, rhs: &Self) -> Ordering { + self.0.wrapping_sub(rhs.0).cmp(&0) + } +} + +impl PartialOrd for Instant { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +// Fake interrupts +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum Interrupt_0 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, +} + +unsafe impl Nr for Interrupt_0 { + fn nr(&self) -> u8 { + *self as u8 + } +} diff --git a/homogeneous/Cargo.toml b/homogeneous/Cargo.toml new file mode 100644 index 0000000..210ee2e --- /dev/null +++ b/homogeneous/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["Jorge Aparicio "] +edition = "2018" +name = "homogeneous" +# this crate is only used for testing +publish = false +version = "0.0.0-alpha.0" + +[dependencies] +bare-metal = "0.2.4" + +[dependencies.cortex-m-rtfm] +path = ".." +features = ["homogeneous"] + +[dev-dependencies] +panic-halt = "0.2.0" diff --git a/homogeneous/README.md b/homogeneous/README.md new file mode 100644 index 0000000..17e9c6e --- /dev/null +++ b/homogeneous/README.md @@ -0,0 +1 @@ +This directory contains *homogeneous* multi-core compile pass tests. diff --git a/homogeneous/examples/smallest.rs b/homogeneous/examples/smallest.rs new file mode 100644 index 0000000..b99476c --- /dev/null +++ b/homogeneous/examples/smallest.rs @@ -0,0 +1,7 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = {}; diff --git a/homogeneous/examples/x-init-2.rs b/homogeneous/examples/x-init-2.rs new file mode 100644 index 0000000..f51e2f6 --- /dev/null +++ b/homogeneous/examples/x-init-2.rs @@ -0,0 +1,39 @@ +//! [compile-pass] Cross initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = { + extern "C" { + // owned by core #1 but initialized by core #0 + static mut X: u32; + + // owned by core #0 but initialized by core #1 + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[idle(core = 0, resources = [Y])] + fn b(_: b::Context) -> ! { + loop {} + } + + #[init(core = 1)] + fn c(_: c::Context) -> c::LateResources { + c::LateResources { Y: 0 } + } + + #[idle(core = 1, resources = [X])] + fn d(_: d::Context) -> ! { + loop {} + } +}; diff --git a/homogeneous/examples/x-init.rs b/homogeneous/examples/x-init.rs new file mode 100644 index 0000000..5089e38 --- /dev/null +++ b/homogeneous/examples/x-init.rs @@ -0,0 +1,26 @@ +//! [compile-pass] Split initialization of late resources + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = { + extern "C" { + static mut X: u32; + static mut Y: u32; + } + + #[init(core = 0, late = [X])] + fn a(_: a::Context) -> a::LateResources { + a::LateResources { X: 0 } + } + + #[init(core = 1)] + fn b(_: b::Context) -> b::LateResources { + b::LateResources { Y: 0 } + } +}; diff --git a/homogeneous/examples/x-schedule.rs b/homogeneous/examples/x-schedule.rs new file mode 100644 index 0000000..12b5cb8 --- /dev/null +++ b/homogeneous/examples/x-schedule.rs @@ -0,0 +1,36 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous, monotonic = homogeneous::MT)] +const APP: () = { + #[init(core = 0, spawn = [ping])] + fn init(c: init::Context) { + c.spawn.ping().ok(); + } + + #[task(core = 0, schedule = [ping])] + fn pong(c: pong::Context) { + c.schedule.ping(c.scheduled + 1_000_000).ok(); + } + + #[task(core = 1, schedule = [pong])] + fn ping(c: ping::Context) { + c.schedule.pong(c.scheduled + 1_000_000).ok(); + } + + extern "C" { + #[core = 0] + fn I0(); + + #[core = 0] + fn I1(); + + #[core = 1] + fn I0(); + + #[core = 1] + fn I1(); + } +}; diff --git a/homogeneous/examples/x-spawn.rs b/homogeneous/examples/x-spawn.rs new file mode 100644 index 0000000..a76ac61 --- /dev/null +++ b/homogeneous/examples/x-spawn.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +#[rtfm::app(cores = 2, device = homogeneous)] +const APP: () = { + #[init(core = 0, spawn = [foo])] + fn init(c: init::Context) { + c.spawn.foo().ok(); + } + + #[task(core = 1)] + fn foo(_: foo::Context) {} + + extern "C" { + #[core = 1] + fn I0(); + } +}; diff --git a/homogeneous/src/lib.rs b/homogeneous/src/lib.rs new file mode 100644 index 0000000..a4f0ec5 --- /dev/null +++ b/homogeneous/src/lib.rs @@ -0,0 +1,94 @@ +//! Fake multi-core PAC + +#![no_std] + +use core::{ + cmp::Ordering, + ops::{Add, Sub}, +}; + +use bare_metal::Nr; +use rtfm::Monotonic; + +// both cores have the exact same interrupts +pub use Interrupt_0 as Interrupt_1; + +// Fake priority bits +pub const NVIC_PRIO_BITS: u8 = 3; + +pub fn xpend(_core: u8, _interrupt: impl Nr) {} + +/// Fake monotonic timer +pub struct MT; + +unsafe impl Monotonic for MT { + type Instant = Instant; + + fn ratio() -> u32 { + 1 + } + + unsafe fn reset() { + (0xE0001004 as *mut u32).write_volatile(0) + } + + fn now() -> Instant { + unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) } + } + + fn zero() -> Instant { + Instant(0) + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Instant(i32); + +impl Add for Instant { + type Output = Instant; + + fn add(self, rhs: u32) -> Self { + Instant(self.0.wrapping_add(rhs as i32)) + } +} + +impl Sub for Instant { + type Output = u32; + + fn sub(self, rhs: Self) -> u32 { + self.0.checked_sub(rhs.0).unwrap() as u32 + } +} + +impl Ord for Instant { + fn cmp(&self, rhs: &Self) -> Ordering { + self.0.wrapping_sub(rhs.0).cmp(&0) + } +} + +impl PartialOrd for Instant { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +// Fake interrupts +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum Interrupt_0 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, +} + +unsafe impl Nr for Interrupt_0 { + fn nr(&self) -> u8 { + *self as u8 + } +} diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 2854dad..c4e897f 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -24,3 +24,4 @@ git = "https://github.com/japaric/rtfm-syntax" [features] heterogeneous = [] +homogeneous = [] diff --git a/macros/src/check.rs b/macros/src/check.rs index c22a0f1..619ec8f 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -20,6 +20,28 @@ impl<'a> Extra<'a> { } pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result> { + if cfg!(feature = "homogeneous") { + // this RTFM mode uses the same namespace for all cores so we need to check that the + // identifiers used for each core `#[init]` and `#[idle]` functions don't collide + let mut seen = HashSet::new(); + + for name in app + .inits + .values() + .map(|init| &init.name) + .chain(app.idles.values().map(|idle| &idle.name)) + { + if seen.contains(name) { + return Err(parse::Error::new( + name.span(), + "this identifier is already being used by another core", + )); + } else { + seen.insert(name); + } + } + } + // check that all exceptions are valid; only exceptions with configurable priorities are // accepted for (name, task) in app diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 86b4a67..9276626 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -67,10 +67,11 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { )); let cfg_core = util::cfg_core(core, app.args.cores); + let main = util::suffixed("main", core); mains.push(quote!( #[no_mangle] #cfg_core - unsafe fn main() -> ! { + unsafe extern "C" fn #main() -> ! { #(#assertion_stmts)* #(#pre_init_stmts)* diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index 65d25c7..988e3c8 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -55,8 +55,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec), quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), ) @@ -156,7 +162,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec util::cfg_core(*core, app.args.cores), // shared `static`s and cross-initialized resources need to be in `.shared` memory - _ => Some(quote!(#[rtfm::export::shared])), + _ => { + if cfg!(feature = "heterogeneous") { + Some(quote!(#[rtfm::export::shared])) + } else { + None + } + } }; let (ty, expr) = if let Some(expr) = expr { diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 8b2c0cd..383a5d8 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -52,8 +52,14 @@ pub fn codegen( })), ) } else { + let shared = if cfg!(feature = "heterogeneous") { + Some(quote!(#[rtfm::export::shared])) + } else { + None + }; + ( - Some(quote!(#[rtfm::export::shared])), + shared, quote!(rtfm::export::MCFQ<#cap_ty>), quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), ) diff --git a/macros/src/codegen/spawn_body.rs b/macros/src/codegen/spawn_body.rs index 83cb5c0..98bce07 100644 --- a/macros/src/codegen/spawn_body.rs +++ b/macros/src/codegen/spawn_body.rs @@ -45,14 +45,15 @@ pub fn codegen( }; let device = extra.device; + let enum_ = util::interrupt_ident(receiver, app.args.cores); let interrupt = &analysis.interrupts[&receiver][&priority]; let pend = if sender != receiver { quote!( - #device::xpend(#receiver, #device::Interrupt::#interrupt); + #device::xpend(#receiver, #device::#enum_::#interrupt); ) } else { quote!( - rtfm::pend(#device::Interrupt::#interrupt); + rtfm::pend(#device::#enum_::#interrupt); ) }; diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index cb84577..d306ed5 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -89,15 +89,16 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec>(); let priority = timer_queue.priority; + let sys_tick = util::suffixed("SysTick", sender); items.push(quote!( #cfg_sender #[no_mangle] - unsafe fn SysTick() { + unsafe fn #sys_tick() { use rtfm::Mutex as _; /// The priority of this handler diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 203fcee..8c43b35 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -27,9 +27,11 @@ pub fn capacity_typenum(capacity: u8, round_up_to_power_of_two: bool) -> TokenSt pub fn cfg_core(core: Core, cores: u8) -> Option { if cores == 1 { None - } else { + } else if cfg!(feature = "heterogeneous") { let core = core.to_string(); Some(quote!(#[cfg(core = #core)])) + } else { + None } } @@ -102,6 +104,15 @@ pub fn instants_ident(task: &Ident, sender: Core) -> Ident { Ident::new(&format!("{}_S{}_INSTANTS", task, sender), Span::call_site()) } +pub fn interrupt_ident(core: Core, cores: u8) -> Ident { + let span = Span::call_site(); + if cores == 1 { + Ident::new("Interrupt", span) + } else { + Ident::new(&format!("Interrupt_{}", core), span) + } +} + /// Generates a pre-reexport identifier for the "late resources" struct pub fn late_resources_ident(init: &Ident) -> Ident { Ident::new( @@ -245,6 +256,16 @@ pub fn spawn_t_ident(receiver: Core, priority: u8, sender: Core) -> Ident { ) } +pub fn suffixed(name: &str, core: u8) -> Ident { + let span = Span::call_site(); + + if cfg!(feature = "homogeneous") { + Ident::new(&format!("{}_{}", name, core), span) + } else { + Ident::new(name, span) + } +} + /// Generates an identifier for a timer queue /// /// At most there's one timer queue per core diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 6e1a797..6502d9c 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -20,7 +20,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { args, input, Settings { - parse_cores: cfg!(feature = "heterogeneous"), + parse_cores: cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous"), parse_exception: true, parse_extern_interrupt: true, parse_interrupt: true, diff --git a/mc/Cargo.toml b/mc/Cargo.toml deleted file mode 100644 index 7c75335..0000000 --- a/mc/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -authors = ["Jorge Aparicio "] -edition = "2018" -name = "mc" -# this crate is only used for testing -publish = false -version = "0.0.0-alpha.0" - -[dependencies] -cortex-m = "0.6.0" - -[dependencies.cortex-m-rtfm] -path = ".." -features = ["heterogeneous"] - -[dev-dependencies] -panic-halt = "0.2.0" -microamp = "0.1.0-alpha.1" diff --git a/mc/README.md b/mc/README.md deleted file mode 100644 index e1335bb..0000000 --- a/mc/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory contains multi-core compile pass tests. diff --git a/mc/examples/smallest.rs b/mc/examples/smallest.rs deleted file mode 100644 index 792935a..0000000 --- a/mc/examples/smallest.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = {}; diff --git a/mc/examples/x-init-2.rs b/mc/examples/x-init-2.rs deleted file mode 100644 index ff48b11..0000000 --- a/mc/examples/x-init-2.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! [compile-pass] Cross initialization of late resources - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = { - extern "C" { - // owned by core #1 but initialized by core #0 - static mut X: u32; - - // owned by core #0 but initialized by core #1 - static mut Y: u32; - } - - #[init(core = 0, late = [X])] - fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } - } - - #[idle(core = 0, resources = [Y])] - fn b(_: b::Context) -> ! { - loop {} - } - - #[init(core = 1)] - fn c(_: c::Context) -> c::LateResources { - c::LateResources { Y: 0 } - } - - #[idle(core = 1, resources = [X])] - fn d(_: d::Context) -> ! { - loop {} - } -}; diff --git a/mc/examples/x-init.rs b/mc/examples/x-init.rs deleted file mode 100644 index 3f26c5c..0000000 --- a/mc/examples/x-init.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! [compile-pass] Split initialization of late resources - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = { - extern "C" { - static mut X: u32; - static mut Y: u32; - } - - #[init(core = 0, late = [X])] - fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } - } - - #[init(core = 1)] - fn b(_: b::Context) -> b::LateResources { - b::LateResources { Y: 0 } - } -}; diff --git a/mc/examples/x-schedule.rs b/mc/examples/x-schedule.rs deleted file mode 100644 index 76e70ac..0000000 --- a/mc/examples/x-schedule.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc, monotonic = mc::MT)] -const APP: () = { - #[init(core = 0, spawn = [ping])] - fn init(c: init::Context) { - c.spawn.ping().ok(); - } - - #[task(core = 0, schedule = [ping])] - fn pong(c: pong::Context) { - c.schedule.ping(c.scheduled + 1_000_000).ok(); - } - - #[task(core = 1, schedule = [pong])] - fn ping(c: ping::Context) { - c.schedule.pong(c.scheduled + 1_000_000).ok(); - } - - extern "C" { - #[core = 0] - fn I0(); - - #[core = 0] - fn I1(); - - #[core = 1] - fn I0(); - - #[core = 1] - fn I1(); - } -}; diff --git a/mc/examples/x-spawn.rs b/mc/examples/x-spawn.rs deleted file mode 100644 index 749918f..0000000 --- a/mc/examples/x-spawn.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![no_main] -#![no_std] - -use panic_halt as _; - -#[rtfm::app(cores = 2, device = mc)] -const APP: () = { - #[init(core = 0, spawn = [foo])] - fn init(c: init::Context) { - c.spawn.foo().ok(); - } - - #[task(core = 1)] - fn foo(_: foo::Context) {} - - extern "C" { - #[core = 1] - fn I0(); - } -}; diff --git a/mc/src/lib.rs b/mc/src/lib.rs deleted file mode 100644 index d86c0e8..0000000 --- a/mc/src/lib.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Fake multi-core PAC - -#![no_std] - -use core::{ - cmp::Ordering, - ops::{Add, Sub}, -}; - -use cortex_m::interrupt::Nr; -use rtfm::Monotonic; - -// Fake priority bits -pub const NVIC_PRIO_BITS: u8 = 3; - -pub struct CrossPend; - -pub fn xpend(_core: u8, _interrupt: impl Nr) {} - -/// Fake monotonic timer -pub struct MT; - -unsafe impl Monotonic for MT { - type Instant = Instant; - - fn ratio() -> u32 { - 1 - } - - unsafe fn reset() { - (0xE0001004 as *mut u32).write_volatile(0) - } - - fn now() -> Instant { - unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) } - } - - fn zero() -> Instant { - Instant(0) - } -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct Instant(i32); - -impl Add for Instant { - type Output = Instant; - - fn add(self, rhs: u32) -> Self { - Instant(self.0.wrapping_add(rhs as i32)) - } -} - -impl Sub for Instant { - type Output = u32; - - fn sub(self, rhs: Self) -> u32 { - self.0.checked_sub(rhs.0).unwrap() as u32 - } -} - -impl Ord for Instant { - fn cmp(&self, rhs: &Self) -> Ordering { - self.0.wrapping_sub(rhs.0).cmp(&0) - } -} - -impl PartialOrd for Instant { - fn partial_cmp(&self, rhs: &Self) -> Option { - Some(self.cmp(rhs)) - } -} - -// Fake interrupts -pub enum Interrupt { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, -} - -unsafe impl Nr for Interrupt { - fn nr(&self) -> u8 { - match self { - Interrupt::I0 => 0, - Interrupt::I1 => 1, - Interrupt::I2 => 2, - Interrupt::I3 => 3, - Interrupt::I4 => 4, - Interrupt::I5 => 5, - Interrupt::I6 => 6, - Interrupt::I7 => 7, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 73e6e20..acb3a63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ use cortex_m::{ interrupt::Nr, peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU}, }; -#[cfg(not(feature = "heterogeneous"))] +#[cfg(all(not(feature = "heterogeneous"), not(feature = "homogeneous")))] use cortex_m_rt as _; // vector table pub use cortex_m_rtfm_macros::app; pub use rtfm_core::{Exclusive, Mutex}; -- cgit v1.2.3 From 4e51bb68b976c6bb6a9a989dc560d2a8123a84ca Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 20 Jun 2019 06:19:59 +0200 Subject: RFC #207 --- examples/baseline.rs | 4 ++-- examples/binds.rs | 2 +- examples/capacity.rs | 4 ++-- examples/generics.rs | 8 ++++---- examples/interrupt.rs | 4 ++-- examples/late.rs | 4 ++-- examples/lock.rs | 12 +++++------ examples/pool.rs | 4 ++-- examples/resource.rs | 8 ++++---- examples/shared-with-init.rs | 4 ++-- examples/static.rs | 8 ++++---- examples/t-binds.rs | 6 ++++-- examples/t-resource.rs | 8 ++++---- examples/t-schedule.rs | 8 ++++---- examples/t-spawn.rs | 8 ++++---- examples/types.rs | 16 +++++++-------- macros/src/check.rs | 19 ++++++----------- macros/src/codegen/hardware_tasks.rs | 4 ++-- macros/src/codegen/pre_init.rs | 14 ++++++------- macros/src/codegen/util.rs | 12 +++++++++++ macros/src/lib.rs | 21 ++++++++----------- macros/src/tests/multi.rs | 12 +++++------ macros/src/tests/single.rs | 7 +++---- ui/single/exception-invalid.rs | 4 ++-- ui/single/exception-invalid.stderr | 4 ++-- ui/single/exception-systick-used.rs | 4 ++-- ui/single/exception-systick-used.stderr | 4 ++-- ui/single/extern-interrupt-used.rs | 2 +- ui/single/extern-interrupt-used.stderr | 6 +++--- ui/single/locals-cfg.rs | 8 ++++---- ui/single/resources-cfg.rs | 8 ++++---- ui/single/resources-cfg.stderr | 12 +++++------ ui/single/task-priority-too-high.rs | 36 ++++++++++++++++----------------- 33 files changed, 143 insertions(+), 142 deletions(-) (limited to 'macros/src/codegen') diff --git a/examples/baseline.rs b/examples/baseline.rs index cc9b412..3a8ab0e 100644 --- a/examples/baseline.rs +++ b/examples/baseline.rs @@ -35,8 +35,8 @@ const APP: () = { } } - #[interrupt(spawn = [foo])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, spawn = [foo])] + fn uart0(c: uart0::Context) { hprintln!("UART0(baseline = {:?})", c.start).unwrap(); // `foo` inherits the baseline of `UART0`: its `start` time diff --git a/examples/binds.rs b/examples/binds.rs index 1959d75..b10cb43 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -30,7 +30,7 @@ const APP: () = { loop {} } - #[interrupt(binds = UART0)] + #[task(binds = UART0)] fn foo(_: foo::Context) { static mut TIMES: u32 = 0; diff --git a/examples/capacity.rs b/examples/capacity.rs index e1a835c..ebc86b8 100644 --- a/examples/capacity.rs +++ b/examples/capacity.rs @@ -16,8 +16,8 @@ const APP: () = { rtfm::pend(Interrupt::UART0); } - #[interrupt(spawn = [foo, bar])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, spawn = [foo, bar])] + fn uart0(c: uart0::Context) { c.spawn.foo(0).unwrap(); c.spawn.foo(1).unwrap(); c.spawn.foo(2).unwrap(); diff --git a/examples/generics.rs b/examples/generics.rs index a35ba23..562470d 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -20,8 +20,8 @@ const APP: () = { rtfm::pend(Interrupt::UART1); } - #[interrupt(resources = [SHARED])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [SHARED])] + fn uart0(c: uart0::Context) { static mut STATE: u32 = 0; hprintln!("UART0(STATE = {})", *STATE).unwrap(); @@ -33,8 +33,8 @@ const APP: () = { debug::exit(debug::EXIT_SUCCESS); } - #[interrupt(priority = 2, resources = [SHARED])] - fn UART1(c: UART1::Context) { + #[task(binds = UART1, priority = 2, resources = [SHARED])] + fn uart1(c: uart1::Context) { static mut STATE: u32 = 0; hprintln!("UART1(STATE = {})", *STATE).unwrap(); diff --git a/examples/interrupt.rs b/examples/interrupt.rs index 3fe8ff3..f0069b8 100644 --- a/examples/interrupt.rs +++ b/examples/interrupt.rs @@ -33,8 +33,8 @@ const APP: () = { loop {} } - #[interrupt] - fn UART0(_: UART0::Context) { + #[task(binds = UART0)] + fn uart0(_: uart0::Context) { static mut TIMES: u32 = 0; // Safe access to local `static mut` variable diff --git a/examples/late.rs b/examples/late.rs index 4d48a6a..19807ff 100644 --- a/examples/late.rs +++ b/examples/late.rs @@ -47,8 +47,8 @@ const APP: () = { } } - #[interrupt(resources = [P])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [P])] + fn uart0(c: uart0::Context) { c.resources.P.enqueue(42).unwrap(); } }; diff --git a/examples/lock.rs b/examples/lock.rs index b7d36b4..17b6a58 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -19,8 +19,8 @@ const APP: () = { } // when omitted priority is assumed to be `1` - #[interrupt(resources = [SHARED])] - fn GPIOA(mut c: GPIOA::Context) { + #[task(binds = GPIOA, resources = [SHARED])] + fn gpioa(mut c: gpioa::Context) { hprintln!("A").unwrap(); // the lower priority task requires a critical section to access the data @@ -44,16 +44,16 @@ const APP: () = { debug::exit(debug::EXIT_SUCCESS); } - #[interrupt(priority = 2, resources = [SHARED])] - fn GPIOB(c: GPIOB::Context) { + #[task(binds = GPIOB, priority = 2, resources = [SHARED])] + fn gpiob(c: gpiob::Context) { // the higher priority task does *not* need a critical section *c.resources.SHARED += 1; hprintln!("D - SHARED = {}", *c.resources.SHARED).unwrap(); } - #[interrupt(priority = 3)] - fn GPIOC(_: GPIOC::Context) { + #[task(binds = GPIOC, priority = 3)] + fn gpioc(_: gpioc::Context) { hprintln!("C").unwrap(); } }; diff --git a/examples/pool.rs b/examples/pool.rs index db321b5..8c44cb1 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -29,8 +29,8 @@ const APP: () = { rtfm::pend(Interrupt::I2C0); } - #[interrupt(priority = 2, spawn = [foo, bar])] - fn I2C0(c: I2C0::Context) { + #[task(binds = I2C0, priority = 2, spawn = [foo, bar])] + fn i2c0(c: i2c0::Context) { // claim a memory block, leave it uninitialized and .. let x = P::alloc().unwrap().freeze(); diff --git a/examples/resource.rs b/examples/resource.rs index 8268950..661f8c3 100644 --- a/examples/resource.rs +++ b/examples/resource.rs @@ -31,16 +31,16 @@ const APP: () = { } // `SHARED` can be access from this context - #[interrupt(resources = [SHARED])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [SHARED])] + fn uart0(c: uart0::Context) { *c.resources.SHARED += 1; hprintln!("UART0: SHARED = {}", c.resources.SHARED).unwrap(); } // `SHARED` can be access from this context - #[interrupt(resources = [SHARED])] - fn UART1(c: UART1::Context) { + #[task(binds = UART1, resources = [SHARED])] + fn uart1(c: uart1::Context) { *c.resources.SHARED += 1; hprintln!("UART1: SHARED = {}", c.resources.SHARED).unwrap(); diff --git a/examples/shared-with-init.rs b/examples/shared-with-init.rs index 1640ca9..ed73c8b 100644 --- a/examples/shared-with-init.rs +++ b/examples/shared-with-init.rs @@ -25,8 +25,8 @@ const APP: () = { rtfm::pend(Interrupt::UART0); } - #[interrupt(resources = [SHARED])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [SHARED])] + fn uart0(c: uart0::Context) { if let Some(message) = c.resources.SHARED.take() { // `message` has been received drop(message); diff --git a/examples/static.rs b/examples/static.rs index eeb522f..5eb7b19 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -23,15 +23,15 @@ const APP: () = { init::LateResources { KEY: 0xdeadbeef } } - #[interrupt(resources = [KEY])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [KEY])] + fn uart0(c: uart0::Context) { hprintln!("UART0(KEY = {:#x})", c.resources.KEY).unwrap(); debug::exit(debug::EXIT_SUCCESS); } - #[interrupt(priority = 2, resources = [KEY])] - fn UART1(c: UART1::Context) { + #[task(binds = UART1, priority = 2, resources = [KEY])] + fn uart1(c: uart1::Context) { hprintln!("UART1(KEY = {:#x})", c.resources.KEY).unwrap(); } }; diff --git a/examples/t-binds.rs b/examples/t-binds.rs index b4693a4..dda8e20 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -12,12 +12,14 @@ const APP: () = { #[init] fn init(_: init::Context) {} - #[exception(binds = SVCall)] + // Cortex-M exception + #[task(binds = SVCall)] fn foo(c: foo::Context) { foo_trampoline(c) } - #[interrupt(binds = UART0)] + // LM3S6965 interrupt + #[task(binds = UART0)] fn bar(c: bar::Context) { bar_trampoline(c) } diff --git a/examples/t-resource.rs b/examples/t-resource.rs index 40dc2a6..adcc04b 100644 --- a/examples/t-resource.rs +++ b/examples/t-resource.rs @@ -51,8 +51,8 @@ const APP: () = { loop {} } - #[interrupt(resources = [O3, S1, S2, S3])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [O3, S1, S2, S3])] + fn uart0(c: uart0::Context) { // owned by interrupt == `&mut` let _: &mut u32 = c.resources.O3; @@ -66,8 +66,8 @@ const APP: () = { let _: &u32 = c.resources.S3; } - #[interrupt(resources = [S2, O5])] - fn UART1(c: UART1::Context) { + #[task(binds = UART1, resources = [S2, O5])] + fn uart1(c: uart1::Context) { // owned by interrupt == `&` if read-only let _: &u32 = c.resources.O5; diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index 67ff358..e6035b3 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -26,15 +26,15 @@ const APP: () = { loop {} } - #[exception(schedule = [foo, bar, baz])] - fn SVCall(c: SVCall::Context) { + #[task(binds = SVCall, schedule = [foo, bar, baz])] + fn svcall(c: svcall::Context) { let _: Result<(), ()> = c.schedule.foo(c.start + 70.cycles()); let _: Result<(), u32> = c.schedule.bar(c.start + 80.cycles(), 0); let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 90.cycles(), 0, 1); } - #[interrupt(schedule = [foo, bar, baz])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, schedule = [foo, bar, baz])] + fn uart0(c: uart0::Context) { let _: Result<(), ()> = c.schedule.foo(c.start + 100.cycles()); let _: Result<(), u32> = c.schedule.bar(c.start + 110.cycles(), 0); let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 120.cycles(), 0, 1); diff --git a/examples/t-spawn.rs b/examples/t-spawn.rs index 6bb9b31..682b9b8 100644 --- a/examples/t-spawn.rs +++ b/examples/t-spawn.rs @@ -25,15 +25,15 @@ const APP: () = { loop {} } - #[exception(spawn = [foo, bar, baz])] - fn SVCall(c: SVCall::Context) { + #[task(binds = SVCall, spawn = [foo, bar, baz])] + fn svcall(c: svcall::Context) { let _: Result<(), ()> = c.spawn.foo(); let _: Result<(), u32> = c.spawn.bar(0); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1); } - #[interrupt(spawn = [foo, bar, baz])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, spawn = [foo, bar, baz])] + fn uart0(c: uart0::Context) { let _: Result<(), ()> = c.spawn.foo(); let _: Result<(), u32> = c.spawn.bar(0); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1); diff --git a/examples/types.rs b/examples/types.rs index 2e72f0a..3e9c7ea 100644 --- a/examples/types.rs +++ b/examples/types.rs @@ -24,19 +24,19 @@ const APP: () = { debug::exit(debug::EXIT_SUCCESS); } - #[exception(schedule = [foo], spawn = [foo])] - fn SVCall(c: SVCall::Context) { + #[task(binds = SVCall, schedule = [foo], spawn = [foo])] + fn svcall(c: svcall::Context) { let _: Instant = c.start; - let _: SVCall::Schedule = c.schedule; - let _: SVCall::Spawn = c.spawn; + let _: svcall::Schedule = c.schedule; + let _: svcall::Spawn = c.spawn; } - #[interrupt(resources = [SHARED], schedule = [foo], spawn = [foo])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [SHARED], schedule = [foo], spawn = [foo])] + fn uart0(c: uart0::Context) { let _: Instant = c.start; let _: resources::SHARED = c.resources.SHARED; - let _: UART0::Schedule = c.schedule; - let _: UART0::Spawn = c.spawn; + let _: uart0::Schedule = c.schedule; + let _: uart0::Spawn = c.spawn; } #[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])] diff --git a/macros/src/check.rs b/macros/src/check.rs index 619ec8f..85fda75 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use proc_macro2::Span; use rtfm_syntax::{ analyze::Analysis, - ast::{App, CustomArg, HardwareTaskKind}, + ast::{App, CustomArg}, }; use syn::{parse, Path}; @@ -44,18 +44,9 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result> { // check that all exceptions are valid; only exceptions with configurable priorities are // accepted - for (name, task) in app - .hardware_tasks - .iter() - .filter(|(_, task)| task.kind == HardwareTaskKind::Exception) - { - let name_s = task.args.binds(name).to_string(); + for (name, task) in &app.hardware_tasks { + let name_s = task.args.binds.to_string(); match &*name_s { - // NOTE that some of these don't exist on ARMv6-M but we don't check that here -- the - // code we generate will check that the exception actually exists on ARMv6-M - "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" - | "DebugMonitor" | "PendSV" => {} // OK - "SysTick" => { if analysis.timer_queues.get(&task.args.core).is_some() { return Err(parse::Error::new( @@ -67,12 +58,14 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result> { } } - _ => { + "NonMaskableInt" | "HardFault" => { return Err(parse::Error::new( name.span(), "only exceptions with configurable priority can be used as hardware tasks", )); } + + _ => {} } } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index e7f053d..a7af510 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -50,9 +50,9 @@ pub fn codegen( }; let symbol = if cfg!(feature = "homogeneous") { - util::suffixed(&task.args.binds(name).to_string(), core) + util::suffixed(&task.args.binds.to_string(), core) } else { - task.args.binds(name).clone() + task.args.binds.clone() }; let priority = task.args.priority; diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 19fc646..948dae5 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtfm_syntax::ast::{App, HardwareTaskKind}; +use rtfm_syntax::ast::App; use crate::{analyze::Analysis, check::Extra, codegen::util}; @@ -52,9 +52,9 @@ pub fn codegen( .get(&core) .iter() .flat_map(|interrupts| *interrupts) - .chain(app.hardware_tasks.iter().flat_map(|(name, task)| { - if task.kind == HardwareTaskKind::Interrupt { - Some((&task.args.priority, task.args.binds(name))) + .chain(app.hardware_tasks.values().flat_map(|task| { + if !util::is_exception(&task.args.binds) { + Some((&task.args.priority, &task.args.binds)) } else { // we do exceptions in another pass None @@ -102,9 +102,9 @@ pub fn codegen( } // set exception priorities - for (name, priority) in app.hardware_tasks.iter().filter_map(|(name, task)| { - if task.kind == HardwareTaskKind::Exception { - Some((task.args.binds(name), task.args.priority)) + for (name, priority) in app.hardware_tasks.values().filter_map(|task| { + if util::is_exception(&task.args.binds) { + Some((&task.args.binds, task.args.priority)) } else { None } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 8c43b35..cd01264 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -113,6 +113,18 @@ pub fn interrupt_ident(core: Core, cores: u8) -> Ident { } } +/// Whether `name` is an exception with configurable priority +pub fn is_exception(name: &Ident) -> bool { + let s = name.to_string(); + + match &*s { + "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" + | "DebugMonitor" | "PendSV" | "SysTick" => true, + + _ => false, + } +} + /// Generates a pre-reexport identifier for the "late resources" struct pub fn late_resources_ident(init: &Ident) -> Ident { Ident::new( diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 6502d9c..ed55095 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -16,19 +16,14 @@ mod tests; #[proc_macro_attribute] pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { - let (app, analysis) = match rtfm_syntax::parse( - args, - input, - Settings { - parse_cores: cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous"), - parse_exception: true, - parse_extern_interrupt: true, - parse_interrupt: true, - parse_schedule: true, - optimize_priorities: true, - ..Settings::default() - }, - ) { + let mut settings = Settings::default(); + settings.optimize_priorities = true; + settings.parse_binds = true; + settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous"); + settings.parse_extern_interrupt = true; + settings.parse_schedule = true; + + let (app, analysis) = match rtfm_syntax::parse(args, input, settings) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; diff --git a/macros/src/tests/multi.rs b/macros/src/tests/multi.rs index 37fef53..b55c451 100644 --- a/macros/src/tests/multi.rs +++ b/macros/src/tests/multi.rs @@ -3,6 +3,10 @@ use rtfm_syntax::Settings; #[test] fn analyze() { + let mut settings = Settings::default(); + settings.parse_cores = true; + settings.parse_extern_interrupt = true; + let (app, analysis) = rtfm_syntax::parse2( quote!(device = pac, cores = 2), quote!( @@ -35,13 +39,9 @@ fn analyze() { } }; ), - Settings { - parse_cores: true, - parse_extern_interrupt: true, - ..Settings::default() - }, + settings, ) - .unwrap(); + .unwrap(); let analysis = crate::analyze::app(analysis, &app); diff --git a/macros/src/tests/single.rs b/macros/src/tests/single.rs index fb2d430..5d7a8a9 100644 --- a/macros/src/tests/single.rs +++ b/macros/src/tests/single.rs @@ -3,6 +3,8 @@ use rtfm_syntax::Settings; #[test] fn analyze() { + let mut settings = Settings::default(); + settings.parse_extern_interrupt = true; let (app, analysis) = rtfm_syntax::parse2( quote!(device = pac), quote!( @@ -20,10 +22,7 @@ fn analyze() { } }; ), - Settings { - parse_extern_interrupt: true, - ..Settings::default() - }, + settings, ) .unwrap(); diff --git a/ui/single/exception-invalid.rs b/ui/single/exception-invalid.rs index 426cb67..54f5992 100644 --- a/ui/single/exception-invalid.rs +++ b/ui/single/exception-invalid.rs @@ -2,6 +2,6 @@ #[rtfm::app(device = lm3s6965)] const APP: () = { - #[exception] - fn NonMaskableInt(_: NonMaskableInt::Context) {} + #[task(binds = NonMaskableInt)] + fn nmi(_: nmi::Context) {} }; diff --git a/ui/single/exception-invalid.stderr b/ui/single/exception-invalid.stderr index f7fc292..306074b 100644 --- a/ui/single/exception-invalid.stderr +++ b/ui/single/exception-invalid.stderr @@ -1,8 +1,8 @@ error: only exceptions with configurable priority can be used as hardware tasks --> $DIR/exception-invalid.rs:6:8 | -6 | fn NonMaskableInt(_: NonMaskableInt::Context) {} - | ^^^^^^^^^^^^^^ +6 | fn nmi(_: nmi::Context) {} + | ^^^ error: aborting due to previous error diff --git a/ui/single/exception-systick-used.rs b/ui/single/exception-systick-used.rs index d30da1b..1155834 100644 --- a/ui/single/exception-systick-used.rs +++ b/ui/single/exception-systick-used.rs @@ -2,8 +2,8 @@ #[rtfm::app(device = lm3s6965)] const APP: () = { - #[exception] - fn SysTick(_: SysTick::Context) {} + #[task(binds = SysTick)] + fn sys_tick(_: sys_tick::Context) {} #[task(schedule = [foo])] fn foo(_: foo::Context) {} diff --git a/ui/single/exception-systick-used.stderr b/ui/single/exception-systick-used.stderr index 47786c6..e2ccbd3 100644 --- a/ui/single/exception-systick-used.stderr +++ b/ui/single/exception-systick-used.stderr @@ -1,8 +1,8 @@ error: this exception can't be used because it's being used by the runtime --> $DIR/exception-systick-used.rs:6:8 | -6 | fn SysTick(_: SysTick::Context) {} - | ^^^^^^^ +6 | fn sys_tick(_: sys_tick::Context) {} + | ^^^^^^^^ error: aborting due to previous error diff --git a/ui/single/extern-interrupt-used.rs b/ui/single/extern-interrupt-used.rs index 25f34b3..59f3806 100644 --- a/ui/single/extern-interrupt-used.rs +++ b/ui/single/extern-interrupt-used.rs @@ -2,7 +2,7 @@ #[rtfm::app(device = lm3s6965)] const APP: () = { - #[interrupt(binds = UART0)] + #[task(binds = UART0)] fn a(_: a::Context) {} extern "C" { diff --git a/ui/single/extern-interrupt-used.stderr b/ui/single/extern-interrupt-used.stderr index 8707b1d..2e084ca 100644 --- a/ui/single/extern-interrupt-used.stderr +++ b/ui/single/extern-interrupt-used.stderr @@ -1,8 +1,8 @@ error: `extern` interrupts can't be used as hardware tasks - --> $DIR/extern-interrupt-used.rs:5:25 + --> $DIR/extern-interrupt-used.rs:5:20 | -5 | #[interrupt(binds = UART0)] - | ^^^^^ +5 | #[task(binds = UART0)] + | ^^^^^ error: aborting due to previous error diff --git a/ui/single/locals-cfg.rs b/ui/single/locals-cfg.rs index bcce5ca..8761f72 100644 --- a/ui/single/locals-cfg.rs +++ b/ui/single/locals-cfg.rs @@ -20,16 +20,16 @@ const APP: () = { loop {} } - #[exception] - fn SVCall(_: SVCall::Context) { + #[task(binds = SVCall)] + fn svcall(_: svcall::Context) { #[cfg(never)] static mut FOO: u32 = 0; FOO; } - #[interrupt] - fn UART0(_: UART0::Context) { + #[task(binds = UART0)] + fn uart0(_: uart0::Context) { #[cfg(never)] static mut FOO: u32 = 0; diff --git a/ui/single/resources-cfg.rs b/ui/single/resources-cfg.rs index f8c3672..6f608fa 100644 --- a/ui/single/resources-cfg.rs +++ b/ui/single/resources-cfg.rs @@ -41,16 +41,16 @@ const APP: () = { loop {} } - #[interrupt(resources = [O3, S1, S2, S3])] - fn UART0(c: UART0::Context) { + #[task(binds = UART0, resources = [O3, S1, S2, S3])] + fn uart0(c: uart0::Context) { c.resources.O3; c.resources.S1; c.resources.S2; c.resources.S3; } - #[interrupt(resources = [S2, O5])] - fn UART1(c: UART1::Context) { + #[task(binds = UART1, resources = [S2, O5])] + fn uart1(c: uart1::Context) { c.resources.S2; c.resources.O5; } diff --git a/ui/single/resources-cfg.stderr b/ui/single/resources-cfg.stderr index 88c34d2..55e7ee0 100644 --- a/ui/single/resources-cfg.stderr +++ b/ui/single/resources-cfg.stderr @@ -70,7 +70,7 @@ error[E0609]: no field `S3` on type `idleResources<'_>` | = note: available fields are: `__marker__` -error[E0609]: no field `O3` on type `UART0Resources<'_>` +error[E0609]: no field `O3` on type `uart0Resources<'_>` --> $DIR/resources-cfg.rs:46:21 | 46 | c.resources.O3; @@ -78,7 +78,7 @@ error[E0609]: no field `O3` on type `UART0Resources<'_>` | = note: available fields are: `__marker__` -error[E0609]: no field `S1` on type `UART0Resources<'_>` +error[E0609]: no field `S1` on type `uart0Resources<'_>` --> $DIR/resources-cfg.rs:47:21 | 47 | c.resources.S1; @@ -86,7 +86,7 @@ error[E0609]: no field `S1` on type `UART0Resources<'_>` | = note: available fields are: `__marker__` -error[E0609]: no field `S2` on type `UART0Resources<'_>` +error[E0609]: no field `S2` on type `uart0Resources<'_>` --> $DIR/resources-cfg.rs:48:21 | 48 | c.resources.S2; @@ -94,7 +94,7 @@ error[E0609]: no field `S2` on type `UART0Resources<'_>` | = note: available fields are: `__marker__` -error[E0609]: no field `S3` on type `UART0Resources<'_>` +error[E0609]: no field `S3` on type `uart0Resources<'_>` --> $DIR/resources-cfg.rs:49:21 | 49 | c.resources.S3; @@ -102,7 +102,7 @@ error[E0609]: no field `S3` on type `UART0Resources<'_>` | = note: available fields are: `__marker__` -error[E0609]: no field `S2` on type `UART1Resources<'_>` +error[E0609]: no field `S2` on type `uart1Resources<'_>` --> $DIR/resources-cfg.rs:54:21 | 54 | c.resources.S2; @@ -110,7 +110,7 @@ error[E0609]: no field `S2` on type `UART1Resources<'_>` | = note: available fields are: `__marker__` -error[E0609]: no field `O5` on type `UART1Resources<'_>` +error[E0609]: no field `O5` on type `uart1Resources<'_>` --> $DIR/resources-cfg.rs:55:21 | 55 | c.resources.O5; diff --git a/ui/single/task-priority-too-high.rs b/ui/single/task-priority-too-high.rs index c7c9dc9..24cb11e 100644 --- a/ui/single/task-priority-too-high.rs +++ b/ui/single/task-priority-too-high.rs @@ -7,32 +7,32 @@ const APP: () = { #[init] fn init(_: init::Context) {} - #[interrupt(priority = 1)] - fn GPIOA(_: GPIOA::Context) {} + #[task(binds = GPIOA, priority = 1)] + fn gpioa(_: gpioa::Context) {} - #[interrupt(priority = 2)] - fn GPIOB(_: GPIOB::Context) {} + #[task(binds = GPIOB, priority = 2)] + fn gpiob(_: gpiob::Context) {} - #[interrupt(priority = 3)] - fn GPIOC(_: GPIOC::Context) {} + #[task(binds = GPIOC, priority = 3)] + fn gpioc(_: gpioc::Context) {} - #[interrupt(priority = 4)] - fn GPIOD(_: GPIOD::Context) {} + #[task(binds = GPIOD, priority = 4)] + fn gpiod(_: gpiod::Context) {} - #[interrupt(priority = 5)] - fn GPIOE(_: GPIOE::Context) {} + #[task(binds = GPIOE, priority = 5)] + fn gpioe(_: gpioe::Context) {} - #[interrupt(priority = 6)] - fn UART0(_: UART0::Context) {} + #[task(binds = UART0, priority = 6)] + fn uart0(_: uart0::Context) {} - #[interrupt(priority = 7)] - fn UART1(_: UART1::Context) {} + #[task(binds = UART1, priority = 7)] + fn uart1(_: uart1::Context) {} // OK, this is the maximum priority supported by the device - #[interrupt(priority = 8)] - fn SSI0(_: SSI0::Context) {} + #[task(binds = SSI0, priority = 8)] + fn ssi0(_: ssi0::Context) {} // this value is too high! - #[interrupt(priority = 9)] - fn I2C0(_: I2C0::Context) {} + #[task(binds = I2C0, priority = 9)] + fn i2c0(_: i2c0::Context) {} }; -- cgit v1.2.3 From 596cf585ea8dc278d88e0652dffbacbc75de04c6 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 24 Jun 2019 14:09:12 +0200 Subject: Monotonic trait is safe; add MultiCore trait --- heterogeneous/src/lib.rs | 6 ++++-- homogeneous/src/lib.rs | 6 ++++-- macros/src/codegen.rs | 2 +- macros/src/codegen/assertions.rs | 11 +++++++++-- src/cyccnt.rs | 7 ++++++- src/export.rs | 7 +++++++ src/lib.rs | 5 ++++- 7 files changed, 35 insertions(+), 9 deletions(-) (limited to 'macros/src/codegen') diff --git a/heterogeneous/src/lib.rs b/heterogeneous/src/lib.rs index a4f0ec5..3288bfe 100644 --- a/heterogeneous/src/lib.rs +++ b/heterogeneous/src/lib.rs @@ -8,7 +8,7 @@ use core::{ }; use bare_metal::Nr; -use rtfm::Monotonic; +use rtfm::{Monotonic, MultiCore}; // both cores have the exact same interrupts pub use Interrupt_0 as Interrupt_1; @@ -21,7 +21,7 @@ pub fn xpend(_core: u8, _interrupt: impl Nr) {} /// Fake monotonic timer pub struct MT; -unsafe impl Monotonic for MT { +impl Monotonic for MT { type Instant = Instant; fn ratio() -> u32 { @@ -41,6 +41,8 @@ unsafe impl Monotonic for MT { } } +impl MultiCore for MT {} + #[derive(Clone, Copy, Eq, PartialEq)] pub struct Instant(i32); diff --git a/homogeneous/src/lib.rs b/homogeneous/src/lib.rs index a4f0ec5..3288bfe 100644 --- a/homogeneous/src/lib.rs +++ b/homogeneous/src/lib.rs @@ -8,7 +8,7 @@ use core::{ }; use bare_metal::Nr; -use rtfm::Monotonic; +use rtfm::{Monotonic, MultiCore}; // both cores have the exact same interrupts pub use Interrupt_0 as Interrupt_1; @@ -21,7 +21,7 @@ pub fn xpend(_core: u8, _interrupt: impl Nr) {} /// Fake monotonic timer pub struct MT; -unsafe impl Monotonic for MT { +impl Monotonic for MT { type Instant = Instant; fn ratio() -> u32 { @@ -41,6 +41,8 @@ unsafe impl Monotonic for MT { } } +impl MultiCore for MT {} + #[derive(Clone, Copy, Eq, PartialEq)] pub struct Instant(i32); diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 9276626..a351599 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -32,7 +32,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { // generate a `main` function for each core for core in 0..app.args.cores { - let assertion_stmts = assertions::codegen(core, analysis); + let assertion_stmts = assertions::codegen(core, analysis, extra); let (const_app_pre_init, pre_init_stmts) = pre_init::codegen(core, &app, analysis, extra); diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 95268a2..4a77352 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -1,10 +1,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::analyze::Analysis; +use crate::{analyze::Analysis, check::Extra}; /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits -pub fn codegen(core: u8, analysis: &Analysis) -> Vec { +pub fn codegen(core: u8, analysis: &Analysis, extra: &Extra) -> Vec { let mut stmts = vec![]; // we don't generate *all* assertions on all cores because the user could conditionally import a @@ -22,5 +22,12 @@ pub fn codegen(core: u8, analysis: &Analysis) -> Vec { } } + // if the `schedule` API is used in more than one core then we need to check that the + // `monotonic` timer can be used in multi-core context + if analysis.timer_queues.len() > 1 && analysis.timer_queues.contains_key(&core) { + let monotonic = extra.monotonic(); + stmts.push(quote!(rtfm::export::assert_multicore::<#monotonic>();)); + } + stmts } diff --git a/src/cyccnt.rs b/src/cyccnt.rs index a2b216c..468aa71 100644 --- a/src/cyccnt.rs +++ b/src/cyccnt.rs @@ -116,6 +116,11 @@ pub struct Duration { } impl Duration { + /// Creates a new `Duration` from the specified number of clock cycles + pub fn from_cycles(cycles: u32) -> Self { + Duration { inner: cycles } + } + /// Returns the total number of clock cycles contained by this `Duration` pub fn as_cycles(&self) -> u32 { self.inner @@ -181,7 +186,7 @@ impl U32Ext for u32 { pub struct CYCCNT; #[cfg(not(feature = "heterogeneous"))] -unsafe impl crate::Monotonic for CYCCNT { +impl crate::Monotonic for CYCCNT { type Instant = Instant; fn ratio() -> u32 { diff --git a/src/export.rs b/src/export.rs index 7646e3c..572068c 100644 --- a/src/export.rs +++ b/src/export.rs @@ -108,6 +108,13 @@ where { } +#[inline(always)] +pub fn assert_multicore() +where + T: super::MultiCore, +{ +} + #[cfg(armv7m)] #[inline(always)] pub unsafe fn lock( diff --git a/src/lib.rs b/src/lib.rs index acb3a63..decd2da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,7 +117,7 @@ impl From for Peripherals { } /// A monotonic clock / counter -pub unsafe trait Monotonic { +pub trait Monotonic { /// A measurement of this clock type Instant: Copy + Ord + Sub; @@ -134,6 +134,9 @@ pub unsafe trait Monotonic { fn zero() -> Self::Instant; } +/// A marker trait that indicates that it is correct to use this type in multi-core context +pub trait MultiCore {} + /// Sets the given `interrupt` as pending /// /// This is a convenience function around -- cgit v1.2.3 From be92041a592f65f38cee8475b61d35e7fcee3694 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 29 Jun 2019 09:11:42 +0200 Subject: WIP --- build.rs | 5 ++++- macros/src/codegen.rs | 2 ++ macros/src/codegen/dispatchers.rs | 7 ++++++- macros/src/codegen/hardware_tasks.rs | 8 +++++++- macros/src/codegen/idle.rs | 13 +++++------- macros/src/codegen/init.rs | 4 +++- macros/src/codegen/locals.rs | 5 ++++- macros/src/codegen/resources.rs | 17 ++++++++++------ macros/src/codegen/schedule.rs | 4 ++++ macros/src/codegen/software_tasks.rs | 24 +++++++++++++++++++--- macros/src/codegen/spawn.rs | 4 ++++ macros/src/codegen/timer_queue.rs | 6 +++++- macros/src/codegen/util.rs | 39 ++++++++++++++++++++++++++++++++++++ 13 files changed, 115 insertions(+), 23 deletions(-) (limited to 'macros/src/codegen') diff --git a/build.rs b/build.rs index 2419b4e..14c3d24 100644 --- a/build.rs +++ b/build.rs @@ -7,7 +7,10 @@ fn main() { println!("cargo:rustc-cfg=armv6m") } - if target.starts_with("thumbv7m") | target.starts_with("thumbv7em") { + if target.starts_with("thumbv7m") + | target.starts_with("thumbv7em") + | target.starts_with("thumbv8m") + { println!("cargo:rustc-cfg=armv7m") } diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 8a54832..8ac06d5 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -68,8 +68,10 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let cfg_core = util::cfg_core(core, app.args.cores); let main = util::suffixed("main", core); + let section = util::link_section("text", core); mains.push(quote!( #[no_mangle] + #section #cfg_core unsafe extern "C" fn #main() -> ! { #(#assertion_stmts)* diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index 988e3c8..9a9cb10 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -46,13 +46,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec), quote!(rtfm::export::Queue(unsafe { rtfm::export::iQueue::u8_sc() })), + util::link_section("bss", sender), ) } else { let shared = if cfg!(feature = "heterogeneous") { @@ -65,6 +66,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec), quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), + None, ) }; @@ -77,6 +79,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec ! { use rtfm::Mutex as _; @@ -73,12 +75,7 @@ pub fn codegen( #name::Context::new(&rtfm::export::Priority::new(0)) )); - ( - const_app, - root_idle, - user_idle, - call_idle, - ) + (const_app, root_idle, user_idle, call_idle) } else { ( None, diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 271be94..878c633 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -72,7 +72,7 @@ pub fn codegen( let mut locals_pat = None; let mut locals_new = None; if !init.locals.is_empty() { - let (struct_, pat) = locals::codegen(Context::Init(core), &init.locals, app); + let (struct_, pat) = locals::codegen(Context::Init(core), &init.locals, core, app); locals_new = Some(quote!(#name::Locals::new())); locals_pat = Some(pat); @@ -82,10 +82,12 @@ pub fn codegen( let context = &init.context; let attrs = &init.attrs; let stmts = &init.stmts; + let section = util::link_section("text", core); let user_init = Some(quote!( #(#attrs)* #cfg_core #[allow(non_snake_case)] + #section fn #name(#(#locals_pat,)* #context: #name::Context) #ret { #(#stmts)* } diff --git a/macros/src/codegen/locals.rs b/macros/src/codegen/locals.rs index 9663563..799ef7a 100644 --- a/macros/src/codegen/locals.rs +++ b/macros/src/codegen/locals.rs @@ -2,7 +2,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use rtfm_syntax::{ ast::{App, Local}, - Context, Map, + Context, Core, Map, }; use crate::codegen::util; @@ -10,6 +10,7 @@ use crate::codegen::util; pub fn codegen( ctxt: Context, locals: &Map, + core: Core, app: &App, ) -> ( // locals @@ -41,6 +42,7 @@ pub fn codegen( let cfgs = &local.cfgs; has_cfgs |= !cfgs.is_empty(); + let section = util::link_section("data", core); let expr = &local.expr; let ty = &local.ty; fields.push(quote!( @@ -49,6 +51,7 @@ pub fn codegen( )); items.push(quote!( #(#cfgs)* + #section static mut #name: #ty = #expr )); values.push(quote!( diff --git a/macros/src/codegen/resources.rs b/macros/src/codegen/resources.rs index 2425681..1161a7a 100644 --- a/macros/src/codegen/resources.rs +++ b/macros/src/codegen/resources.rs @@ -26,20 +26,24 @@ pub fn codegen( let ty = &res.ty; { - let loc_attr = match loc { + let (loc_attr, section) = match loc { Location::Owned { core, cross_initialized: false, - } => util::cfg_core(*core, app.args.cores), + } => ( + util::cfg_core(*core, app.args.cores), + util::link_section("data", *core), + ), // shared `static`s and cross-initialized resources need to be in `.shared` memory - _ => { + _ => ( if cfg!(feature = "heterogeneous") { Some(quote!(#[rtfm::export::shared])) } else { None - } - } + }, + None, + ), }; let (ty, expr) = if let Some(expr) = expr { @@ -53,9 +57,10 @@ pub fn codegen( let attrs = &res.attrs; const_app.push(quote!( - #loc_attr #(#attrs)* #(#cfgs)* + #loc_attr + #section static mut #name: #ty = #expr; )); } diff --git a/macros/src/codegen/schedule.rs b/macros/src/codegen/schedule.rs index 57f01a2..8cf6098 100644 --- a/macros/src/codegen/schedule.rs +++ b/macros/src/codegen/schedule.rs @@ -35,8 +35,10 @@ pub fn codegen(app: &App, extra: &Extra) -> Vec { let body = schedule_body::codegen(scheduler, &name, app); + let section = util::link_section("text", sender); methods.push(quote!( #(#cfgs)* + #section fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> { #body } @@ -50,9 +52,11 @@ pub fn codegen(app: &App, extra: &Extra) -> Vec { let body = schedule_body::codegen(scheduler, &name, app); + let section = util::link_section("text", sender); items.push(quote!( #cfg_sender #(#cfgs)* + #section unsafe fn #schedule( priority: &rtfm::export::Priority, instant: #instant diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 383a5d8..2960faf 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -43,13 +43,21 @@ pub fn codegen( let cfg_sender = util::cfg_core(sender, app.args.cores); let fq = util::fq_ident(name, sender); - let (loc, fq_ty, fq_expr) = if receiver == sender { + let (loc, fq_ty, fq_expr, bss, mk_uninit): ( + _, + _, + _, + _, + Box Option<_>>, + ) = if receiver == sender { ( cfg_sender.clone(), quote!(rtfm::export::SCFQ<#cap_ty>), quote!(rtfm::export::Queue(unsafe { rtfm::export::iQueue::u8_sc() })), + util::link_section("bss", sender), + Box::new(|| util::link_section_uninit(Some(sender))), ) } else { let shared = if cfg!(feature = "heterogeneous") { @@ -62,6 +70,8 @@ pub fn codegen( shared, quote!(rtfm::export::MCFQ<#cap_ty>), quote!(rtfm::export::Queue(rtfm::export::iQueue::u8())), + None, + Box::new(|| util::link_section_uninit(None)), ) }; let loc = &loc; @@ -70,6 +80,7 @@ pub fn codegen( /// Queue version of a free-list that keeps track of empty slots in /// the following buffers #loc + #bss static mut #fq: #fq_ty = #fq_expr; )); @@ -102,8 +113,10 @@ pub fn codegen( let m = extra.monotonic(); let instants = util::instants_ident(name, sender); + let uninit = mk_uninit(); const_app.push(quote!( #loc + #uninit /// Buffer that holds the instants associated to the inputs of a task static mut #instants: [core::mem::MaybeUninit<<#m as rtfm::Monotonic>::Instant>; #cap_lit] = @@ -111,9 +124,11 @@ pub fn codegen( )); } + let uninit = mk_uninit(); let inputs = util::inputs_ident(name, sender); const_app.push(quote!( #loc + #uninit /// Buffer that holds the inputs of a task static mut #inputs: [core::mem::MaybeUninit<#input_ty>; #cap_lit] = [#(#elems,)*]; @@ -140,13 +155,15 @@ pub fn codegen( // `${task}Locals` let mut locals_pat = None; if !task.locals.is_empty() { - let (struct_, pat) = locals::codegen(Context::SoftwareTask(name), &task.locals, app); + let (struct_, pat) = + locals::codegen(Context::SoftwareTask(name), &task.locals, receiver, app); locals_pat = Some(pat); root.push(struct_); } let cfg_receiver = util::cfg_core(receiver, app.args.cores); + let section = util::link_section("text", receiver); let context = &task.context; let attrs = &task.attrs; let cfgs = &task.cfgs; @@ -154,8 +171,9 @@ pub fn codegen( user_tasks.push(quote!( #(#attrs)* #(#cfgs)* - #cfg_receiver #[allow(non_snake_case)] + #cfg_receiver + #section fn #name(#(#locals_pat,)* #context: #name::Context #(,#inputs)*) { use rtfm::Mutex as _; diff --git a/macros/src/codegen/spawn.rs b/macros/src/codegen/spawn.rs index 1539e27..c63c410 100644 --- a/macros/src/codegen/spawn.rs +++ b/macros/src/codegen/spawn.rs @@ -42,8 +42,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Result<(), #ty> { #let_instant #body @@ -66,9 +68,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec); + let section = util::link_section("bss", sender); items.push(quote!( #cfg_sender #[doc = #doc] + #section static mut #tq: #tq_ty = rtfm::export::TimerQueue( rtfm::export::BinaryHeap( rtfm::export::iBinaryHeap::new() @@ -117,9 +119,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Ident { ) } +fn link_section_index() -> usize { + static INDEX: AtomicUsize = AtomicUsize::new(0); + + INDEX.fetch_add(1, Ordering::Relaxed) +} + +pub fn link_section(section: &str, core: Core) -> Option { + if cfg!(feature = "homogeneous") { + let section = format!(".{}_{}.rtfm{}", section, core, link_section_index()); + Some(quote!(#[link_section = #section])) + } else { + None + } +} + +// NOTE `None` means in shared memory +pub fn link_section_uninit(core: Option) -> Option { + let section = if let Some(core) = core { + let index = link_section_index(); + + if cfg!(feature = "homogeneous") { + format!(".uninit_{}.rtfm{}", core, index) + } else { + format!(".uninit.rtfm{}", index) + } + } else { + if cfg!(feature = "heterogeneous") { + // `#[shared]` attribute sets the linker section + return None; + } + + format!(".uninit.rtfm{}", link_section_index()) + }; + + Some(quote!(#[link_section = #section])) +} + /// Generates a pre-reexport identifier for the "locals" struct pub fn locals_ident(ctxt: Context, app: &App) -> Ident { let mut s = match ctxt { -- cgit v1.2.3 From 14d63f496118f4243f28ddf3218523aa36a80322 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 3 Jul 2019 20:36:52 +0200 Subject: fix (cross-core) initialization barriers --- macros/src/codegen/post_init.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'macros/src/codegen') diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 3f1e445..19773e4 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -25,8 +25,12 @@ pub fn codegen( if analysis.timer_queues.is_empty() { // cross-initialization barriers -- notify *other* cores that their resources have been // initialized - if analysis.initialization_barriers.contains_key(&core) { - let ib = util::init_barrier(core); + for (user, initializers) in &analysis.initialization_barriers { + if !initializers.contains(&core) { + continue; + } + + let ib = util::init_barrier(*user); let shared = if cfg!(feature = "heterogeneous") { Some(quote!( #[rtfm::export::shared] @@ -46,14 +50,12 @@ pub fn codegen( } // then wait until the other cores have initialized *our* resources - for (&initializer, users) in &analysis.initialization_barriers { - if users.contains(&core) { - let ib = util::init_barrier(initializer); + if analysis.initialization_barriers.contains_key(&core) { + let ib = util::init_barrier(core); - stmts.push(quote!( - #ib.wait(); - )); - } + stmts.push(quote!( + #ib.wait(); + )); } // cross-spawn barriers: wait until other cores are ready to receive messages -- cgit v1.2.3 From 9195038c87703fc94b6e99f6de593886d51c2b19 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 10 Jul 2019 22:42:44 +0200 Subject: implement RFC #212 --- ci/expected/generics.run | 6 +-- ci/expected/lock.run | 4 +- ci/expected/resource.run | 4 +- ci/expected/static.run | 4 +- examples/cfg.rs | 15 +++--- examples/generics.rs | 19 ++++--- examples/late.rs | 16 +++--- examples/lock.rs | 19 ++++--- examples/not-send.rs | 13 +++-- examples/not-sync.rs | 13 +++-- examples/resource.rs | 27 +++++----- examples/shared-with-init.rs | 13 +++-- examples/static.rs | 14 ++--- examples/t-cfg.rs | 11 ++-- examples/t-late-not-send.rs | 16 +++--- examples/t-resource.rs | 70 ++++++++++++++----------- examples/types.rs | 13 +++-- heterogeneous/examples/x-init-2.rs | 16 +++--- heterogeneous/examples/x-init.rs | 12 ++--- homogeneous/examples/x-init-2.rs | 16 +++--- homogeneous/examples/x-init.rs | 12 ++--- macros/src/codegen/resources.rs | 79 ++++++++++++++-------------- macros/src/codegen/resources_struct.rs | 8 ++- ui/single/resources-cfg.rs | 96 ++++++++++++++++++++-------------- ui/single/resources-cfg.stderr | 90 +++++++++++++++---------------- 25 files changed, 332 insertions(+), 274 deletions(-) (limited to 'macros/src/codegen') diff --git a/ci/expected/generics.run b/ci/expected/generics.run index 7fa9775..fb31731 100644 --- a/ci/expected/generics.run +++ b/ci/expected/generics.run @@ -1,6 +1,6 @@ UART1(STATE = 0) -SHARED: 0 -> 1 +shared: 0 -> 1 UART0(STATE = 0) -SHARED: 1 -> 2 +shared: 1 -> 2 UART1(STATE = 1) -SHARED: 2 -> 4 +shared: 2 -> 4 diff --git a/ci/expected/lock.run b/ci/expected/lock.run index 156ac22..a987b37 100644 --- a/ci/expected/lock.run +++ b/ci/expected/lock.run @@ -1,5 +1,5 @@ A -B - SHARED = 1 +B - shared = 1 C -D - SHARED = 2 +D - shared = 2 E diff --git a/ci/expected/resource.run b/ci/expected/resource.run index 9c70856..a587a94 100644 --- a/ci/expected/resource.run +++ b/ci/expected/resource.run @@ -1,2 +1,2 @@ -UART0: SHARED = 1 -UART1: SHARED = 2 +UART0: shared = 1 +UART1: shared = 2 diff --git a/ci/expected/static.run b/ci/expected/static.run index 2c295c9..1d4eed0 100644 --- a/ci/expected/static.run +++ b/ci/expected/static.run @@ -1,2 +1,2 @@ -UART1(KEY = 0xdeadbeef) -UART0(KEY = 0xdeadbeef) +UART1(key = 0xdeadbeef) +UART0(key = 0xdeadbeef) diff --git a/examples/cfg.rs b/examples/cfg.rs index b1f65cf..fb812cb 100644 --- a/examples/cfg.rs +++ b/examples/cfg.rs @@ -11,25 +11,28 @@ use panic_semihosting as _; #[rtfm::app(device = lm3s6965)] const APP: () = { - #[cfg(debug_assertions)] // <- `true` when using the `dev` profile - static mut COUNT: u32 = 0; + struct Resources { + #[cfg(debug_assertions)] // <- `true` when using the `dev` profile + #[init(0)] + count: u32, + } #[init] fn init(_: init::Context) { // .. } - #[task(priority = 3, resources = [COUNT], spawn = [log])] + #[task(priority = 3, resources = [count], spawn = [log])] fn foo(_c: foo::Context) { #[cfg(debug_assertions)] { - *_c.resources.COUNT += 1; + *_c.resources.count += 1; - _c.spawn.log(*_c.resources.COUNT).ok(); + _c.spawn.log(*_c.resources.count).ok(); } // this wouldn't compile in `release` mode - // *resources.COUNT += 1; + // *resources.count += 1; // .. } diff --git a/examples/generics.rs b/examples/generics.rs index 562470d..f0632d9 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -12,7 +12,10 @@ use rtfm::{Exclusive, Mutex}; #[rtfm::app(device = lm3s6965)] const APP: () = { - static mut SHARED: u32 = 0; + struct Resources { + #[init(0)] + shared: u32, + } #[init] fn init(_: init::Context) { @@ -20,29 +23,29 @@ const APP: () = { rtfm::pend(Interrupt::UART1); } - #[task(binds = UART0, resources = [SHARED])] + #[task(binds = UART0, resources = [shared])] fn uart0(c: uart0::Context) { static mut STATE: u32 = 0; hprintln!("UART0(STATE = {})", *STATE).unwrap(); - advance(STATE, c.resources.SHARED); + advance(STATE, c.resources.shared); rtfm::pend(Interrupt::UART1); debug::exit(debug::EXIT_SUCCESS); } - #[task(binds = UART1, priority = 2, resources = [SHARED])] + #[task(binds = UART1, priority = 2, resources = [shared])] fn uart1(c: uart1::Context) { static mut STATE: u32 = 0; hprintln!("UART1(STATE = {})", *STATE).unwrap(); - // just to show that `SHARED` can be accessed directly - *c.resources.SHARED += 0; + // just to show that `shared` can be accessed directly + *c.resources.shared += 0; - advance(STATE, Exclusive(c.resources.SHARED)); + advance(STATE, Exclusive(c.resources.shared)); } }; @@ -55,5 +58,5 @@ fn advance(state: &mut u32, mut shared: impl Mutex) { (old, *shared) }); - hprintln!("SHARED: {} -> {}", old, new).unwrap(); + hprintln!("shared: {} -> {}", old, new).unwrap(); } diff --git a/examples/late.rs b/examples/late.rs index 19807ff..536d71a 100644 --- a/examples/late.rs +++ b/examples/late.rs @@ -16,9 +16,9 @@ use panic_semihosting as _; #[rtfm::app(device = lm3s6965)] const APP: () = { // Late resources - extern "C" { - static mut P: Producer<'static, u32, U4>; - static mut C: Consumer<'static, u32, U4>; + struct Resources { + p: Producer<'static, u32, U4>, + c: Consumer<'static, u32, U4>, } #[init] @@ -31,13 +31,13 @@ const APP: () = { let (p, c) = Q.as_mut().unwrap().split(); // Initialization of late resources - init::LateResources { P: p, C: c } + init::LateResources { p, c } } - #[idle(resources = [C])] + #[idle(resources = [c])] fn idle(c: idle::Context) -> ! { loop { - if let Some(byte) = c.resources.C.dequeue() { + if let Some(byte) = c.resources.c.dequeue() { hprintln!("received message: {}", byte).unwrap(); debug::exit(debug::EXIT_SUCCESS); @@ -47,8 +47,8 @@ const APP: () = { } } - #[task(binds = UART0, resources = [P])] + #[task(binds = UART0, resources = [p])] fn uart0(c: uart0::Context) { - c.resources.P.enqueue(42).unwrap(); + c.resources.p.enqueue(42).unwrap(); } }; diff --git a/examples/lock.rs b/examples/lock.rs index 17b6a58..f33a60a 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -11,7 +11,10 @@ use panic_semihosting as _; #[rtfm::app(device = lm3s6965)] const APP: () = { - static mut SHARED: u32 = 0; + struct Resources { + #[init(0)] + shared: u32, + } #[init] fn init(_: init::Context) { @@ -19,21 +22,21 @@ const APP: () = { } // when omitted priority is assumed to be `1` - #[task(binds = GPIOA, resources = [SHARED])] + #[task(binds = GPIOA, resources = [shared])] fn gpioa(mut c: gpioa::Context) { hprintln!("A").unwrap(); // the lower priority task requires a critical section to access the data - c.resources.SHARED.lock(|shared| { + c.resources.shared.lock(|shared| { // data can only be modified within this critical section (closure) *shared += 1; // GPIOB will *not* run right now due to the critical section rtfm::pend(Interrupt::GPIOB); - hprintln!("B - SHARED = {}", *shared).unwrap(); + hprintln!("B - shared = {}", *shared).unwrap(); - // GPIOC does not contend for `SHARED` so it's allowed to run now + // GPIOC does not contend for `shared` so it's allowed to run now rtfm::pend(Interrupt::GPIOC); }); @@ -44,12 +47,12 @@ const APP: () = { debug::exit(debug::EXIT_SUCCESS); } - #[task(binds = GPIOB, priority = 2, resources = [SHARED])] + #[task(binds = GPIOB, priority = 2, resources = [shared])] fn gpiob(c: gpiob::Context) { // the higher priority task does *not* need a critical section - *c.resources.SHARED += 1; + *c.resources.shared += 1; - hprintln!("D - SHARED = {}", *c.resources.SHARED).unwrap(); + hprintln!("D - shared = {}", *c.resources.shared).unwrap(); } #[task(binds = GPIOC, priority = 3)] diff --git a/examples/not-send.rs b/examples/not-send.rs index f240e51..d27cc82 100644 --- a/examples/not-send.rs +++ b/examples/not-send.rs @@ -17,7 +17,10 @@ pub struct NotSend { #[app(device = lm3s6965)] const APP: () = { - static mut SHARED: Option = None; + struct Resources { + #[init(None)] + shared: Option, + } #[init(spawn = [baz, quux])] fn init(c: init::Context) { @@ -36,16 +39,16 @@ const APP: () = { // scenario 1 } - #[task(priority = 2, resources = [SHARED])] + #[task(priority = 2, resources = [shared])] fn baz(c: baz::Context) { // scenario 2: resource shared between tasks that run at the same priority - *c.resources.SHARED = Some(NotSend { _0: PhantomData }); + *c.resources.shared = Some(NotSend { _0: PhantomData }); } - #[task(priority = 2, resources = [SHARED])] + #[task(priority = 2, resources = [shared])] fn quux(c: quux::Context) { // scenario 2 - let _not_send = c.resources.SHARED.take().unwrap(); + let _not_send = c.resources.shared.take().unwrap(); debug::exit(debug::EXIT_SUCCESS); } diff --git a/examples/not-sync.rs b/examples/not-sync.rs index 6b49911..f0f6075 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -16,21 +16,24 @@ pub struct NotSync { #[rtfm::app(device = lm3s6965)] const APP: () = { - static SHARED: NotSync = NotSync { _0: PhantomData }; + struct Resources { + #[init(NotSync { _0: PhantomData })] + shared: NotSync, + } #[init] fn init(_: init::Context) { debug::exit(debug::EXIT_SUCCESS); } - #[task(resources = [SHARED])] + #[task(resources = [shared])] fn foo(c: foo::Context) { - let _: &NotSync = c.resources.SHARED; + let _: &NotSync = c.resources.shared; } - #[task(resources = [SHARED])] + #[task(resources = [shared])] fn bar(c: bar::Context) { - let _: &NotSync = c.resources.SHARED; + let _: &NotSync = c.resources.shared; } extern "C" { diff --git a/examples/resource.rs b/examples/resource.rs index 661f8c3..2506a2c 100644 --- a/examples/resource.rs +++ b/examples/resource.rs @@ -11,8 +11,11 @@ use panic_semihosting as _; #[rtfm::app(device = lm3s6965)] const APP: () = { - // A resource - static mut SHARED: u32 = 0; + struct Resources { + // A resource + #[init(0)] + shared: u32, + } #[init] fn init(_: init::Context) { @@ -24,25 +27,25 @@ const APP: () = { fn idle(_: idle::Context) -> ! { debug::exit(debug::EXIT_SUCCESS); - // error: `SHARED` can't be accessed from this context - // SHARED += 1; + // error: `shared` can't be accessed from this context + // shared += 1; loop {} } - // `SHARED` can be access from this context - #[task(binds = UART0, resources = [SHARED])] + // `shared` can be access from this context + #[task(binds = UART0, resources = [shared])] fn uart0(c: uart0::Context) { - *c.resources.SHARED += 1; + *c.resources.shared += 1; - hprintln!("UART0: SHARED = {}", c.resources.SHARED).unwrap(); + hprintln!("UART0: shared = {}", c.resources.shared).unwrap(); } - // `SHARED` can be access from this context - #[task(binds = UART1, resources = [SHARED])] + // `shared` can be access from this context + #[task(binds = UART1, resources = [shared])] fn uart1(c: uart1::Context) { - *c.resources.SHARED += 1; + *c.resources.shared += 1; - hprintln!("UART1: SHARED = {}", c.resources.SHARED).unwrap(); + hprintln!("UART1: shared = {}", c.resources.shared).unwrap(); } }; diff --git a/examples/shared-with-init.rs b/examples/shared-with-init.rs index ed73c8b..14fa54b 100644 --- a/examples/shared-with-init.rs +++ b/examples/shared-with-init.rs @@ -14,20 +14,23 @@ pub struct MustBeSend; #[app(device = lm3s6965)] const APP: () = { - static mut SHARED: Option = None; + struct Resources { + #[init(None)] + shared: Option, + } - #[init(resources = [SHARED])] + #[init(resources = [shared])] fn init(c: init::Context) { // this `message` will be sent to task `UART0` let message = MustBeSend; - *c.resources.SHARED = Some(message); + *c.resources.shared = Some(message); rtfm::pend(Interrupt::UART0); } - #[task(binds = UART0, resources = [SHARED])] + #[task(binds = UART0, resources = [shared])] fn uart0(c: uart0::Context) { - if let Some(message) = c.resources.SHARED.take() { + if let Some(message) = c.resources.shared.take() { // `message` has been received drop(message); diff --git a/examples/static.rs b/examples/static.rs index 5eb7b19..4af7ee6 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -11,8 +11,8 @@ use panic_semihosting as _; #[rtfm::app(device = lm3s6965)] const APP: () = { - extern "C" { - static KEY: u32; + struct Resources { + key: u32, } #[init] @@ -20,18 +20,18 @@ const APP: () = { rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART1); - init::LateResources { KEY: 0xdeadbeef } + init::LateResources { key: 0xdeadbeef } } - #[task(binds = UART0, resources = [KEY])] + #[task(binds = UART0, resources = [&key])] fn uart0(c: uart0::Context) { - hprintln!("UART0(KEY = {:#x})", c.resources.KEY).unwrap(); + hprintln!("UART0(key = {:#x})", c.resources.key).unwrap(); debug::exit(debug::EXIT_SUCCESS); } - #[task(binds = UART1, priority = 2, resources = [KEY])] + #[task(binds = UART1, priority = 2, resources = [&key])] fn uart1(c: uart1::Context) { - hprintln!("UART1(KEY = {:#x})", c.resources.KEY).unwrap(); + hprintln!("UART1(key = {:#x})", c.resources.key).unwrap(); } }; diff --git a/examples/t-cfg.rs b/examples/t-cfg.rs index 158eef5..e61ec79 100644 --- a/examples/t-cfg.rs +++ b/examples/t-cfg.rs @@ -7,8 +7,11 @@ use panic_halt as _; #[rtfm::app(device = lm3s6965, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { - #[cfg(never)] - static mut FOO: u32 = 0; + struct Resources { + #[cfg(never)] + #[init(0)] + foo: u32, + } #[init] fn init(_: init::Context) { @@ -24,13 +27,13 @@ const APP: () = { loop {} } - #[task(resources = [FOO], schedule = [quux], spawn = [quux])] + #[task(resources = [foo], schedule = [quux], spawn = [quux])] fn foo(_: foo::Context) { #[cfg(never)] static mut BAR: u32 = 0; } - #[task(priority = 3, resources = [FOO], schedule = [quux], spawn = [quux])] + #[task(priority = 3, resources = [foo], schedule = [quux], spawn = [quux])] fn bar(_: bar::Context) { #[cfg(never)] static mut BAR: u32 = 0; diff --git a/examples/t-late-not-send.rs b/examples/t-late-not-send.rs index 55a053d..4fd3504 100644 --- a/examples/t-late-not-send.rs +++ b/examples/t-late-not-send.rs @@ -13,23 +13,23 @@ pub struct NotSend { #[rtfm::app(device = lm3s6965)] const APP: () = { - extern "C" { - static mut X: NotSend; + struct Resources { + x: NotSend, + #[init(None)] + y: Option, } - static mut Y: Option = None; - - #[init(resources = [Y])] + #[init(resources = [y])] fn init(c: init::Context) -> init::LateResources { // equivalent to late resource initialization - *c.resources.Y = Some(NotSend { _0: PhantomData }); + *c.resources.y = Some(NotSend { _0: PhantomData }); init::LateResources { - X: NotSend { _0: PhantomData }, + x: NotSend { _0: PhantomData }, } } - #[idle(resources = [X, Y])] + #[idle(resources = [x, y])] fn idle(_: idle::Context) -> ! { loop {} } diff --git a/examples/t-resource.rs b/examples/t-resource.rs index adcc04b..303340e 100644 --- a/examples/t-resource.rs +++ b/examples/t-resource.rs @@ -9,69 +9,79 @@ use panic_halt as _; #[rtfm::app(device = lm3s6965)] const APP: () = { - static mut O1: u32 = 0; // init - static mut O2: u32 = 0; // idle - static mut O3: u32 = 0; // EXTI0 - static O4: u32 = 0; // idle - static O5: u32 = 0; // EXTI1 - static O6: u32 = 0; // init - - static mut S1: u32 = 0; // idle & EXTI0 - static mut S2: u32 = 0; // EXTI0 & EXTI1 - static S3: u32 = 0; - - #[init(resources = [O1, O4, O5, O6, S3])] + struct Resources { + #[init(0)] + o1: u32, // init + #[init(0)] + o2: u32, // idle + #[init(0)] + o3: u32, // EXTI0 + #[init(0)] + o4: u32, // idle + #[init(0)] + o5: u32, // EXTI1 + #[init(0)] + o6: u32, // init + #[init(0)] + s1: u32, // idle & uart0 + #[init(0)] + s2: u32, // uart0 & uart1 + #[init(0)] + s3: u32, // idle & uart0 + } + + #[init(resources = [o1, o4, o5, o6, s3])] fn init(c: init::Context) { // owned by `init` == `&'static mut` - let _: &'static mut u32 = c.resources.O1; + let _: &'static mut u32 = c.resources.o1; // owned by `init` == `&'static` if read-only - let _: &'static u32 = c.resources.O6; + let _: &'static u32 = c.resources.o6; // `init` has exclusive access to all resources - let _: &mut u32 = c.resources.O4; - let _: &mut u32 = c.resources.O5; - let _: &mut u32 = c.resources.S3; + let _: &mut u32 = c.resources.o4; + let _: &mut u32 = c.resources.o5; + let _: &mut u32 = c.resources.s3; } - #[idle(resources = [O2, O4, S1, S3])] + #[idle(resources = [o2, &o4, s1, &s3])] fn idle(mut c: idle::Context) -> ! { // owned by `idle` == `&'static mut` - let _: &'static mut u32 = c.resources.O2; + let _: &'static mut u32 = c.resources.o2; // owned by `idle` == `&'static` if read-only - let _: &'static u32 = c.resources.O4; + let _: &'static u32 = c.resources.o4; // shared with `idle` == `Mutex` - c.resources.S1.lock(|_| {}); + c.resources.s1.lock(|_| {}); // `&` if read-only - let _: &u32 = c.resources.S3; + let _: &u32 = c.resources.s3; loop {} } - #[task(binds = UART0, resources = [O3, S1, S2, S3])] + #[task(binds = UART0, resources = [o3, s1, s2, &s3])] fn uart0(c: uart0::Context) { // owned by interrupt == `&mut` - let _: &mut u32 = c.resources.O3; + let _: &mut u32 = c.resources.o3; // no `Mutex` proxy when access from highest priority task - let _: &mut u32 = c.resources.S1; + let _: &mut u32 = c.resources.s1; // no `Mutex` proxy when co-owned by cooperative (same priority) tasks - let _: &mut u32 = c.resources.S2; + let _: &mut u32 = c.resources.s2; // `&` if read-only - let _: &u32 = c.resources.S3; + let _: &u32 = c.resources.s3; } - #[task(binds = UART1, resources = [S2, O5])] + #[task(binds = UART1, resources = [s2, &o5])] fn uart1(c: uart1::Context) { // owned by interrupt == `&` if read-only - let _: &u32 = c.resources.O5; + let _: &u32 = c.resources.o5; // no `Mutex` proxy when co-owned by cooperative (same priority) tasks - let _: &mut u32 = c.resources.S2; + let _: &mut u32 = c.resources.s2; } }; diff --git a/examples/types.rs b/examples/types.rs index 3e9c7ea..0c8097f 100644 --- a/examples/types.rs +++ b/examples/types.rs @@ -11,7 +11,10 @@ use rtfm::cyccnt::Instant; #[rtfm::app(device = lm3s6965, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { - static mut SHARED: u32 = 0; + struct Resources { + #[init(0)] + shared: u32, + } #[init(schedule = [foo], spawn = [foo])] fn init(c: init::Context) { @@ -31,18 +34,18 @@ const APP: () = { let _: svcall::Spawn = c.spawn; } - #[task(binds = UART0, resources = [SHARED], schedule = [foo], spawn = [foo])] + #[task(binds = UART0, resources = [shared], schedule = [foo], spawn = [foo])] fn uart0(c: uart0::Context) { let _: Instant = c.start; - let _: resources::SHARED = c.resources.SHARED; + let _: resources::shared = c.resources.shared; let _: uart0::Schedule = c.schedule; let _: uart0::Spawn = c.spawn; } - #[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])] + #[task(priority = 2, resources = [shared], schedule = [foo], spawn = [foo])] fn foo(c: foo::Context) { let _: Instant = c.scheduled; - let _: &mut u32 = c.resources.SHARED; + let _: &mut u32 = c.resources.shared; let _: foo::Resources = c.resources; let _: foo::Schedule = c.schedule; let _: foo::Spawn = c.spawn; diff --git a/heterogeneous/examples/x-init-2.rs b/heterogeneous/examples/x-init-2.rs index b9c3919..033753c 100644 --- a/heterogeneous/examples/x-init-2.rs +++ b/heterogeneous/examples/x-init-2.rs @@ -9,30 +9,30 @@ use panic_halt as _; #[rtfm::app(cores = 2, device = heterogeneous)] const APP: () = { - extern "C" { + struct Resources { // owned by core #1 but initialized by core #0 - static mut X: u32; + x: u32, // owned by core #0 but initialized by core #1 - static mut Y: u32; + y: u32, } - #[init(core = 0, late = [X])] + #[init(core = 0, late = [x])] fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } + a::LateResources { x: 0 } } - #[idle(core = 0, resources = [Y])] + #[idle(core = 0, resources = [y])] fn b(_: b::Context) -> ! { loop {} } #[init(core = 1)] fn c(_: c::Context) -> c::LateResources { - c::LateResources { Y: 0 } + c::LateResources { y: 0 } } - #[idle(core = 1, resources = [X])] + #[idle(core = 1, resources = [x])] fn d(_: d::Context) -> ! { loop {} } diff --git a/heterogeneous/examples/x-init.rs b/heterogeneous/examples/x-init.rs index 53e7380..4183713 100644 --- a/heterogeneous/examples/x-init.rs +++ b/heterogeneous/examples/x-init.rs @@ -9,18 +9,18 @@ use panic_halt as _; #[rtfm::app(cores = 2, device = heterogeneous)] const APP: () = { - extern "C" { - static mut X: u32; - static mut Y: u32; + struct Resources { + x: u32, + y: u32, } - #[init(core = 0, late = [X])] + #[init(core = 0, late = [x])] fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } + a::LateResources { x: 0 } } #[init(core = 1)] fn b(_: b::Context) -> b::LateResources { - b::LateResources { Y: 0 } + b::LateResources { y: 0 } } }; diff --git a/homogeneous/examples/x-init-2.rs b/homogeneous/examples/x-init-2.rs index f51e2f6..de35cf6 100644 --- a/homogeneous/examples/x-init-2.rs +++ b/homogeneous/examples/x-init-2.rs @@ -9,30 +9,30 @@ use panic_halt as _; #[rtfm::app(cores = 2, device = homogeneous)] const APP: () = { - extern "C" { + struct Resources { // owned by core #1 but initialized by core #0 - static mut X: u32; + x: u32, // owned by core #0 but initialized by core #1 - static mut Y: u32; + y: u32, } - #[init(core = 0, late = [X])] + #[init(core = 0, late = [x])] fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } + a::LateResources { x: 0 } } - #[idle(core = 0, resources = [Y])] + #[idle(core = 0, resources = [y])] fn b(_: b::Context) -> ! { loop {} } #[init(core = 1)] fn c(_: c::Context) -> c::LateResources { - c::LateResources { Y: 0 } + c::LateResources { y: 0 } } - #[idle(core = 1, resources = [X])] + #[idle(core = 1, resources = [x])] fn d(_: d::Context) -> ! { loop {} } diff --git a/homogeneous/examples/x-init.rs b/homogeneous/examples/x-init.rs index 5089e38..c359901 100644 --- a/homogeneous/examples/x-init.rs +++ b/homogeneous/examples/x-init.rs @@ -9,18 +9,18 @@ use panic_halt as _; #[rtfm::app(cores = 2, device = homogeneous)] const APP: () = { - extern "C" { - static mut X: u32; - static mut Y: u32; + struct Resources { + x: u32, + y: u32, } - #[init(core = 0, late = [X])] + #[init(core = 0, late = [x])] fn a(_: a::Context) -> a::LateResources { - a::LateResources { X: 0 } + a::LateResources { x: 0 } } #[init(core = 1)] fn b(_: b::Context) -> b::LateResources { - b::LateResources { Y: 0 } + b::LateResources { y: 0 } } }; diff --git a/macros/src/codegen/resources.rs b/macros/src/codegen/resources.rs index 1161a7a..bec4602 100644 --- a/macros/src/codegen/resources.rs +++ b/macros/src/codegen/resources.rs @@ -57,6 +57,7 @@ pub fn codegen( let attrs = &res.attrs; const_app.push(quote!( + #[allow(non_upper_case_globals)] #(#attrs)* #(#cfgs)* #loc_attr @@ -65,50 +66,48 @@ pub fn codegen( )); } - // generate a resource proxy if needed - if res.mutability.is_some() { - if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) { - let cfg_core = util::cfg_core(loc.core().expect("UNREACHABLE"), app.args.cores); + if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) { + let cfg_core = util::cfg_core(loc.core().expect("UNREACHABLE"), app.args.cores); - mod_resources.push(quote!( - #(#cfgs)* - #cfg_core - pub struct #name<'a> { - priority: &'a Priority, + mod_resources.push(quote!( + #[allow(non_camel_case_types)] + #(#cfgs)* + #cfg_core + pub struct #name<'a> { + priority: &'a Priority, + } + + #(#cfgs)* + #cfg_core + impl<'a> #name<'a> { + #[inline(always)] + pub unsafe fn new(priority: &'a Priority) -> Self { + #name { priority } } - #(#cfgs)* - #cfg_core - impl<'a> #name<'a> { - #[inline(always)] - pub unsafe fn new(priority: &'a Priority) -> Self { - #name { priority } - } - - #[inline(always)] - pub unsafe fn priority(&self) -> &Priority { - self.priority - } + #[inline(always)] + pub unsafe fn priority(&self) -> &Priority { + self.priority } - )); - - let ptr = if expr.is_none() { - quote!(#name.as_mut_ptr()) - } else { - quote!(&mut #name) - }; - - const_app.push(util::impl_mutex( - extra, - cfgs, - cfg_core.as_ref(), - true, - name, - quote!(#ty), - *ceiling, - ptr, - )); - } + } + )); + + let ptr = if expr.is_none() { + quote!(#name.as_mut_ptr()) + } else { + quote!(&mut #name) + }; + + const_app.push(util::impl_mutex( + extra, + cfgs, + cfg_core.as_ref(), + true, + name, + quote!(#ty), + *ceiling, + ptr, + )); } } diff --git a/macros/src/codegen/resources_struct.rs b/macros/src/codegen/resources_struct.rs index 0248f19..07a6061 100644 --- a/macros/src/codegen/resources_struct.rs +++ b/macros/src/codegen/resources_struct.rs @@ -24,13 +24,17 @@ pub fn codegen( let mut values = vec![]; let mut has_cfgs = false; - for name in resources { + for (name, access) in resources { let (res, expr) = app.resource(name).expect("UNREACHABLE"); let cfgs = &res.cfgs; has_cfgs |= !cfgs.is_empty(); - let mut_ = res.mutability; + let mut_ = if access.is_exclusive() { + Some(quote!(mut)) + } else { + None + }; let ty = &res.ty; if ctxt.is_init() { diff --git a/ui/single/resources-cfg.rs b/ui/single/resources-cfg.rs index 6f608fa..906b3e2 100644 --- a/ui/single/resources-cfg.rs +++ b/ui/single/resources-cfg.rs @@ -2,56 +2,74 @@ #[rtfm::app(device = lm3s6965)] const APP: () = { - #[cfg(never)] - static mut O1: u32 = 0; // init - #[cfg(never)] - static mut O2: u32 = 0; // idle - #[cfg(never)] - static mut O3: u32 = 0; // EXTI0 - #[cfg(never)] - static O4: u32 = 0; // idle - #[cfg(never)] - static O5: u32 = 0; // EXTI1 - #[cfg(never)] - static O6: u32 = 0; // init - - #[cfg(never)] - static mut S1: u32 = 0; // idle & EXTI0 - #[cfg(never)] - static mut S2: u32 = 0; // EXTI0 & EXTI1 - #[cfg(never)] - static S3: u32 = 0; - - #[init(resources = [O1, O4, O5, O6, S3])] + struct Resources { + #[cfg(never)] + #[init(0)] + o1: u32, // init + + #[cfg(never)] + #[init(0)] + o2: u32, // idle + + #[cfg(never)] + #[init(0)] + o3: u32, // EXTI0 + + #[cfg(never)] + #[init(0)] + o4: u32, // idle + + #[cfg(never)] + #[init(0)] + o5: u32, // EXTI1 + + #[cfg(never)] + #[init(0)] + o6: u32, // init + + #[cfg(never)] + #[init(0)] + s1: u32, // idle & EXTI0 + + #[cfg(never)] + #[init(0)] + s2: u32, // EXTI0 & EXTI1 + + #[cfg(never)] + #[init(0)] + s3: u32, + } + + #[init(resources = [o1, o4, o5, o6, s3])] fn init(c: init::Context) { - c.resources.O1; - c.resources.O4; - c.resources.O5; - c.resources.O6; - c.resources.S3; + c.resources.o1; + c.resources.o4; + c.resources.o5; + c.resources.o6; + c.resources.s3; } - #[idle(resources = [O2, O4, S1, S3])] + #[idle(resources = [o2, &o4, s1, &s3])] fn idle(c: idle::Context) -> ! { - c.resources.O2; - c.resources.O4; - c.resources.S1; - c.resources.S3; + c.resources.o2; + c.resources.o4; + c.resources.s1; + c.resources.s3; loop {} } - #[task(binds = UART0, resources = [O3, S1, S2, S3])] + #[task(binds = UART0, resources = [o3, s1, s2, &s3])] fn uart0(c: uart0::Context) { - c.resources.O3; - c.resources.S1; - c.resources.S2; - c.resources.S3; + c.resources.o3; + c.resources.s1; + c.resources.s2; + c.resources.s3; } - #[task(binds = UART1, resources = [S2, O5])] + #[task(binds = UART1, resources = [s2, &o5])] fn uart1(c: uart1::Context) { - c.resources.S2; - c.resources.O5; + c.resources.s2; + c.resources.o5; } }; diff --git a/ui/single/resources-cfg.stderr b/ui/single/resources-cfg.stderr index 55e7ee0..a745e6e 100644 --- a/ui/single/resources-cfg.stderr +++ b/ui/single/resources-cfg.stderr @@ -1,119 +1,119 @@ -error[E0609]: no field `O1` on type `initResources<'_>` - --> $DIR/resources-cfg.rs:27:21 +error[E0609]: no field `o1` on type `initResources<'_>` + --> $DIR/resources-cfg.rs:45:21 | -27 | c.resources.O1; +45 | c.resources.o1; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O4` on type `initResources<'_>` - --> $DIR/resources-cfg.rs:28:21 +error[E0609]: no field `o4` on type `initResources<'_>` + --> $DIR/resources-cfg.rs:46:21 | -28 | c.resources.O4; +46 | c.resources.o4; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O5` on type `initResources<'_>` - --> $DIR/resources-cfg.rs:29:21 +error[E0609]: no field `o5` on type `initResources<'_>` + --> $DIR/resources-cfg.rs:47:21 | -29 | c.resources.O5; +47 | c.resources.o5; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O6` on type `initResources<'_>` - --> $DIR/resources-cfg.rs:30:21 +error[E0609]: no field `o6` on type `initResources<'_>` + --> $DIR/resources-cfg.rs:48:21 | -30 | c.resources.O6; +48 | c.resources.o6; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S3` on type `initResources<'_>` - --> $DIR/resources-cfg.rs:31:21 +error[E0609]: no field `s3` on type `initResources<'_>` + --> $DIR/resources-cfg.rs:49:21 | -31 | c.resources.S3; +49 | c.resources.s3; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O2` on type `idleResources<'_>` - --> $DIR/resources-cfg.rs:36:21 +error[E0609]: no field `o2` on type `idleResources<'_>` + --> $DIR/resources-cfg.rs:54:21 | -36 | c.resources.O2; +54 | c.resources.o2; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O4` on type `idleResources<'_>` - --> $DIR/resources-cfg.rs:37:21 +error[E0609]: no field `o4` on type `idleResources<'_>` + --> $DIR/resources-cfg.rs:55:21 | -37 | c.resources.O4; +55 | c.resources.o4; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S1` on type `idleResources<'_>` - --> $DIR/resources-cfg.rs:38:21 +error[E0609]: no field `s1` on type `idleResources<'_>` + --> $DIR/resources-cfg.rs:56:21 | -38 | c.resources.S1; +56 | c.resources.s1; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S3` on type `idleResources<'_>` - --> $DIR/resources-cfg.rs:39:21 +error[E0609]: no field `s3` on type `idleResources<'_>` + --> $DIR/resources-cfg.rs:57:21 | -39 | c.resources.S3; +57 | c.resources.s3; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O3` on type `uart0Resources<'_>` - --> $DIR/resources-cfg.rs:46:21 +error[E0609]: no field `o3` on type `uart0Resources<'_>` + --> $DIR/resources-cfg.rs:64:21 | -46 | c.resources.O3; +64 | c.resources.o3; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S1` on type `uart0Resources<'_>` - --> $DIR/resources-cfg.rs:47:21 +error[E0609]: no field `s1` on type `uart0Resources<'_>` + --> $DIR/resources-cfg.rs:65:21 | -47 | c.resources.S1; +65 | c.resources.s1; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S2` on type `uart0Resources<'_>` - --> $DIR/resources-cfg.rs:48:21 +error[E0609]: no field `s2` on type `uart0Resources<'_>` + --> $DIR/resources-cfg.rs:66:21 | -48 | c.resources.S2; +66 | c.resources.s2; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S3` on type `uart0Resources<'_>` - --> $DIR/resources-cfg.rs:49:21 +error[E0609]: no field `s3` on type `uart0Resources<'_>` + --> $DIR/resources-cfg.rs:67:21 | -49 | c.resources.S3; +67 | c.resources.s3; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `S2` on type `uart1Resources<'_>` - --> $DIR/resources-cfg.rs:54:21 +error[E0609]: no field `s2` on type `uart1Resources<'_>` + --> $DIR/resources-cfg.rs:72:21 | -54 | c.resources.S2; +72 | c.resources.s2; | ^^ unknown field | = note: available fields are: `__marker__` -error[E0609]: no field `O5` on type `uart1Resources<'_>` - --> $DIR/resources-cfg.rs:55:21 +error[E0609]: no field `o5` on type `uart1Resources<'_>` + --> $DIR/resources-cfg.rs:73:21 | -55 | c.resources.O5; +73 | c.resources.o5; | ^^ unknown field | = note: available fields are: `__marker__` -- cgit v1.2.3 From fb84029beef9bec3c205583296b181023f2e4b6b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Jul 2019 12:53:58 +0200 Subject: implement the #[shared] attribute as specified in RFC #211 --- macros/src/codegen/locals.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'macros/src/codegen') diff --git a/macros/src/codegen/locals.rs b/macros/src/codegen/locals.rs index 799ef7a..cbfe05f 100644 --- a/macros/src/codegen/locals.rs +++ b/macros/src/codegen/locals.rs @@ -42,7 +42,11 @@ pub fn codegen( let cfgs = &local.cfgs; has_cfgs |= !cfgs.is_empty(); - let section = util::link_section("data", core); + let section = if local.shared && cfg!(feature = "heterogeneous") { + Some(quote!(#[rtfm::export::shared])) + } else { + util::link_section("data", core) + }; let expr = &local.expr; let ty = &local.ty; fields.push(quote!( -- cgit v1.2.3 From 0e146f8d1142672725b6abb38478f503a9261c80 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 20 Aug 2019 15:11:24 +0200 Subject: adapt to changes in rtfm-syntax --- .travis.yml | 8 +++++++- macros/Cargo.toml | 6 +++--- macros/src/check.rs | 7 ++++--- macros/src/codegen.rs | 2 +- macros/src/codegen/hardware_tasks.rs | 1 + macros/src/codegen/idle.rs | 2 ++ macros/src/codegen/init.rs | 2 ++ macros/src/codegen/software_tasks.rs | 1 + macros/src/codegen/util.rs | 6 +++--- macros/src/lib.rs | 1 - tests/single.rs | 6 ++++-- 11 files changed, 28 insertions(+), 14 deletions(-) (limited to 'macros/src/codegen') diff --git a/.travis.yml b/.travis.yml index 31d10e8..ac5a7b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,20 +5,26 @@ matrix: # NOTE used to build docs on successful merges to master - env: TARGET=x86_64-unknown-linux-gnu + # MSRV + - env: TARGET=thumbv7m-none-eabi + rust: 1.36.0 + if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) + - env: TARGET=thumbv6m-none-eabi if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - env: TARGET=thumbv7m-none-eabi if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) + # compile-fail tests - env: TARGET=x86_64-unknown-linux-gnu rust: nightly if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) + # heterogeneous multi-core support - env: TARGET=thumbv6m-none-eabi rust: nightly if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - env: TARGET=thumbv7m-none-eabi rust: nightly if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index c4e897f..ed7626f 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -15,9 +15,9 @@ version = "0.5.0-alpha.1" proc-macro = true [dependencies] -proc-macro2 = "0.4.30" -quote = "0.6.12" -syn = "0.15.34" +proc-macro2 = "1" +quote = "1" +syn = "1" [dependencies.rtfm-syntax] git = "https://github.com/japaric/rtfm-syntax" diff --git a/macros/src/check.rs b/macros/src/check.rs index 85fda75..0136370 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -169,9 +169,10 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result> { peripherals = if *x { Some(0) } else { None } } - CustomArg::UInt(x) if app.args.cores != 1 => { - peripherals = if *x < u64::from(app.args.cores) { - Some(*x as u8) + CustomArg::UInt(s) if app.args.cores != 1 => { + let x = s.parse::().ok(); + peripherals = if x.is_some() && x.unwrap() < app.args.cores { + Some(x.unwrap()) } else { return Err(parse::Error::new( k.span(), diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 8ac06d5..0213848 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -126,7 +126,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #(#root)* - #(#mod_resources)* + #mod_resources #(#root_hardware_tasks)* diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index cf92e07..a9c2a2b 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -115,6 +115,7 @@ pub fn codegen( let stmts = &task.stmts; let section = util::link_section("text", core); // XXX shouldn't this have a cfg_core? + let locals_pat = locals_pat.iter(); user_tasks.push(quote!( #(#attrs)* #[allow(non_snake_case)] diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index d656076..35a7252 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -58,6 +58,7 @@ pub fn codegen( let context = &idle.context; let stmts = &idle.stmts; let section = util::link_section("text", core); + let locals_pat = locals_pat.iter(); let user_idle = Some(quote!( #(#attrs)* #[allow(non_snake_case)] @@ -70,6 +71,7 @@ pub fn codegen( } )); + let locals_new = locals_new.iter(); let call_idle = quote!(#name( #(#locals_new,)* #name::Context::new(&rtfm::export::Priority::new(0)) diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 878c633..9c8ce31 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -83,6 +83,7 @@ pub fn codegen( let attrs = &init.attrs; let stmts = &init.stmts; let section = util::link_section("text", core); + let locals_pat = locals_pat.iter(); let user_init = Some(quote!( #(#attrs)* #cfg_core @@ -102,6 +103,7 @@ pub fn codegen( const_app = Some(constructor); } + let locals_new = locals_new.iter(); let call_init = Some(quote!(let late = #name(#(#locals_new,)* #name::Context::new(core.into()));)); diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 2960faf..be1eb05 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -168,6 +168,7 @@ pub fn codegen( let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; + let locals_pat = locals_pat.iter(); user_tasks.push(quote!( #(#attrs)* #(#cfgs)* diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index f5f96de..207272d 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -3,13 +3,13 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; use rtfm_syntax::{ast::App, Context, Core}; -use syn::{ArgCaptured, Attribute, Ident, IntSuffix, LitInt}; +use syn::{Attribute, Ident, LitInt, PatType}; use crate::check::Extra; /// Turns `capacity` into an unsuffixed integer literal pub fn capacity_literal(capacity: u8) -> LitInt { - LitInt::new(u64::from(capacity), IntSuffix::None, Span::call_site()) + LitInt::new(&capacity.to_string(), Span::call_site()) } /// Turns `capacity` into a type-level (`typenum`) integer @@ -194,7 +194,7 @@ pub fn rendezvous_ident(core: Core) -> Ident { // // `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] pub fn regroup_inputs( - inputs: &[ArgCaptured], + inputs: &[PatType], ) -> ( // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] Vec, diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ed55095..7a436e7 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,5 +1,4 @@ #![deny(warnings)] -#![recursion_limit = "128"] extern crate proc_macro; diff --git a/tests/single.rs b/tests/single.rs index 93addf6..01b8031 100644 --- a/tests/single.rs +++ b/tests/single.rs @@ -8,8 +8,10 @@ fn ui() { config.mode = Mode::Ui; config.src_base = PathBuf::from("ui/single"); - config.target_rustcflags = - Some("--edition=2018 -L target/debug/deps -Z unstable-options --extern rtfm --extern lm3s6965".to_owned()); + config.target_rustcflags = Some( + "--edition=2018 -L target/debug/deps -Z unstable-options --extern rtfm --extern lm3s6965" + .to_owned(), + ); config.link_deps(); config.clean_rmeta(); -- cgit v1.2.3 From 7aa270cb92180abfc9102a69efdde378c3396b5e Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 15 Sep 2019 18:36:00 +0200 Subject: don't use deprecated API --- macros/src/codegen/pre_init.rs | 2 +- src/export.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'macros/src/codegen') diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 948dae5..605171b 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -75,7 +75,7 @@ pub fn codegen( // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended // interrupt is implementation defined - stmts.push(quote!(core.NVIC.enable(#device::#interrupt::#name);)); + stmts.push(quote!(rtfm::export::NVIC::unmask(#device::#interrupt::#name);)); } // cross-spawn barriers: now that priorities have been set and the interrupts have been unmasked diff --git a/src/export.rs b/src/export.rs index 572068c..96c444b 100644 --- a/src/export.rs +++ b/src/export.rs @@ -9,7 +9,7 @@ pub use cortex_m::register::basepri; pub use cortex_m::{ asm::wfi, interrupt, - peripheral::{scb::SystemHandler, syst::SystClkSource, DWT}, + peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC}, Peripherals, }; use heapless::spsc::{MultiCore, SingleCore}; -- cgit v1.2.3