mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-23 20:22:51 +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 = { path = "../../rtfm-syntax" }
|
||||
syn = "0.13.1"
|
||||
either = "1.5.0"
|
||||
|
||||
[dependencies.either]
|
||||
version = "1"
|
||||
default-features = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
|
|
@ -15,11 +15,17 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
|||
|
||||
root.push(quote! {
|
||||
extern crate cortex_m_rtfm as #k;
|
||||
use #k::Resource as _cortex_m_rtfm_Resource;
|
||||
});
|
||||
|
||||
/* Resources */
|
||||
let mut resources = vec![];
|
||||
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
|
||||
|
@ -52,6 +58,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
|||
});
|
||||
|
||||
resources.push(quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct #name { _not_send_or_sync: PhantomData<*const ()> }
|
||||
|
||||
#[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 */
|
||||
for (name, task) in &app.tasks {
|
||||
let path = &task.path;
|
||||
|
@ -851,15 +954,18 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
|||
.resources
|
||||
.iter()
|
||||
.map(|r| {
|
||||
let ty = &app.resources[r].ty;
|
||||
quote!(#r: &'static mut #ty)
|
||||
if app.resources[r].expr.is_some() {
|
||||
quote!(pub #r: ::#r)
|
||||
} else {
|
||||
quote!(pub #r: #k::_impl::Uninit<::#r>)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let res_exprs = app.init
|
||||
.resources
|
||||
.iter()
|
||||
.map(|r| quote!(#r: _resource::#r::_var()))
|
||||
.map(|r| quote!(#r: #r::_new()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let tasks_fields = app.init
|
||||
|
@ -895,7 +1001,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
|||
let late_resources = app.resources
|
||||
.iter()
|
||||
.filter_map(|(name, res)| {
|
||||
if res.expr.is_none() {
|
||||
if res.expr.is_none() && !app.init.resources.contains(name) {
|
||||
let ty = &res.ty;
|
||||
Some(quote!(pub #name: #ty))
|
||||
} else {
|
||||
|
@ -993,7 +1099,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens {
|
|||
|
||||
// Initialize LateResources
|
||||
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! {
|
||||
core::ptr::write(_resource::#name::_var(), _lr.#name);
|
||||
});
|
||||
|
|
|
@ -5,7 +5,11 @@ pub use self::tq::{dispatch, NotReady, TimerQueue};
|
|||
pub use cortex_m::interrupt;
|
||||
use cortex_m::interrupt::Nr;
|
||||
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;
|
||||
pub use typenum::consts::*;
|
||||
pub use typenum::{Max, Maximum, Unsigned};
|
||||
|
@ -16,6 +20,16 @@ mod tq;
|
|||
pub type FreeQueue<N> = Queue<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)]
|
||||
#[cfg(feature = "timer-queue")]
|
||||
pub struct Peripherals<'a> {
|
||||
|
@ -33,7 +47,7 @@ pub struct Peripherals<'a> {
|
|||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(not(feature = "timer-queue"))]
|
||||
pub struct Peripherals {
|
||||
pub struct Peripherals<'a> {
|
||||
pub CBP: CBP,
|
||||
pub CPUID: CPUID,
|
||||
pub DCB: DCB,
|
||||
|
@ -43,7 +57,7 @@ pub struct Peripherals {
|
|||
pub ITM: ITM,
|
||||
pub MPU: MPU,
|
||||
// pub NVIC: NVIC,
|
||||
pub SCB: SCB,
|
||||
pub SCB: &'a mut SCB,
|
||||
pub SYST: SYST,
|
||||
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)]
|
||||
#![deny(missing_docs)]
|
||||
|
@ -13,6 +155,7 @@ extern crate cortex_m;
|
|||
extern crate cortex_m_rtfm_macros;
|
||||
extern crate heapless;
|
||||
extern crate typenum;
|
||||
extern crate stable_deref_trait;
|
||||
|
||||
use cortex_m::interrupt;
|
||||
pub use cortex_m_rtfm_macros::app;
|
||||
|
@ -23,6 +166,10 @@ pub use resource::{Priority, Resource};
|
|||
|
||||
#[doc(hidden)]
|
||||
pub mod _impl;
|
||||
pub mod event_task;
|
||||
pub mod idle;
|
||||
pub mod init;
|
||||
pub mod on_demand_task;
|
||||
mod resource;
|
||||
|
||||
/// 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