mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-02 00:14:33 +01:00
WIP
This commit is contained in:
parent
14fedeb342
commit
b1abd52be2
10 changed files with 627 additions and 10 deletions
|
@ -16,7 +16,10 @@ quote = "0.5.1"
|
||||||
# rtfm-syntax = "0.3.0"
|
# rtfm-syntax = "0.3.0"
|
||||||
rtfm-syntax = { path = "../../rtfm-syntax" }
|
rtfm-syntax = { path = "../../rtfm-syntax" }
|
||||||
syn = "0.13.1"
|
syn = "0.13.1"
|
||||||
either = "1.5.0"
|
|
||||||
|
[dependencies.either]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
|
@ -15,11 +15,17 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
||||||
|
|
||||||
root.push(quote! {
|
root.push(quote! {
|
||||||
extern crate cortex_m_rtfm as #k;
|
extern crate cortex_m_rtfm as #k;
|
||||||
|
use #k::Resource as _cortex_m_rtfm_Resource;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Resources */
|
/* Resources */
|
||||||
let mut resources = vec![];
|
let mut resources = vec![];
|
||||||
for (name, resource) in &app.resources {
|
for (name, resource) in &app.resources {
|
||||||
|
if app.init.resources.contains(name) {
|
||||||
|
// `init` resources are handled below
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
let expr = resource
|
let expr = resource
|
||||||
.expr
|
.expr
|
||||||
|
@ -52,6 +58,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
||||||
});
|
});
|
||||||
|
|
||||||
resources.push(quote! {
|
resources.push(quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
pub struct #name { _not_send_or_sync: PhantomData<*const ()> }
|
pub struct #name { _not_send_or_sync: PhantomData<*const ()> }
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -73,6 +80,102 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* "resources" owned by `init` */
|
||||||
|
// These don't implement the `Resource` trait because they are never shared. Instead they
|
||||||
|
// implement the `Singleton` trait and may or may not appear wrapped in `Uninit`
|
||||||
|
for (name, resource) in &app.resources {
|
||||||
|
if !app.init.resources.contains(name) {
|
||||||
|
// `init` resources are handled below
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = &resource.ty;
|
||||||
|
let expr = resource.expr.as_ref().map(|e| quote!(#e)).unwrap_or_else(|| {
|
||||||
|
quote!(unsafe { #k::_impl::uninitialized() })
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO replace this with a call to `heapless::singleton!` when it doesn't require a feature
|
||||||
|
// gate in the user code
|
||||||
|
root.push(quote! {
|
||||||
|
pub struct #name { _private: #k::_impl::Private }
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe impl #k::_impl::Singleton for #name {
|
||||||
|
type Data = #ty;
|
||||||
|
|
||||||
|
unsafe fn _var() -> &'static mut #ty {
|
||||||
|
static mut VAR: #ty = #expr;
|
||||||
|
|
||||||
|
&mut VAR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
impl AsRef<#ty> for #name {
|
||||||
|
fn as_ref(&self) -> &#ty {
|
||||||
|
use #k::_impl::Singleton;
|
||||||
|
|
||||||
|
unsafe { #name::_var() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
impl AsMut<#ty> for #name {
|
||||||
|
fn as_mut(&mut self) -> &mut #ty {
|
||||||
|
use #k::_impl::Singleton;
|
||||||
|
|
||||||
|
unsafe { #name::_var() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Deref for #name {
|
||||||
|
type Target = #ty;
|
||||||
|
|
||||||
|
fn deref(&self) -> &#ty {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::DerefMut for #name {
|
||||||
|
fn deref_mut(&mut self) -> &mut #ty {
|
||||||
|
self.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
impl Into<&'static mut #ty> for #name {
|
||||||
|
fn into(self) -> &'static mut #ty {
|
||||||
|
use #k::_impl::Singleton;
|
||||||
|
|
||||||
|
unsafe { #name::_var() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe impl #k::_impl::StableDeref for #name {}
|
||||||
|
});
|
||||||
|
|
||||||
|
if resource.expr.is_some() {
|
||||||
|
root.push(quote! {
|
||||||
|
impl #name {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn _new() -> Self {
|
||||||
|
#name { _private: #k::_impl::Private::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
root.push(quote! {
|
||||||
|
impl #name {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn _new() -> #k::_impl::Uninit<Self> {
|
||||||
|
#k::_impl::Uninit::new(#name { _private: #k::_impl::Private::new() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Tasks */
|
/* Tasks */
|
||||||
for (name, task) in &app.tasks {
|
for (name, task) in &app.tasks {
|
||||||
let path = &task.path;
|
let path = &task.path;
|
||||||
|
@ -851,15 +954,18 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
||||||
.resources
|
.resources
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
let ty = &app.resources[r].ty;
|
if app.resources[r].expr.is_some() {
|
||||||
quote!(#r: &'static mut #ty)
|
quote!(pub #r: ::#r)
|
||||||
|
} else {
|
||||||
|
quote!(pub #r: #k::_impl::Uninit<::#r>)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let res_exprs = app.init
|
let res_exprs = app.init
|
||||||
.resources
|
.resources
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| quote!(#r: _resource::#r::_var()))
|
.map(|r| quote!(#r: #r::_new()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let tasks_fields = app.init
|
let tasks_fields = app.init
|
||||||
|
@ -895,7 +1001,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
||||||
let late_resources = app.resources
|
let late_resources = app.resources
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(name, res)| {
|
.filter_map(|(name, res)| {
|
||||||
if res.expr.is_none() {
|
if res.expr.is_none() && !app.init.resources.contains(name) {
|
||||||
let ty = &res.ty;
|
let ty = &res.ty;
|
||||||
Some(quote!(pub #name: #ty))
|
Some(quote!(pub #name: #ty))
|
||||||
} else {
|
} else {
|
||||||
|
@ -993,7 +1099,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
||||||
|
|
||||||
// Initialize LateResources
|
// Initialize LateResources
|
||||||
for (name, res) in &app.resources {
|
for (name, res) in &app.resources {
|
||||||
if res.expr.is_none() {
|
if res.expr.is_none() && !app.init.resources.contains(name) {
|
||||||
post_init.push(quote! {
|
post_init.push(quote! {
|
||||||
core::ptr::write(_resource::#name::_var(), _lr.#name);
|
core::ptr::write(_resource::#name::_var(), _lr.#name);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,11 @@ pub use self::tq::{dispatch, NotReady, TimerQueue};
|
||||||
pub use cortex_m::interrupt;
|
pub use cortex_m::interrupt;
|
||||||
use cortex_m::interrupt::Nr;
|
use cortex_m::interrupt::Nr;
|
||||||
pub use cortex_m::peripheral::syst::SystClkSource;
|
pub use cortex_m::peripheral::syst::SystClkSource;
|
||||||
use cortex_m::peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, SYST, TPIU};
|
use cortex_m::peripheral::{CBP, CPUID, DCB, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU};
|
||||||
|
#[cfg(not(feature = "timer-queue"))]
|
||||||
|
use cortex_m::peripheral::{DWT, SYST};
|
||||||
|
pub use heapless::object_pool::{Singleton, Uninit};
|
||||||
|
pub use stable_deref_trait::StableDeref;
|
||||||
use heapless::RingBuffer as Queue;
|
use heapless::RingBuffer as Queue;
|
||||||
pub use typenum::consts::*;
|
pub use typenum::consts::*;
|
||||||
pub use typenum::{Max, Maximum, Unsigned};
|
pub use typenum::{Max, Maximum, Unsigned};
|
||||||
|
@ -16,6 +20,16 @@ mod tq;
|
||||||
pub type FreeQueue<N> = Queue<u8, N, u8>;
|
pub type FreeQueue<N> = Queue<u8, N, u8>;
|
||||||
pub type ReadyQueue<T, N> = Queue<(T, u8), N, u8>;
|
pub type ReadyQueue<T, N> = Queue<(T, u8), N, u8>;
|
||||||
|
|
||||||
|
pub struct Private {
|
||||||
|
_0: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Private {
|
||||||
|
pub unsafe fn new() -> Self {
|
||||||
|
Private { _0: () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(feature = "timer-queue")]
|
#[cfg(feature = "timer-queue")]
|
||||||
pub struct Peripherals<'a> {
|
pub struct Peripherals<'a> {
|
||||||
|
@ -33,7 +47,7 @@ pub struct Peripherals<'a> {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(not(feature = "timer-queue"))]
|
#[cfg(not(feature = "timer-queue"))]
|
||||||
pub struct Peripherals {
|
pub struct Peripherals<'a> {
|
||||||
pub CBP: CBP,
|
pub CBP: CBP,
|
||||||
pub CPUID: CPUID,
|
pub CPUID: CPUID,
|
||||||
pub DCB: DCB,
|
pub DCB: DCB,
|
||||||
|
@ -43,7 +57,7 @@ pub struct Peripherals {
|
||||||
pub ITM: ITM,
|
pub ITM: ITM,
|
||||||
pub MPU: MPU,
|
pub MPU: MPU,
|
||||||
// pub NVIC: NVIC,
|
// pub NVIC: NVIC,
|
||||||
pub SCB: SCB,
|
pub SCB: &'a mut SCB,
|
||||||
pub SYST: SYST,
|
pub SYST: SYST,
|
||||||
pub TPIU: TPIU,
|
pub TPIU: TPIU,
|
||||||
}
|
}
|
||||||
|
|
39
src/event_task.rs
Normal file
39
src/event_task.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//! An event task, a task that starts in response to an event (interrupt source)
|
||||||
|
|
||||||
|
use Priority;
|
||||||
|
|
||||||
|
/// The execution context of this event task
|
||||||
|
pub struct Context {
|
||||||
|
/// The time at which this task started executing
|
||||||
|
///
|
||||||
|
/// *NOTE* that this is not the *arrival* time of the event that started this task. Due to
|
||||||
|
/// prioritization of other tasks this task could have started much later than the time the
|
||||||
|
/// event arrived at.
|
||||||
|
///
|
||||||
|
/// *This field is only available if the `"timer-queue"` feature is enabled*
|
||||||
|
pub baseline: u32,
|
||||||
|
|
||||||
|
/// The input of this task
|
||||||
|
pub input: Input,
|
||||||
|
|
||||||
|
/// The starting priority of this task
|
||||||
|
pub priority: Priority<P>,
|
||||||
|
|
||||||
|
/// Resources assigned to this event task
|
||||||
|
pub resources: Resources,
|
||||||
|
|
||||||
|
/// Tasks that this event task can schedule
|
||||||
|
pub tasks: Tasks,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct Input;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct P;
|
||||||
|
|
||||||
|
/// Resources assigned to this event task
|
||||||
|
pub struct Resources {}
|
||||||
|
|
||||||
|
/// Tasks that this event task can schedule
|
||||||
|
pub struct Tasks {}
|
24
src/idle.rs
Normal file
24
src/idle.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//! The `idle` function
|
||||||
|
|
||||||
|
use Priority;
|
||||||
|
|
||||||
|
use typenum::consts::U0;
|
||||||
|
|
||||||
|
/// The execution context of `idle`
|
||||||
|
pub struct Context {
|
||||||
|
/// The starting priority of `idle`
|
||||||
|
pub priority: Priority<U0>,
|
||||||
|
|
||||||
|
/// Resources assigned to `idle`
|
||||||
|
pub resources: Resources,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resources assigned to `idle`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct Resources {
|
||||||
|
/// Example of a resource assigned to `idle`
|
||||||
|
pub KEY: KEY,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct KEY;
|
66
src/init.rs
Normal file
66
src/init.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
//! The `init`ialization function
|
||||||
|
use typenum::consts::U255;
|
||||||
|
|
||||||
|
use Priority;
|
||||||
|
pub use _impl::Peripherals as Core;
|
||||||
|
|
||||||
|
/// Execution context of `init`
|
||||||
|
pub struct Context<'a> {
|
||||||
|
/// Core (Cortex-M) peripherals
|
||||||
|
pub core: Core<'a>,
|
||||||
|
/// Device specific peripherals
|
||||||
|
pub device: Device,
|
||||||
|
/// The priority of `init`
|
||||||
|
pub priority: Priority<U255>,
|
||||||
|
/// Resources assigned to `init`
|
||||||
|
pub resources: Resources,
|
||||||
|
/// Tasks that `init` can schedule
|
||||||
|
pub tasks: Tasks,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Device specific peripherals
|
||||||
|
///
|
||||||
|
/// The contents of this `struct` will depend on the selected `device`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct Device {
|
||||||
|
/// Example
|
||||||
|
pub GPIOA: GPIOA,
|
||||||
|
/// Example
|
||||||
|
pub TIM2: TIM2,
|
||||||
|
/// Example
|
||||||
|
pub USART1: USART1,
|
||||||
|
_more: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct GPIOA;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct RCC;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct TIM2;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct USART1;
|
||||||
|
|
||||||
|
/// The initial value of resources that were not given an initial value in `app.resources`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct LateResources {
|
||||||
|
/// Example of a resource that's initialized "late", or at runtime
|
||||||
|
pub KEY: [u8; 128],
|
||||||
|
_more: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resources assigned to and owned by `init`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct Resources {
|
||||||
|
/// Example of a resource assigned to `init`
|
||||||
|
pub BUFFER: BUFFER,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct BUFFER;
|
||||||
|
|
||||||
|
/// Tasks that `init` can schedule
|
||||||
|
pub struct Tasks {}
|
203
src/lib.rs
203
src/lib.rs
|
@ -1,4 +1,146 @@
|
||||||
//! Real Time for The Masses: high performance, predictable, bare metal task scheduler
|
//! Real Time for The Masses: a high performance and predictable bare metal task scheduler
|
||||||
|
//!
|
||||||
|
//! # Features
|
||||||
|
//!
|
||||||
|
//! - Priority based scheduler implemented mostly in hardware with minimal bookkeeping and overhead.
|
||||||
|
//! - Tasks can be started in response to events or scheduled on-demand
|
||||||
|
//! - Message passing between tasks
|
||||||
|
//! - Data race free sharing of resources (e.g. memory) between tasks using the Priority Ceiling
|
||||||
|
//! Protocol (PCP).
|
||||||
|
//! - Guaranteed dead lock free execution
|
||||||
|
//! - Doesn't need a memory allocator to operate
|
||||||
|
//!
|
||||||
|
//! # User guide and internal documentation
|
||||||
|
//!
|
||||||
|
//! Check [the RTFM book] instead. These auto-generated docs only contain the API reference and some
|
||||||
|
//! examples.
|
||||||
|
//!
|
||||||
|
//! [the RTFM book]: TODO
|
||||||
|
//!
|
||||||
|
//! # `app!`
|
||||||
|
//!
|
||||||
|
//! The `app!` macro contains the specification of an application. It declares the tasks that
|
||||||
|
//! compose the application of and how resources (`static` variables) are distributed across them.
|
||||||
|
//!
|
||||||
|
//! This section describes the syntax of the `app!` macro.
|
||||||
|
//!
|
||||||
|
//! ## `app.device`
|
||||||
|
//!
|
||||||
|
//! ``` ignore
|
||||||
|
//! app! {
|
||||||
|
//! device: some::path,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! This field specifies the target device as a path to a crate generated using [`svd2rust`]
|
||||||
|
//! v0.13.x.
|
||||||
|
//!
|
||||||
|
//! [`svd2rust`]: https://crates.io/crates/svd2rust
|
||||||
|
//!
|
||||||
|
//! ## `app.resources`
|
||||||
|
//!
|
||||||
|
//! This section contains a list of `static` variables that will be used as resources. These
|
||||||
|
//! variables don't need to be assigned an initial value. If a resource lacks an initial value it
|
||||||
|
//! will have to be assigned one in `init`.
|
||||||
|
//!
|
||||||
|
//! ``` ignore
|
||||||
|
//! app! {
|
||||||
|
//! resources: {
|
||||||
|
//! // Resource with initial value; its initial value is stored in Flash
|
||||||
|
//! static STATE: bool = false;
|
||||||
|
//!
|
||||||
|
//! // Resource without initial value; it will be initialized at runtime
|
||||||
|
//! static KEY: [u8; 128];
|
||||||
|
//!
|
||||||
|
//! // ..
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## `app.free_interrupts`
|
||||||
|
//!
|
||||||
|
//! This a list of interrupts that the RTFM runtime can use to dispatch on-demand tasks.
|
||||||
|
//!
|
||||||
|
//! ## `app.init`
|
||||||
|
//!
|
||||||
|
//! This section describes the context of the [`init`][fn@init]ialization function.
|
||||||
|
//!
|
||||||
|
//! ``` ignore
|
||||||
|
//! app! {
|
||||||
|
//! init: {
|
||||||
|
//! body: some::path,
|
||||||
|
//! resources: [A, B],
|
||||||
|
//! schedule_now: [on_demand_task],
|
||||||
|
//! schedule_after: [task_a],
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### `app.init.body`
|
||||||
|
//!
|
||||||
|
//! This is the path to the `init` function. If omitted this field will default to `init`.
|
||||||
|
//!
|
||||||
|
//! ### `app.init.resources`
|
||||||
|
//!
|
||||||
|
//! The resources assigned to, and owned by, `init`. This field is optional; if omitted this field
|
||||||
|
//! defaults to an empty list.
|
||||||
|
//!
|
||||||
|
//! ### `app.init.schedule_now` / `app.init.schedule_after`
|
||||||
|
//!
|
||||||
|
//! List of tasks `init` can schedule via the [`schedule_now`] and [`schedule_after`] APIs.
|
||||||
|
//!
|
||||||
|
//! [`schedule_now`]: trait.ScheduleNow.html
|
||||||
|
//! [`schedule_after`]: trait.ScheduleAfter.html
|
||||||
|
//!
|
||||||
|
//! ## `app.idle`
|
||||||
|
//!
|
||||||
|
//! ## `app.tasks`
|
||||||
|
//!
|
||||||
|
//! This section contains a list of tasks. These tasks can be event tasks or on-demand tasks.
|
||||||
|
//!
|
||||||
|
//! ``` ignore
|
||||||
|
//! app! {
|
||||||
|
//! tasks: {
|
||||||
|
//! event_task: {
|
||||||
|
//! body: some::path,
|
||||||
|
//! interrupt: USART1,
|
||||||
|
//! resources: [STATE],
|
||||||
|
//! schedule_now: [on_demand_task],
|
||||||
|
//! schedule_after: [task_a],
|
||||||
|
//! },
|
||||||
|
//!
|
||||||
|
//! on_demand_task: {
|
||||||
|
//! body: some::other::path,
|
||||||
|
//! instances: 2,
|
||||||
|
//! resources: [STATE],
|
||||||
|
//! schedule_now: [on_demand_task],
|
||||||
|
//! schedule_after: [task_a],
|
||||||
|
//! },
|
||||||
|
//!
|
||||||
|
//! // more tasks here
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### `app.tasks.$.body`
|
||||||
|
//!
|
||||||
|
//! The path to the body of the task. This field is optional; if omitted the path defaults to the
|
||||||
|
//! name of the task.
|
||||||
|
//!
|
||||||
|
//! ### `app.tasks.$.interrupt`
|
||||||
|
//!
|
||||||
|
//! Event tasks only. This is the event, or interrupt source, that triggers the execution of the
|
||||||
|
//! task.
|
||||||
|
//!
|
||||||
|
//! ### `app.tasks.$.instances`
|
||||||
|
//!
|
||||||
|
//! On-demand tasks only. The maximum number of times this task can be scheduled and remain in a
|
||||||
|
//! pending execution, or ready, state. This field is optional; if omitted, it defaults to `1`.
|
||||||
|
//!
|
||||||
|
//! ### `app.tasks.$.resources`
|
||||||
|
//!
|
||||||
|
//! The resources assigned to this task. This field is optional; if omitted this field defaults to
|
||||||
|
//! an empty list.
|
||||||
|
|
||||||
#![allow(warnings)]
|
#![allow(warnings)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
@ -13,6 +155,7 @@ extern crate cortex_m;
|
||||||
extern crate cortex_m_rtfm_macros;
|
extern crate cortex_m_rtfm_macros;
|
||||||
extern crate heapless;
|
extern crate heapless;
|
||||||
extern crate typenum;
|
extern crate typenum;
|
||||||
|
extern crate stable_deref_trait;
|
||||||
|
|
||||||
use cortex_m::interrupt;
|
use cortex_m::interrupt;
|
||||||
pub use cortex_m_rtfm_macros::app;
|
pub use cortex_m_rtfm_macros::app;
|
||||||
|
@ -23,6 +166,10 @@ pub use resource::{Priority, Resource};
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod _impl;
|
pub mod _impl;
|
||||||
|
pub mod event_task;
|
||||||
|
pub mod idle;
|
||||||
|
pub mod init;
|
||||||
|
pub mod on_demand_task;
|
||||||
mod resource;
|
mod resource;
|
||||||
|
|
||||||
/// Executes the given closure atomically
|
/// Executes the given closure atomically
|
||||||
|
@ -47,3 +194,57 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `init`ialization function takes care of system and resource initialization
|
||||||
|
pub fn init(_ctxt: init::Context) -> init::LateResources {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When no task is being executed the processor resumes the execution of the `idle` function
|
||||||
|
pub fn idle(_ctxt: idle::Context) -> ! {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `schedule_now` interface
|
||||||
|
pub trait ScheduleNow {
|
||||||
|
/// Optional message sent to the scheduled task
|
||||||
|
type Payload;
|
||||||
|
|
||||||
|
/// Schedules a task to run right away
|
||||||
|
///
|
||||||
|
/// This method will return an error if the maximum number of `instances` of the task are
|
||||||
|
/// already pending execution.
|
||||||
|
///
|
||||||
|
/// If `"timer-queue"` is enabled the newly scheduled task will inherit the `baseline` of the
|
||||||
|
/// *current* task.
|
||||||
|
///
|
||||||
|
/// *NOTE* that the `payload` argument is not required if the task has no input, i.e. its input
|
||||||
|
/// type is `()`
|
||||||
|
fn schedule_now<P>(
|
||||||
|
&mut self,
|
||||||
|
priority: &mut Priority<P>,
|
||||||
|
payload: Self::Payload,
|
||||||
|
) -> Result<(), Self::Payload>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `schedule_after` interface
|
||||||
|
///
|
||||||
|
/// *NOTE* that this API is only available if the `"timer-queue"` feature is enabled.
|
||||||
|
pub trait ScheduleAfter {
|
||||||
|
/// Optional message sent to the scheduled task
|
||||||
|
type Payload;
|
||||||
|
|
||||||
|
/// Schedules a task to run `offset` ticks after the *current* task `baseline`.
|
||||||
|
///
|
||||||
|
/// This method will return an error if the maximum number of instances of the task are pending
|
||||||
|
/// execution.
|
||||||
|
///
|
||||||
|
/// *NOTE* that the `payload` argument is not required if the task has no input, i.e. its input
|
||||||
|
/// type is `()`
|
||||||
|
fn schedule_after<P>(
|
||||||
|
&mut self,
|
||||||
|
priority: &mut Priority<P>,
|
||||||
|
offset: u32,
|
||||||
|
payload: Self::Payload,
|
||||||
|
) -> Result<(), Self::Payload>;
|
||||||
|
}
|
||||||
|
|
39
src/on_demand_task.rs
Normal file
39
src/on_demand_task.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//! An schedulable task
|
||||||
|
|
||||||
|
use Priority;
|
||||||
|
|
||||||
|
/// The execution context of this schedulable task
|
||||||
|
pub struct Context {
|
||||||
|
/// The time at which this task was scheduled to run
|
||||||
|
///
|
||||||
|
/// *NOTE* that this is not the *start* time of the task. Due to scheduling overhead a task will
|
||||||
|
/// always start a bit later than its scheduled time. Also due to prioritization of other tasks
|
||||||
|
/// a task may start much later than its scheduled time.
|
||||||
|
///
|
||||||
|
/// *This field is only available if the `"timer-queue"` feature is enabled*
|
||||||
|
pub baseline: u32,
|
||||||
|
|
||||||
|
/// The input of this task
|
||||||
|
pub input: Input,
|
||||||
|
|
||||||
|
/// The starting priority of this task
|
||||||
|
pub priority: Priority<P>,
|
||||||
|
|
||||||
|
/// Resources assigned to this event task
|
||||||
|
pub resources: Resources,
|
||||||
|
|
||||||
|
/// Tasks that this event task can schedule
|
||||||
|
pub tasks: Tasks,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct Input;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct P;
|
||||||
|
|
||||||
|
/// Resources assigned to this event task
|
||||||
|
pub struct Resources {}
|
||||||
|
|
||||||
|
/// Tasks that this event task can schedule
|
||||||
|
pub struct Tasks {}
|
125
src/tq.rs
Normal file
125
src/tq.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use core::cmp::{self, Ordering};
|
||||||
|
|
||||||
|
use cortex_m::peripheral::{SCB, SYST};
|
||||||
|
use heapless::binary_heap::{BinaryHeap, Min};
|
||||||
|
use heapless::ArrayLength;
|
||||||
|
use typenum::{Max, Maximum, Unsigned};
|
||||||
|
|
||||||
|
use instant::Instant;
|
||||||
|
use resource::{Resource, Threshold};
|
||||||
|
|
||||||
|
pub struct Message<T> {
|
||||||
|
pub baseline: Instant,
|
||||||
|
pub index: u8,
|
||||||
|
pub task: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Eq for Message<T> {}
|
||||||
|
|
||||||
|
impl<T> Ord for Message<T> {
|
||||||
|
fn cmp(&self, other: &Message<T>) -> Ordering {
|
||||||
|
self.baseline.cmp(&other.baseline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PartialEq for Message<T> {
|
||||||
|
fn eq(&self, other: &Message<T>) -> bool {
|
||||||
|
self.baseline == other.baseline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PartialOrd for Message<T> {
|
||||||
|
fn partial_cmp(&self, other: &Message<T>) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct TimerQueue<T, N>
|
||||||
|
where
|
||||||
|
N: ArrayLength<Message<T>>,
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub syst: SYST,
|
||||||
|
pub queue: BinaryHeap<Message<T>, N, Min>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, N> TimerQueue<T, N>
|
||||||
|
where
|
||||||
|
N: ArrayLength<Message<T>>,
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub const fn new(syst: SYST) -> Self {
|
||||||
|
TimerQueue {
|
||||||
|
syst,
|
||||||
|
queue: BinaryHeap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn enqueue(&mut self, m: Message<T>) {
|
||||||
|
let mut is_empty = true;
|
||||||
|
if self.queue
|
||||||
|
.peek()
|
||||||
|
.map(|head| {
|
||||||
|
is_empty = false;
|
||||||
|
m.baseline < head.baseline
|
||||||
|
})
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
if is_empty {
|
||||||
|
self.syst.enable_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set SysTick pending
|
||||||
|
unsafe { (*SCB::ptr()).icsr.write(1 << 26) }
|
||||||
|
}
|
||||||
|
|
||||||
|
self.queue.push_unchecked(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch<T, TQ, N, F, P>(t: &mut Threshold<P>, tq: &mut TQ, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut Threshold<P>, T, u8),
|
||||||
|
N: 'static + ArrayLength<Message<T>>,
|
||||||
|
P: Max<TQ::Ceiling> + Unsigned,
|
||||||
|
T: 'static + Copy + Send,
|
||||||
|
TQ: Resource<Data = TimerQueue<T, N>>,
|
||||||
|
TQ::Ceiling: Unsigned,
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
let next = tq.claim_mut(t, |tq, _| {
|
||||||
|
if let Some(bl) = tq.queue.peek().map(|p| p.baseline) {
|
||||||
|
let diff = bl - Instant::now();
|
||||||
|
|
||||||
|
if diff < 0 {
|
||||||
|
// message ready
|
||||||
|
let m = unsafe { tq.queue.pop_unchecked() };
|
||||||
|
|
||||||
|
Some((m.task, m.index))
|
||||||
|
} else {
|
||||||
|
// set a new timeout
|
||||||
|
const MAX: u32 = 0x00ffffff;
|
||||||
|
|
||||||
|
tq.syst.set_reload(cmp::min(MAX, diff as u32));
|
||||||
|
|
||||||
|
// start counting from the new reload
|
||||||
|
tq.syst.clear_current();
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// empty queue
|
||||||
|
tq.syst.disable_interrupt();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((task, index)) = next {
|
||||||
|
f(t, task, index)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue