diff --git a/.travis.yml b/.travis.yml index 09edbfe4ee..2825a15b90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,26 +4,23 @@ matrix: include: # NOTE used to build docs on successful merges to master - env: TARGET=x86_64-unknown-linux-gnu - rust: beta - env: TARGET=thumbv6m-none-eabi - rust: beta if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - env: TARGET=thumbv7m-none-eabi - rust: beta if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - env: TARGET=x86_64-unknown-linux-gnu - rust: nightly + rust: nightly-2018-12-06 if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - env: TARGET=thumbv6m-none-eabi - rust: nightly + rust: nightly-2018-12-06 if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - env: TARGET=thumbv7m-none-eabi - rust: nightly + rust: nightly-2018-12-06 if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) before_install: set -e diff --git a/ci/script.sh b/ci/script.sh index eb582d4c4c..20394d5cdc 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -5,10 +5,11 @@ main() { if [ $T = x86_64-unknown-linux-gnu ]; then # compile-fail and compile-pass tests - if [ $TRAVIS_RUST_VERSION = nightly ]; then - # TODO how to run a subset of these tests when timer-queue is disabled? - cargo test --features timer-queue --test compiletest --target $T - fi + case $TRAVIS_RUST_VERSION in + nightly*) + # TODO how to run a subset of these tests when timer-queue is disabled? + cargo test --features timer-queue --test compiletest --target $T + esac cargo check --target $T cargo check --features timer-queue --target $T diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 6f5e1007d7..8e1970f53c 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -24,58 +24,55 @@ struct Context { // Alias #[cfg(feature = "timer-queue")] baseline: Ident, - // Dispatcher -> Alias (`enum`) - enums: HashMap, - // Task -> Alias (`static` / resource) - free_queues: Aliases, + dispatchers: HashMap, // Alias (`fn`) idle: Ident, // Alias (`fn`) init: Ident, - // Task -> Alias (`static`) - inputs: Aliases, // Alias priority: Ident, - // Dispatcher -> Alias (`static` / resource) - ready_queues: HashMap, // For non-singletons this maps the resource name to its `static mut` variable name statics: Aliases, /// Task -> Alias (`struct`) resources: HashMap, - // Task -> Alias (`static`) - #[cfg(feature = "timer-queue")] - scheduleds: Aliases, - // Task -> Alias (`fn`) - spawn_fn: Aliases, // Alias (`enum`) schedule_enum: Ident, // Task -> Alias (`fn`) schedule_fn: Aliases, - tasks: Aliases, + tasks: HashMap, // Alias (`struct` / `static mut`) timer_queue: Ident, } +struct Dispatcher { + enum_: Ident, + ready_queue: Ident, +} + +struct Task { + alias: Ident, + free_queue: Ident, + inputs: Ident, + spawn_fn: Ident, + + #[cfg(feature = "timer-queue")] + scheduleds: Ident, +} + impl Default for Context { fn default() -> Self { Context { #[cfg(feature = "timer-queue")] baseline: mk_ident(None), - enums: HashMap::new(), - free_queues: Aliases::new(), + dispatchers: HashMap::new(), idle: mk_ident(Some("idle")), init: mk_ident(Some("init")), - inputs: Aliases::new(), priority: mk_ident(None), - ready_queues: HashMap::new(), statics: Aliases::new(), resources: HashMap::new(), - #[cfg(feature = "timer-queue")] - scheduleds: Aliases::new(), - spawn_fn: Aliases::new(), schedule_enum: mk_ident(None), schedule_fn: Aliases::new(), - tasks: Aliases::new(), + tasks: HashMap::new(), timer_queue: mk_ident(None), } } @@ -145,7 +142,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { let timer_queue = timer_queue(&ctxt, app, analysis); - let pre_init = pre_init(&ctxt, analysis); + let pre_init = pre_init(&ctxt, &app, analysis); let assertions = assertions(app, analysis); @@ -388,16 +385,6 @@ fn post_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok // the device into compile errors let device = &app.args.device; let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); - for (name, interrupt) in &app.interrupts { - let priority = interrupt.args.priority; - exprs.push(quote!(p.NVIC.enable(#device::Interrupt::#name))); - exprs.push(quote!(assert!(#priority <= (1 << #nvic_prio_bits)))); - exprs.push(quote!(p.NVIC.set_priority( - #device::Interrupt::#name, - ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits), - ))); - } - for (name, exception) in &app.exceptions { let priority = exception.args.priority; exprs.push(quote!(assert!(#priority <= (1 << #nvic_prio_bits)))); @@ -416,16 +403,6 @@ fn post_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok ))); } - for (priority, dispatcher) in &analysis.dispatchers { - let name = &dispatcher.interrupt; - exprs.push(quote!(p.NVIC.enable(#device::Interrupt::#name))); - exprs.push(quote!(assert!(#priority <= (1 << #nvic_prio_bits)))); - exprs.push(quote!(p.NVIC.set_priority( - #device::Interrupt::#name, - ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits), - ))); - } - if app.idle.is_none() { // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR exprs.push(quote!(p.SCB.scr.modify(|r| r | 1 << 1))); @@ -802,15 +779,6 @@ fn prelude( } if !spawn.is_empty() { - // Populate `spawn_fn` - for task in spawn { - if ctxt.spawn_fn.contains_key(task) { - continue; - } - - ctxt.spawn_fn.insert(task.clone(), mk_ident(None)); - } - if kind.is_idle() { items.push(quote!( #[allow(unused_variables)] @@ -1067,6 +1035,8 @@ fn interrupts( fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { let mut items = vec![]; + + // first pass to generate buffers (statics and resources) and spawn aliases for (name, task) in &app.tasks { #[cfg(feature = "timer-queue")] let scheduleds_alias = mk_ident(None); @@ -1074,21 +1044,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok let inputs_alias = mk_ident(None); let task_alias = mk_ident(Some(&name.to_string())); - let attrs = &task.attrs; let inputs = &task.inputs; - let locals = mk_locals(&task.statics, false); - let stmts = &task.stmts; - - let prelude = prelude( - ctxt, - Kind::Task(name.clone()), - &task.args.resources, - &task.args.spawn, - &task.args.schedule, - app, - task.args.priority, - analysis, - ); let ty = tuple_ty(inputs); @@ -1121,28 +1077,8 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok () => quote!(), }; - let scheduled_let = match () { - #[cfg(feature = "timer-queue")] - () => { - let baseline = &ctxt.baseline; - quote!(let scheduled = #baseline;) - } - #[cfg(not(feature = "timer-queue"))] - () => quote!(), - }; - - let baseline_arg = match () { - #[cfg(feature = "timer-queue")] - () => { - let baseline = &ctxt.baseline; - quote!(#baseline: rtfm::Instant,) - } - #[cfg(not(feature = "timer-queue"))] - () => quote!(), - }; let inputs_symbol = format!("{}::INPUTS::{}", name, inputs_alias); let free_symbol = format!("{}::FREE_QUEUE::{}", name, free_alias); - let unsafety = &task.unsafety; items.push(quote!( // FIXME(MaybeUninit) MaybeUninit won't be necessary when core::mem::MaybeUninit // stabilizes because heapless constructors will work in const context @@ -1158,7 +1094,70 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok rtfm::export::MaybeUninit::uninitialized(); #scheduleds_static + )); + ctxt.tasks.insert( + name.clone(), + Task { + alias: task_alias, + free_queue: free_alias, + inputs: inputs_alias, + spawn_fn: mk_ident(None), + + #[cfg(feature = "timer-queue")] + scheduleds: scheduleds_alias, + }, + ); + } + + // second pass to generate the actual task function + for (name, task) in &app.tasks { + let attrs = &task.attrs; + let inputs = &task.inputs; + let locals = mk_locals(&task.statics, false); + let stmts = &task.stmts; + let unsafety = &task.unsafety; + + let scheduled_let = match () { + #[cfg(feature = "timer-queue")] + () => { + let baseline = &ctxt.baseline; + quote!(let scheduled = #baseline;) + } + #[cfg(not(feature = "timer-queue"))] + () => quote!(), + }; + + let prelude = prelude( + ctxt, + Kind::Task(name.clone()), + &task.args.resources, + &task.args.spawn, + &task.args.schedule, + app, + task.args.priority, + analysis, + ); + + items.push(module( + ctxt, + Kind::Task(name.clone()), + !task.args.schedule.is_empty(), + !task.args.spawn.is_empty(), + app, + )); + + let task_alias = &ctxt.tasks[name].alias; + let baseline_arg = match () { + #[cfg(feature = "timer-queue")] + () => { + let baseline = &ctxt.baseline; + quote!(#baseline: rtfm::Instant,) + } + #[cfg(not(feature = "timer-queue"))] + () => quote!(), + }; + items.push(quote!( #(#attrs)* #unsafety fn #task_alias(#baseline_arg #(#inputs,)*) { #(#locals)* @@ -1170,20 +1169,6 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok #(#stmts)* } )); - - items.push(module( - ctxt, - Kind::Task(name.clone()), - !task.args.schedule.is_empty(), - !task.args.spawn.is_empty(), - app, - )); - - #[cfg(feature = "timer-queue")] - ctxt.scheduleds.insert(name.clone(), scheduleds_alias); - ctxt.free_queues.insert(name.clone(), free_alias); - ctxt.inputs.insert(name.clone(), inputs_alias); - ctxt.tasks.insert(name.clone(), task_alias); } quote!(#(#items)*) @@ -1233,17 +1218,18 @@ fn dispatchers( .tasks .iter() .map(|task| { - let inputs = &ctxt.inputs[task]; - let free = &ctxt.free_queues[task]; + let task_ = &ctxt.tasks[task]; + let inputs = &task_.inputs; + let free = &task_.free_queue; let pats = tuple_pat(&app.tasks[task].inputs); - let alias = &ctxt.tasks[task]; + let alias = &task_.alias; let baseline_let; let call; match () { #[cfg(feature = "timer-queue")] () => { - let scheduleds = &ctxt.scheduleds[task]; + let scheduleds = &task_.scheduleds; baseline_let = quote!( let baseline = ptr::read(#scheduleds.get_ref().get_unchecked(usize::from(index))); @@ -1284,8 +1270,13 @@ fn dispatchers( } )); - ctxt.ready_queues.insert(*level, ready_alias); - ctxt.enums.insert(*level, enum_alias); + ctxt.dispatchers.insert( + *level, + Dispatcher { + ready_queue: ready_alias, + enum_: enum_alias, + }, + ); } (quote!(#(#data)*), quote!(#(#dispatchers)*)) @@ -1299,21 +1290,24 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt let priority = &ctxt.priority; #[cfg(feature = "timer-queue")] let baseline = &ctxt.baseline; - for (task, alias) in &ctxt.spawn_fn { - let free = &ctxt.free_queues[task]; - let level = app.tasks[task].args.priority; - let ready = &ctxt.ready_queues[&level]; - let enum_ = &ctxt.enums[&level]; + for (name, task) in &ctxt.tasks { + let alias = &task.spawn_fn; + let task_ = &app.tasks[name]; + let free = &task.free_queue; + let level = task_.args.priority; + let dispatcher = &ctxt.dispatchers[&level]; + let ready = &dispatcher.ready_queue; + let enum_ = &dispatcher.enum_; let dispatcher = &analysis.dispatchers[&level].interrupt; - let inputs = &ctxt.inputs[task]; - let args = &app.tasks[task].inputs; + let inputs = &task.inputs; + let args = &task_.inputs; let ty = tuple_ty(args); let pats = tuple_pat(args); let scheduleds_write = match () { #[cfg(feature = "timer-queue")] () => { - let scheduleds = &ctxt.scheduleds[task]; + let scheduleds = &ctxt.tasks[name].scheduleds; quote!( ptr::write( #scheduleds.get_mut().get_unchecked_mut(usize::from(index)), @@ -1348,7 +1342,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt #scheduleds_write #ready { #priority }.lock(|rq| { - rq.split().0.enqueue_unchecked((#enum_::#task, index)) + rq.split().0.enqueue_unchecked((#enum_::#name, index)) }); rtfm::pend(#device::Interrupt::#dispatcher); @@ -1372,7 +1366,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt let mut methods = vec![]; for task in spawn { - let alias = &ctxt.spawn_fn[task]; + let alias = &ctxt.tasks[task].spawn_fn; let inputs = &app.tasks[task].inputs; let ty = tuple_ty(inputs); let pats = tuple_pat(inputs); @@ -1416,10 +1410,11 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream { let priority = &ctxt.priority; let timer_queue = &ctxt.timer_queue; for (task, alias) in &ctxt.schedule_fn { - let free = &ctxt.free_queues[task]; + let task_ = &ctxt.tasks[task]; + let free = &task_.free_queue; let enum_ = &ctxt.schedule_enum; - let inputs = &ctxt.inputs[task]; - let scheduleds = &ctxt.scheduleds[task]; + let inputs = &task_.inputs; + let scheduleds = &task_.scheduleds; let args = &app.tasks[task].inputs; let ty = tuple_ty(args); let pats = tuple_pat(args); @@ -1538,8 +1533,9 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T .iter() .map(|task| { let level = app.tasks[task].args.priority; - let tenum = &ctxt.enums[&level]; - let ready = &ctxt.ready_queues[&level]; + let dispatcher_ = &ctxt.dispatchers[&level]; + let tenum = &dispatcher_.enum_; + let ready = &dispatcher_.ready_queue; let dispatcher = &analysis.dispatchers[&level].interrupt; quote!( @@ -1576,7 +1572,7 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T quote!(#(#items)*) } -fn pre_init(ctxt: &Context, analysis: &Analysis) -> proc_macro2::TokenStream { +fn pre_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { let mut exprs = vec![]; // FIXME(MaybeUninit) Because we are using a fake MaybeUninit we need to set the Option tag to @@ -1584,23 +1580,27 @@ fn pre_init(ctxt: &Context, analysis: &Analysis) -> proc_macro2::TokenStream { // be constructed in const context; we have to initialize them at runtime (i.e. here). // these are `MaybeUninit` arrays - for inputs in ctxt.inputs.values() { + for task in ctxt.tasks.values() { + let inputs = &task.inputs; exprs.push(quote!(#inputs.set(core::mem::uninitialized());)) } #[cfg(feature = "timer-queue")] - for inputs in ctxt.scheduleds.values() { - exprs.push(quote!(#inputs.set(core::mem::uninitialized());)) + for task in ctxt.tasks.values() { + let scheduleds = &task.scheduleds; + exprs.push(quote!(#scheduleds.set(core::mem::uninitialized());)) } // these are `MaybeUninit` `ReadyQueue`s - for queue in ctxt.ready_queues.values() { - exprs.push(quote!(#queue.set(rtfm::export::ReadyQueue::new());)) + for dispatcher in ctxt.dispatchers.values() { + let rq = &dispatcher.ready_queue; + exprs.push(quote!(#rq.set(rtfm::export::ReadyQueue::new());)) } // these are `MaybeUninit` `FreeQueue`s - for free in ctxt.free_queues.values() { - exprs.push(quote!(#free.set(rtfm::export::FreeQueue::new());)) + for task in ctxt.tasks.values() { + let fq = &task.free_queue; + exprs.push(quote!(#fq.set(rtfm::export::FreeQueue::new());)) } // end-of-FIXME @@ -1612,15 +1612,40 @@ fn pre_init(ctxt: &Context, analysis: &Analysis) -> proc_macro2::TokenStream { } // Populate the `FreeQueue`s - for (task, alias) in &ctxt.free_queues { - let capacity = analysis.capacities[task]; + for (name, task) in &ctxt.tasks { + let fq = &task.free_queue; + let capacity = analysis.capacities[name]; exprs.push(quote!( for i in 0..#capacity { - #alias.get_mut().enqueue_unchecked(i); + #fq.get_mut().enqueue_unchecked(i); } )) } + // TODO turn the assertions that check that the priority is not larger than what's supported by + // the device into compile errors + let device = &app.args.device; + let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); + for (name, interrupt) in &app.interrupts { + let priority = interrupt.args.priority; + exprs.push(quote!(p.NVIC.enable(#device::Interrupt::#name);)); + exprs.push(quote!(assert!(#priority <= (1 << #nvic_prio_bits));)); + exprs.push(quote!(p.NVIC.set_priority( + #device::Interrupt::#name, + ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits), + );)); + } + + for (priority, dispatcher) in &analysis.dispatchers { + let name = &dispatcher.interrupt; + exprs.push(quote!(p.NVIC.enable(#device::Interrupt::#name);)); + exprs.push(quote!(assert!(#priority <= (1 << #nvic_prio_bits));)); + exprs.push(quote!(p.NVIC.set_priority( + #device::Interrupt::#name, + ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits), + );)); + } + // Set the cycle count to 0 and disable it while `init` executes if cfg!(feature = "timer-queue") { exprs.push(quote!(p.DWT.ctrl.modify(|r| r & !1);)); diff --git a/tests/cpass/late-not-send.rs b/tests/cpass/late-not-send.rs index b7d2d60a32..06d376bdf1 100644 --- a/tests/cpass/late-not-send.rs +++ b/tests/cpass/late-not-send.rs @@ -1,4 +1,3 @@ -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/late-resource.rs b/tests/cpass/late-resource.rs index 43f5170869..94ec8c9676 100644 --- a/tests/cpass/late-resource.rs +++ b/tests/cpass/late-resource.rs @@ -1,5 +1,4 @@ //! Runtime initialized resources -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/peripheral.rs b/tests/cpass/peripheral.rs index 0ca720cb36..509a6be162 100644 --- a/tests/cpass/peripheral.rs +++ b/tests/cpass/peripheral.rs @@ -1,5 +1,4 @@ //! Core and device peripherals -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/resource.rs b/tests/cpass/resource.rs index 5718a4ab3d..bb8373939f 100644 --- a/tests/cpass/resource.rs +++ b/tests/cpass/resource.rs @@ -1,6 +1,5 @@ //! Check code generation of resources -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/schedule.rs b/tests/cpass/schedule.rs index bc7fe34271..0728d8b322 100644 --- a/tests/cpass/schedule.rs +++ b/tests/cpass/schedule.rs @@ -1,4 +1,3 @@ -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/singleton.rs b/tests/cpass/singleton.rs index 75b736c1b1..d50f8523ee 100644 --- a/tests/cpass/singleton.rs +++ b/tests/cpass/singleton.rs @@ -1,4 +1,3 @@ -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/spawn.rs b/tests/cpass/spawn.rs index ba5a855f51..3df606a4a0 100644 --- a/tests/cpass/spawn.rs +++ b/tests/cpass/spawn.rs @@ -1,5 +1,4 @@ //! Check code generation of `spawn` -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std] diff --git a/tests/cpass/unsafe.rs b/tests/cpass/unsafe.rs index 86f461f13d..b6996ad115 100644 --- a/tests/cpass/unsafe.rs +++ b/tests/cpass/unsafe.rs @@ -1,5 +1,4 @@ //! Check code generation of `unsafe` `init` / `idle` / `exception` / `interrupt` / `task` -#![feature(extern_crate_item_prelude)] // ??? #![no_main] #![no_std]