const generics

This commit is contained in:
Andrey Zgarbul 2021-04-03 20:30:34 +03:00
parent c67657371b
commit e4319de3d5
11 changed files with 50 additions and 94 deletions

View file

@ -44,9 +44,8 @@ cortex-m = "0.7.0"
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" } cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" }
rtic-monotonic = "0.1.0-alpha.2" rtic-monotonic = "0.1.0-alpha.2"
rtic-core = "0.3.1" rtic-core = "0.3.1"
heapless = "0.6.1" heapless = "0.7.1"
bare-metal = "1.0.0" bare-metal = "1.0.0"
generic-array = "0.14"
[dependencies.dwt-systick-monotonic] [dependencies.dwt-systick-monotonic]
version = "0.1.0-alpha.3" version = "0.1.0-alpha.3"

View file

@ -78,8 +78,8 @@ mod app {
} }
// ready queue of the task dispatcher // ready queue of the task dispatcher
// `U4` is a type-level integer that represents the capacity of this queue // `5-1=4` represents the capacity of this queue
static mut RQ1: Queue<Ready<T1>, U4> = Queue::new(); static mut RQ1: Queue<Ready<T1>, 5> = Queue::new();
// interrupt handler chosen to dispatch tasks at priority `1` // interrupt handler chosen to dispatch tasks at priority `1`
#[no_mangle] #[no_mangle]
@ -153,7 +153,7 @@ mod app {
// used to track how many more `bar` messages can be enqueued // used to track how many more `bar` messages can be enqueued
// `U2` is the capacity of the `bar` task; a max of two instances can be queued // `U2` is the capacity of the `bar` task; a max of two instances can be queued
// this queue is filled by the framework before `init` runs // this queue is filled by the framework before `init` runs
static mut bar_FQ: Queue<(), U2> = Queue::new(); static mut bar_FQ: Queue<(), 3> = Queue::new();
// Priority ceiling for the consumer endpoint of `bar_FQ` // Priority ceiling for the consumer endpoint of `bar_FQ`
const bar_FQ_CEILING: u8 = 2; const bar_FQ_CEILING: u8 = 2;
@ -227,7 +227,7 @@ mod app {
// the free queue: used to track free slots in the `baz_INPUTS` array // the free queue: used to track free slots in the `baz_INPUTS` array
// this queue is initialized with values `0` and `1` before `init` is executed // this queue is initialized with values `0` and `1` before `init` is executed
static mut baz_FQ: Queue<u8, U2> = Queue::new(); static mut baz_FQ: Queue<u8, 3> = Queue::new();
// Priority ceiling for the consumer endpoint of `baz_FQ` // Priority ceiling for the consumer endpoint of `baz_FQ`
const baz_FQ_CEILING: u8 = 2; const baz_FQ_CEILING: u8 = 2;

View file

@ -10,23 +10,19 @@ use panic_semihosting as _;
#[rtic::app(device = lm3s6965)] #[rtic::app(device = lm3s6965)]
mod app { mod app {
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use heapless::{ use heapless::spsc::{Consumer, Producer, Queue};
consts::*,
i,
spsc::{Consumer, Producer, Queue},
};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
#[shared] #[shared]
struct Shared { struct Shared {
p: Producer<'static, u32, U4>, p: Producer<'static, u32, 5>,
c: Consumer<'static, u32, U4>, c: Consumer<'static, u32, 5>,
} }
#[local] #[local]
struct Local {} struct Local {}
#[init(local = [q: Queue<u32, U4> = Queue(i::Queue::new())])] #[init(local = [q: Queue<u32, 5> = Queue::new()])]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let (p, c) = cx.local.q.split(); let (p, c) = cx.local.q.split();

View file

@ -11,23 +11,19 @@ use panic_semihosting as _;
mod app { mod app {
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use heapless::{ use heapless::spsc::{Consumer, Producer, Queue};
consts::*,
i,
spsc::{Consumer, Producer, Queue},
};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
#[shared] #[shared]
struct Shared { struct Shared {
p: Producer<'static, u32, U4>, p: Producer<'static, u32, 5>,
c: Consumer<'static, u32, U4>, c: Consumer<'static, u32, 5>,
} }
#[local] #[local]
struct Local {} struct Local {}
#[init(local = [q: Queue<u32, U4> = Queue(i::Queue::new())])] #[init(local = [q: Queue<u32, 5> = Queue::new()])]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let (p, c) = cx.local.q.split(); let (p, c) = cx.local.q.split();

View file

@ -42,15 +42,13 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
} }
)); ));
let n = util::capacity_typenum(channel.capacity, true); let n = util::capacity_literal(channel.capacity as usize + 1);
let rq = util::rq_ident(level); let rq = util::rq_ident(level);
let rq = util::mark_internal_ident(&rq); let rq = util::mark_internal_ident(&rq);
let (rq_ty, rq_expr) = { let (rq_ty, rq_expr) = {
( (
quote!(rtic::export::SCRQ<#t, #n>), quote!(rtic::export::SCRQ<#t, #n>),
quote!(rtic::export::Queue(unsafe { quote!(rtic::export::Queue::new()),
rtic::export::iQueue::u8_sc()
})),
) )
}; };

View file

@ -32,8 +32,8 @@ pub fn codegen(
let (_, _, _, input_ty) = util::regroup_inputs(inputs); let (_, _, _, input_ty) = util::regroup_inputs(inputs);
let cap = task.args.capacity; let cap = task.args.capacity;
let cap_lit = util::capacity_literal(cap); let cap_lit = util::capacity_literal(cap as usize);
let cap_ty = util::capacity_typenum(cap, true); let cap_lit_p1 = util::capacity_literal(cap as usize + 1);
// Create free queues and inputs / instants buffers // Create free queues and inputs / instants buffers
let fq = util::fq_ident(name); let fq = util::fq_ident(name);
@ -41,10 +41,8 @@ pub fn codegen(
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = { let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
( (
quote!(rtic::export::SCFQ<#cap_ty>), quote!(rtic::export::SCFQ<#cap_lit_p1>),
quote!(rtic::export::Queue(unsafe { quote!(rtic::export::Queue::new()),
rtic::export::iQueue::u8_sc()
})),
Box::new(|| util::link_section_uninit()), Box::new(|| util::link_section_uninit()),
) )
}; };

View file

@ -62,12 +62,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
{ {
// For future use // For future use
// let doc = &format!("Timer queue for {}", monotonic_name); // let doc = &format!("Timer queue for {}", monotonic_name);
let cap = app let cap: u8 = app
.software_tasks .software_tasks
.iter() .iter()
.map(|(_name, task)| task.args.capacity) .map(|(_name, task)| task.args.capacity)
.sum(); .sum();
let n = util::capacity_typenum(cap, false); let n = util::capacity_literal(cap as usize);
let tq_ty = let tq_ty =
quote!(core::mem::MaybeUninit<rtic::export::TimerQueue<#mono_type, #t, #n>>); quote!(core::mem::MaybeUninit<rtic::export::TimerQueue<#mono_type, #t, #n>>);

View file

@ -8,23 +8,10 @@ use syn::{Attribute, Ident, LitInt, PatType};
use crate::check::Extra; use crate::check::Extra;
/// Turns `capacity` into an unsuffixed integer literal /// Turns `capacity` into an unsuffixed integer literal
pub fn capacity_literal(capacity: u8) -> LitInt { pub fn capacity_literal(capacity: usize) -> LitInt {
LitInt::new(&capacity.to_string(), Span::call_site()) LitInt::new(&capacity.to_string(), 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!(rtic::export::consts::#ident)
}
/// Identifier for the free queue /// Identifier for the free queue
pub fn fq_ident(task: &Ident) -> Ident { pub fn fq_ident(task: &Ident) -> Ident {
Ident::new(&format!("{}_FQ", task.to_string()), Span::call_site()) Ident::new(&format!("{}_FQ", task.to_string()), Span::call_site())

View file

@ -13,13 +13,12 @@ pub use cortex_m::{
peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC}, peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC},
Peripherals, Peripherals,
}; };
use heapless::spsc::SingleCore; pub use heapless::spsc::Queue;
pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; pub use heapless::BinaryHeap;
pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap};
pub use rtic_monotonic as monotonic; pub use rtic_monotonic as monotonic;
pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>; pub type SCFQ<const N: usize> = Queue<u8, N>;
pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>; pub type SCRQ<T, const N: usize> = Queue<(T, u8), N>;
#[cfg(armv7m)] #[cfg(armv7m)]
#[inline(always)] #[inline(always)]

View file

@ -3,8 +3,6 @@ use core::marker::PhantomData;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::ptr; use core::ptr;
pub use generic_array::ArrayLength;
use generic_array::GenericArray;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct LinkedIndex(u16); struct LinkedIndex(u16);
@ -37,21 +35,19 @@ pub struct Node<T> {
} }
/// Iterator for the linked list. /// Iterator for the linked list.
pub struct Iter<'a, T, Kind, N> pub struct Iter<'a, T, Kind, const N: usize>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
list: &'a LinkedList<T, Kind, N>, list: &'a LinkedList<T, Kind, N>,
index: LinkedIndex, index: LinkedIndex,
} }
impl<'a, T, Kind, N> Iterator for Iter<'a, T, Kind, N> impl<'a, T, Kind, const N: usize> Iterator for Iter<'a, T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
type Item = &'a T; type Item = &'a T;
@ -66,11 +62,10 @@ where
} }
/// Comes from [`LinkedList::find_mut`]. /// Comes from [`LinkedList::find_mut`].
pub struct FindMut<'a, T, Kind, N> pub struct FindMut<'a, T, Kind, const N: usize>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
list: &'a mut LinkedList<T, Kind, N>, list: &'a mut LinkedList<T, Kind, N>,
is_head: bool, is_head: bool,
@ -79,11 +74,10 @@ where
maybe_changed: bool, maybe_changed: bool,
} }
impl<'a, T, Kind, N> FindMut<'a, T, Kind, N> impl<'a, T, Kind, const N: usize> FindMut<'a, T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
fn pop_internal(&mut self) -> T { fn pop_internal(&mut self) -> T {
if self.is_head { if self.is_head {
@ -122,11 +116,10 @@ where
} }
} }
impl<T, Kind, N> Drop for FindMut<'_, T, Kind, N> impl<T, Kind, const N: usize> Drop for FindMut<'_, T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
fn drop(&mut self) { fn drop(&mut self) {
// Only resort the list if the element has changed // Only resort the list if the element has changed
@ -137,11 +130,10 @@ where
} }
} }
impl<T, Kind, N> Deref for FindMut<'_, T, Kind, N> impl<T, Kind, const N: usize> Deref for FindMut<'_, T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
type Target = T; type Target = T;
@ -150,11 +142,10 @@ where
} }
} }
impl<T, Kind, N> DerefMut for FindMut<'_, T, Kind, N> impl<T, Kind, const N: usize> DerefMut for FindMut<'_, T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.maybe_changed = true; self.maybe_changed = true;
@ -162,11 +153,10 @@ where
} }
} }
impl<T, Kind, N> fmt::Debug for FindMut<'_, T, Kind, N> impl<T, Kind, const N: usize> fmt::Debug for FindMut<'_, T, Kind, N>
where where
T: PartialEq + PartialOrd + core::fmt::Debug, T: PartialEq + PartialOrd + core::fmt::Debug,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FindMut") f.debug_struct("FindMut")
@ -189,23 +179,21 @@ where
} }
/// The linked list. /// The linked list.
pub struct LinkedList<T, Kind, N> pub struct LinkedList<T, Kind, const N: usize>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
list: MaybeUninit<GenericArray<Node<T>, N>>, list: MaybeUninit<[Node<T>; N]>,
head: LinkedIndex, head: LinkedIndex,
free: LinkedIndex, free: LinkedIndex,
_kind: PhantomData<Kind>, _kind: PhantomData<Kind>,
} }
impl<T, Kind, N> LinkedList<T, Kind, N> impl<T, Kind, const N: usize> LinkedList<T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
/// Internal helper to not do pointer arithmetic all over the place. /// Internal helper to not do pointer arithmetic all over the place.
#[inline] #[inline]
@ -266,7 +254,7 @@ where
_kind: PhantomData, _kind: PhantomData,
}; };
let len = N::U16; let len = N as u16;
let mut free = 0; let mut free = 0;
if len == 0 { if len == 0 {
@ -451,11 +439,10 @@ where
} }
} }
impl<T, Kind, N> Drop for LinkedList<T, Kind, N> impl<T, Kind, const N: usize> Drop for LinkedList<T, Kind, N>
where where
T: PartialEq + PartialOrd, T: PartialEq + PartialOrd,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
fn drop(&mut self) { fn drop(&mut self) {
let mut index = self.head; let mut index = self.head;
@ -471,11 +458,10 @@ where
} }
} }
impl<T, Kind, N> fmt::Debug for LinkedList<T, Kind, N> impl<T, Kind, const N: usize> fmt::Debug for LinkedList<T, Kind, N>
where where
T: PartialEq + PartialOrd + core::fmt::Debug, T: PartialEq + PartialOrd + core::fmt::Debug,
Kind: kind::Kind, Kind: kind::Kind,
N: ArrayLength<Node<T>>,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish() f.debug_list().entries(self.iter()).finish()
@ -518,11 +504,10 @@ pub mod kind {
mod tests { mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope. // Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*; use super::*;
use generic_array::typenum::consts::*;
#[test] #[test]
fn test_peek() { fn test_peek() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
ll.push(1).unwrap(); ll.push(1).unwrap();
assert_eq!(ll.peek().unwrap(), &1); assert_eq!(ll.peek().unwrap(), &1);
@ -533,7 +518,7 @@ mod tests {
ll.push(3).unwrap(); ll.push(3).unwrap();
assert_eq!(ll.peek().unwrap(), &3); assert_eq!(ll.peek().unwrap(), &3);
let mut ll: LinkedList<u32, Min, U3> = LinkedList::new(); let mut ll: LinkedList<u32, Min, 3> = LinkedList::new();
ll.push(2).unwrap(); ll.push(2).unwrap();
assert_eq!(ll.peek().unwrap(), &2); assert_eq!(ll.peek().unwrap(), &2);
@ -547,7 +532,7 @@ mod tests {
#[test] #[test]
fn test_full() { fn test_full() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
ll.push(1).unwrap(); ll.push(1).unwrap();
ll.push(2).unwrap(); ll.push(2).unwrap();
ll.push(3).unwrap(); ll.push(3).unwrap();
@ -557,14 +542,14 @@ mod tests {
#[test] #[test]
fn test_empty() { fn test_empty() {
let ll: LinkedList<u32, Max, U3> = LinkedList::new(); let ll: LinkedList<u32, Max, 3> = LinkedList::new();
assert!(ll.is_empty()) assert!(ll.is_empty())
} }
#[test] #[test]
fn test_zero_size() { fn test_zero_size() {
let ll: LinkedList<u32, Max, U0> = LinkedList::new(); let ll: LinkedList<u32, Max, 0> = LinkedList::new();
assert!(ll.is_empty()); assert!(ll.is_empty());
assert!(ll.is_full()); assert!(ll.is_full());
@ -572,7 +557,7 @@ mod tests {
#[test] #[test]
fn test_rejected_push() { fn test_rejected_push() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
ll.push(1).unwrap(); ll.push(1).unwrap();
ll.push(2).unwrap(); ll.push(2).unwrap();
ll.push(3).unwrap(); ll.push(3).unwrap();
@ -585,7 +570,7 @@ mod tests {
#[test] #[test]
fn test_updating() { fn test_updating() {
let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); let mut ll: LinkedList<u32, Max, 3> = LinkedList::new();
ll.push(1).unwrap(); ll.push(1).unwrap();
ll.push(2).unwrap(); ll.push(2).unwrap();
ll.push(3).unwrap(); ll.push(3).unwrap();

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
linked_list::{ArrayLength, LinkedList, Min, Node}, linked_list::{LinkedList, Min},
time::{Clock, Instant}, time::{Clock, Instant},
Monotonic, Monotonic,
}; };
@ -14,16 +14,14 @@ fn unwrapper<T, E>(val: Result<T, E>) -> T {
} }
} }
pub struct TimerQueue<Mono, Task, N>(pub LinkedList<NotReady<Mono, Task>, Min, N>) pub struct TimerQueue<Mono, Task, const N: usize>(pub LinkedList<NotReady<Mono, Task>, Min, N>)
where where
Mono: Monotonic, Mono: Monotonic,
N: ArrayLength<Node<NotReady<Mono, Task>>>,
Task: Copy; Task: Copy;
impl<Mono, Task, N> TimerQueue<Mono, Task, N> impl<Mono, Task, const N: usize> TimerQueue<Mono, Task, N>
where where
Mono: Monotonic, Mono: Monotonic,
N: ArrayLength<Node<NotReady<Mono, Task>>>,
Task: Copy, Task: Copy,
{ {
pub fn new() -> Self { pub fn new() -> Self {