99: impl Mutex on all shared resources r=japaric a=japaric

document how to write generic code that operates on resources

Co-authored-by: Jorge Aparicio <jorge@japaric.io>
This commit is contained in:
bors[bot] 2018-11-04 18:58:45 +00:00
commit 8a27efeaeb
28 changed files with 381 additions and 316 deletions

View file

@ -42,7 +42,7 @@ owned-singleton = "0.1.0"
[dev-dependencies]
alloc-singleton = "0.1.0"
cortex-m-semihosting = "0.3.1"
cortex-m-semihosting = "0.3.2"
lm3s6965 = "0.1.3"
panic-halt = "0.2.0"

View file

@ -1,5 +1,25 @@
# Tips & tricks
## Generics
Resources shared between two or more tasks implement the `Mutex` trait in *all*
contexts, even on those where a critical section is not required to access the
data. This lets you easily write generic code that operates on resources and can
be called from different tasks. Here's one such example:
``` rust
{{#include ../../../examples/generics.rs}}
```
``` console
$ cargo run --example generics
{{#include ../../../ci/expected/generics.run}}```
This also lets you change the static priorities of tasks without having to
rewrite code. If you consistently use `lock`s to access the data behind shared
resources then your code will continue to compile when you change the priority
of tasks.
## Running tasks from RAM
The main goal of moving the specification of RTFM applications to attributes in

6
ci/expected/generics.run Normal file
View file

@ -0,0 +1,6 @@
UART1(STATE = 0)
SHARED: 0 -> 1
UART0(STATE = 0)
SHARED: 1 -> 2
UART1(STATE = 1)
SHARED: 2 -> 4

View file

@ -57,6 +57,7 @@ main() {
not-send
not-sync
generics
ramfunc
)

View file

@ -7,26 +7,16 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
// NOTE: does NOT properly work on QEMU
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
fn init() {
println!("init(baseline = {:?})", start);
hprintln!("init(baseline = {:?})", start).unwrap();
// `foo` inherits the baseline of `init`: `Instant(0)`
spawn.foo().unwrap();
@ -36,7 +26,7 @@ const APP: () = {
fn foo() {
static mut ONCE: bool = true;
println!("foo(baseline = {:?})", scheduled);
hprintln!("foo(baseline = {:?})", scheduled).unwrap();
if *ONCE {
*ONCE = false;
@ -49,7 +39,7 @@ const APP: () = {
#[interrupt(spawn = [foo])]
fn UART0() {
println!("UART0(baseline = {:?})", start);
hprintln!("UART0(baseline = {:?})", start).unwrap();
// `foo` inherits the baseline of `UART0`: its `start` time
spawn.foo().unwrap();

View file

@ -7,23 +7,13 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
#[init]
fn init() {
rtfm::pend(Interrupt::UART0);
}
@ -40,12 +30,12 @@ const APP: () = {
#[task(capacity = 4)]
fn foo(x: u32) {
println!("foo({})", x);
hprintln!("foo({})", x).unwrap();
}
#[task]
fn bar() {
println!("bar");
hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}

62
examples/generics.rs Normal file
View file

@ -0,0 +1,62 @@
//! examples/generics.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::{app, Mutex};
#[app(device = lm3s6965)]
const APP: () = {
static mut SHARED: u32 = 0;
#[init]
fn init() {
rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1);
}
#[interrupt(resources = [SHARED])]
fn UART0() {
static mut STATE: u32 = 0;
hprintln!("UART0(STATE = {})", *STATE).unwrap();
advance(STATE, resources.SHARED);
rtfm::pend(Interrupt::UART1);
debug::exit(debug::EXIT_SUCCESS);
}
#[interrupt(priority = 2, resources = [SHARED])]
fn UART1() {
static mut STATE: u32 = 0;
hprintln!("UART1(STATE = {})", *STATE).unwrap();
// just to show that `SHARED` can be accessed directly and ..
*resources.SHARED += 0;
// .. also through a (no-op) `lock`
resources.SHARED.lock(|shared| *shared += 0);
advance(STATE, resources.SHARED);
}
};
fn advance(state: &mut u32, mut shared: impl Mutex<T = u32>) {
*state += 1;
let (old, new) = shared.lock(|shared| {
let old = *shared;
*shared += *state;
(old, *shared)
});
hprintln!("SHARED: {} -> {}", old, new).unwrap();
}

View file

@ -7,24 +7,14 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init() {
println!("init");
hprintln!("init").unwrap();
}
#[idle]
@ -34,7 +24,7 @@ const APP: () = {
// Safe access to local `static mut` variable
let _x: &'static mut u32 = X;
println!("idle");
hprintln!("idle").unwrap();
debug::exit(debug::EXIT_SUCCESS);

View file

@ -7,21 +7,9 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
// NOTE: This convenience macro will appear in all the other examples and
// will always look the same
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init]
@ -37,7 +25,7 @@ const APP: () = {
// Safe access to local `static mut` variable
let _x: &'static mut u32 = X;
println!("init");
hprintln!("init").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}

View file

@ -7,20 +7,10 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init]
@ -29,14 +19,14 @@ const APP: () = {
// `init` returns because interrupts are disabled
rtfm::pend(Interrupt::UART0);
println!("init");
hprintln!("init").unwrap();
}
#[idle]
fn idle() -> ! {
// interrupts are enabled again; the `UART0` handler runs at this point
println!("idle");
hprintln!("idle").unwrap();
rtfm::pend(Interrupt::UART0);
@ -52,10 +42,11 @@ const APP: () = {
// Safe access to local `static mut` variable
*TIMES += 1;
println!(
hprintln!(
"UART0 called {} time{}",
*TIMES,
if *TIMES > 1 { "s" } else { "" }
);
)
.unwrap();
}
};

View file

@ -7,7 +7,7 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use heapless::{
consts::*,
spsc::{Consumer, Producer, Queue},
@ -15,16 +15,6 @@ use heapless::{
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
// Late resources
@ -49,7 +39,7 @@ const APP: () = {
fn idle() -> ! {
loop {
if let Some(byte) = resources.C.dequeue() {
println!("received message: {}", byte);
hprintln!("received message: {}", byte).unwrap();
debug::exit(debug::EXIT_SUCCESS);
} else {

View file

@ -7,20 +7,10 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
static mut SHARED: u32 = 0;
@ -33,7 +23,7 @@ const APP: () = {
// when omitted priority is assumed to be `1`
#[interrupt(resources = [SHARED])]
fn GPIOA() {
println!("A");
hprintln!("A").unwrap();
// the lower priority task requires a critical section to access the data
resources.SHARED.lock(|shared| {
@ -43,7 +33,7 @@ const APP: () = {
// GPIOB will *not* run right now due to the critical section
rtfm::pend(Interrupt::GPIOB);
println!("B - SHARED = {}", *shared);
hprintln!("B - SHARED = {}", *shared).unwrap();
// GPIOC does not contend for `SHARED` so it's allowed to run now
rtfm::pend(Interrupt::GPIOC);
@ -51,7 +41,7 @@ const APP: () = {
// critical section is over: GPIOB can now start
println!("E");
hprintln!("E").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
@ -61,11 +51,11 @@ const APP: () = {
// the higher priority task does *not* need a critical section
*resources.SHARED += 1;
println!("D - SHARED = {}", *resources.SHARED);
hprintln!("D - SHARED = {}", *resources.SHARED).unwrap();
}
#[interrupt(priority = 3)]
fn GPIOC() {
println!("C");
hprintln!("C").unwrap();
}
};

View file

@ -7,19 +7,9 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
@ -31,7 +21,7 @@ const APP: () = {
fn foo() {
static mut COUNT: u32 = 0;
println!("foo");
hprintln!("foo").unwrap();
spawn.bar(*COUNT).unwrap();
*COUNT += 1;
@ -39,14 +29,14 @@ const APP: () = {
#[task(spawn = [baz])]
fn bar(x: u32) {
println!("bar({})", x);
hprintln!("bar({})", x).unwrap();
spawn.baz(x + 1, x + 2).unwrap();
}
#[task(spawn = [foo])]
fn baz(x: u32, y: u32) {
println!("baz({}, {})", x, y);
hprintln!("baz({}, {})", x, y).unwrap();
if x + y > 4 {
debug::exit(debug::EXIT_SUCCESS);

View file

@ -7,18 +7,9 @@
extern crate panic_semihosting;
use cortex_m_semihosting::hprintln;
use rtfm::{app, Instant};
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
const PERIOD: u32 = 8_000_000;
// NOTE: does NOT work on QEMU!
@ -32,7 +23,7 @@ const APP: () = {
#[task(schedule = [foo])]
fn foo() {
let now = Instant::now();
println!("foo(scheduled = {:?}, now = {:?})", scheduled, now);
hprintln!("foo(scheduled = {:?}, now = {:?})", scheduled, now).unwrap();
schedule.foo(scheduled + PERIOD.cycles()).unwrap();
}

View file

@ -7,19 +7,9 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [bar])]
@ -30,7 +20,7 @@ const APP: () = {
#[inline(never)]
#[task]
fn foo() {
println!("foo");
hprintln!("foo").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}

View file

@ -7,20 +7,10 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
// A resource
@ -47,7 +37,7 @@ const APP: () = {
fn UART0() {
*resources.SHARED += 1;
println!("UART0: SHARED = {}", resources.SHARED);
hprintln!("UART0: SHARED = {}", resources.SHARED).unwrap();
}
// `SHARED` can be access from this context
@ -55,6 +45,6 @@ const APP: () = {
fn UART1() {
*resources.SHARED += 1;
println!("UART1: SHARED = {}", resources.SHARED);
hprintln!("UART1: SHARED = {}", resources.SHARED).unwrap();
}
};

View file

@ -7,18 +7,9 @@
extern crate panic_semihosting;
use cortex_m_semihosting::hprintln;
use rtfm::{app, Instant};
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
// NOTE: does NOT work on QEMU!
#[app(device = lm3s6965)]
const APP: () = {
@ -26,7 +17,7 @@ const APP: () = {
fn init() {
let now = Instant::now();
println!("init @ {:?}", now);
hprintln!("init @ {:?}", now).unwrap();
// Schedule `foo` to run 8e6 cycles (clock cycles) in the future
schedule.foo(now + 8_000_000.cycles()).unwrap();
@ -37,12 +28,12 @@ const APP: () = {
#[task]
fn foo() {
println!("foo @ {:?}", Instant::now());
hprintln!("foo @ {:?}", Instant::now()).unwrap();
}
#[task]
fn bar() {
println!("bar @ {:?}", Instant::now());
hprintln!("bar @ {:?}", Instant::now()).unwrap();
}
extern "C" {

View file

@ -8,20 +8,10 @@
extern crate panic_semihosting;
use alloc_singleton::stable::pool::{Box, Pool};
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[Singleton(Send)]
@ -48,7 +38,7 @@ const APP: () = {
#[task(resources = [P])]
fn foo(x: Box<M>) {
println!("foo({})", x);
hprintln!("foo({})", x).unwrap();
resources.P.lock(|p| p.dealloc(x));
@ -57,7 +47,7 @@ const APP: () = {
#[task(priority = 2, resources = [P])]
fn bar(x: Box<M>) {
println!("bar({})", x);
hprintln!("bar({})", x).unwrap();
resources.P.dealloc(x);
}

View file

@ -7,20 +7,10 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
static KEY: u32 = ();
@ -35,13 +25,13 @@ const APP: () = {
#[interrupt(resources = [KEY])]
fn UART0() {
println!("UART0(KEY = {:#x})", resources.KEY);
hprintln!("UART0(KEY = {:#x})", resources.KEY).unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
#[interrupt(priority = 2, resources = [KEY])]
fn UART1() {
println!("UART1(KEY = {:#x})", resources.KEY);
hprintln!("UART1(KEY = {:#x})", resources.KEY).unwrap();
}
};

View file

@ -7,19 +7,9 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
macro_rules! println {
($($tt:tt)*) => {
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
use core::fmt::Write;
writeln!(stdout, $($tt)*).ok();
}
};
}
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
@ -29,7 +19,7 @@ const APP: () = {
#[task(spawn = [bar, baz])]
fn foo() {
println!("foo");
hprintln!("foo").unwrap();
// spawns `bar` onto the task scheduler
// `foo` and `bar` have the same priority so `bar` will not run until
@ -43,14 +33,14 @@ const APP: () = {
#[task]
fn bar() {
println!("bar");
hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
#[task(priority = 2)]
fn baz() {
println!("baz");
hprintln!("baz").unwrap();
}
// Interrupt handlers used to dispatch software tasks

View file

@ -8,7 +8,7 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
use rtfm::{app, Instant};
use rtfm::{app, Exclusive, Instant};
#[app(device = lm3s6965)]
const APP: () = {
@ -43,6 +43,7 @@ const APP: () = {
#[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])]
fn foo() {
let _: Instant = scheduled;
let _: Exclusive<u32> = resources.SHARED;
let _: foo::Resources = resources;
let _: foo::Schedule = schedule;
let _: foo::Spawn = spawn;

View file

@ -30,13 +30,14 @@ pub struct Analysis {
pub enum Ownership {
// NOTE priorities and ceilings are "logical" (0 = lowest priority, 255 = highest priority)
Owned { priority: u8 },
CoOwned { priority: u8 },
Shared { ceiling: u8 },
}
impl Ownership {
pub fn needs_lock(&self, priority: u8) -> bool {
match *self {
Ownership::Owned { .. } => false,
Ownership::Owned { .. } | Ownership::CoOwned { .. } => false,
Ownership::Shared { ceiling } => {
debug_assert!(ceiling >= priority);
@ -44,6 +45,13 @@ impl Ownership {
}
}
}
pub fn is_owned(&self) -> bool {
match *self {
Ownership::Owned { .. } => true,
_ => false,
}
}
}
pub struct Dispatcher {
@ -72,18 +80,24 @@ pub fn app(app: &App) -> Analysis {
for (priority, res) in app.resource_accesses() {
if let Some(ownership) = ownerships.get_mut(res) {
match *ownership {
Ownership::Owned { priority: ceiling } | Ownership::Shared { ceiling } => {
if priority != ceiling {
*ownership = Ownership::Shared {
ceiling: cmp::max(ceiling, priority),
};
Ownership::Owned { priority: ceiling }
| Ownership::CoOwned { priority: ceiling }
| Ownership::Shared { ceiling }
if priority != ceiling =>
{
*ownership = Ownership::Shared {
ceiling: cmp::max(ceiling, priority),
};
let res = &app.resources[res];
if res.mutability.is_none() {
assert_sync.insert(res.ty.clone());
}
let res = &app.resources[res];
if res.mutability.is_none() {
assert_sync.insert(res.ty.clone());
}
}
Ownership::Owned { priority: ceiling } if ceiling == priority => {
*ownership = Ownership::CoOwned { priority };
}
_ => {}
}
continue;

View file

@ -676,6 +676,7 @@ fn prelude(
}
} else {
let ownership = &analysis.ownerships[name];
let mut exclusive = false;
if ownership.needs_lock(logical_prio) {
may_call_lock = true;
@ -710,28 +711,61 @@ fn prelude(
exprs.push(quote!(#name: <#name as owned_singleton::Singleton>::new()));
} else {
needs_unsafe = true;
defs.push(quote!(pub #name: &'a mut #name));
exprs.push(
quote!(#name: &mut <#name as owned_singleton::Singleton>::new()),
);
if ownership.is_owned() || mut_.is_none() {
defs.push(quote!(pub #name: &'a #mut_ #name));
let alias = mk_ident();
items.push(quote!(
let #mut_ #alias = unsafe {
<#name as owned_singleton::Singleton>::new()
};
));
exprs.push(quote!(#name: &#mut_ #alias));
} else {
may_call_lock = true;
defs.push(quote!(pub #name: rtfm::Exclusive<'a, #name>));
let alias = mk_ident();
items.push(quote!(
let #mut_ #alias = unsafe {
<#name as owned_singleton::Singleton>::new()
};
));
exprs.push(quote!(
#name: rtfm::Exclusive(&mut #alias)
));
}
}
continue;
} else {
defs.push(quote!(pub #name: &#lt #mut_ #ty));
if ownership.is_owned() || mut_.is_none() {
defs.push(quote!(pub #name: &#lt #mut_ #ty));
} else {
exclusive = true;
may_call_lock = true;
defs.push(quote!(pub #name: rtfm::Exclusive<#lt, #ty>));
}
}
}
let alias = &ctxt.statics[name];
needs_unsafe = true;
if initialized {
exprs.push(quote!(#name: &#mut_ #alias));
if exclusive {
exprs.push(quote!(#name: rtfm::Exclusive(&mut #alias)));
} else {
exprs.push(quote!(#name: &#mut_ #alias));
}
} else {
let method = if mut_.is_some() {
quote!(get_mut)
} else {
quote!(get_ref)
};
exprs.push(quote!(#name: #alias.#method() ));
if exclusive {
exprs.push(quote!(#name: rtfm::Exclusive(#alias.#method()) ));
} else {
exprs.push(quote!(#name: #alias.#method() ));
}
}
}
}
@ -1655,19 +1689,23 @@ fn mk_resource(
};
items.push(quote!(
unsafe impl<'a> rtfm::Mutex for #path<'a> {
const CEILING: u8 = #ceiling;
const NVIC_PRIO_BITS: u8 = #device::NVIC_PRIO_BITS;
type Data = #ty;
impl<'a> rtfm::Mutex for #path<'a> {
type T = #ty;
#[inline(always)]
unsafe fn priority(&self) -> &core::cell::Cell<u8> {
&self.#priority
}
#[inline(always)]
fn ptr(&self) -> *mut Self::Data {
unsafe { #ptr }
#[inline]
fn lock<R, F>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self::T) -> R,
{
unsafe {
rtfm::export::claim(
#ptr,
&self.#priority,
#ceiling,
#device::NVIC_PRIO_BITS,
f,
)
}
}
}
));

View file

@ -1,5 +1,8 @@
/// IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
use core::{hint, ptr};
//! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
#[cfg(not(debug_assertions))]
use core::hint;
use core::{cell::Cell, ptr, u8};
#[cfg(armv7m)]
use cortex_m::register::basepri;
@ -52,7 +55,13 @@ impl<T> MaybeUninit<T> {
if let Some(x) = self.value.as_ref() {
x
} else {
hint::unreachable_unchecked()
match () {
// Try to catch UB when compiling in release with debug assertions enabled
#[cfg(debug_assertions)]
() => unreachable!(),
#[cfg(not(debug_assertions))]
() => hint::unreachable_unchecked(),
}
}
}
@ -60,12 +69,19 @@ impl<T> MaybeUninit<T> {
if let Some(x) = self.value.as_mut() {
x
} else {
hint::unreachable_unchecked()
match () {
// Try to catch UB when compiling in release with debug assertions enabled
#[cfg(debug_assertions)]
() => unreachable!(),
#[cfg(not(debug_assertions))]
() => hint::unreachable_unchecked(),
}
}
}
pub fn set(&mut self, val: T) {
unsafe { ptr::write(&mut self.value, Some(val)) }
// NOTE(volatile) we have observed UB when this uses a plain `ptr::write`
unsafe { ptr::write_volatile(&mut self.value, Some(val)) }
}
}
@ -82,3 +98,66 @@ where
T: Sync,
{
}
#[cfg(armv7m)]
#[inline(always)]
pub unsafe fn claim<T, R, F>(
ptr: *mut T,
priority: &Cell<u8>,
ceiling: u8,
nvic_prio_bits: u8,
f: F,
) -> R
where
F: FnOnce(&mut T) -> R,
{
let current = priority.get();
if priority.get() < ceiling {
if ceiling == (1 << nvic_prio_bits) {
priority.set(u8::MAX);
let r = interrupt::free(|_| f(&mut *ptr));
priority.set(current);
r
} else {
priority.set(ceiling);
basepri::write(logical2hw(ceiling, nvic_prio_bits));
let r = f(&mut *ptr);
basepri::write(logical2hw(current, nvic_prio_bits));
priority.set(current);
r
}
} else {
f(&mut *ptr)
}
}
#[cfg(not(armv7m))]
#[inline(always)]
pub unsafe fn claim<T, R, F>(
ptr: *mut T,
priority: &Cell<u8>,
ceiling: u8,
_nvic_prio_bits: u8,
f: F,
) -> R
where
F: FnOnce(&mut T) -> R,
{
let current = priority.get();
if priority.get() < ceiling {
priority.set(u8::MAX);
let r = interrupt::free(|_| f(&mut *ptr));
priority.set(current);
r
} else {
f(&mut *ptr)
}
}
#[cfg(armv7m)]
#[inline]
fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
}

View file

@ -26,16 +26,14 @@
#![deny(warnings)]
#![no_std]
use core::{cell::Cell, u8};
#[cfg(feature = "timer-queue")]
use core::{cmp::Ordering, ops};
use core::cmp::Ordering;
use core::{fmt, ops};
#[cfg(not(feature = "timer-queue"))]
use cortex_m::peripheral::SYST;
#[cfg(armv7m)]
use cortex_m::register::basepri;
use cortex_m::{
interrupt::{self, Nr},
interrupt::Nr,
peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU},
};
pub use cortex_m_rtfm_macros::app;
@ -253,81 +251,76 @@ impl U32Ext for u32 {
/// [BASEPRI]) of the current context.
///
/// [BASEPRI]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100701/latest/special-purpose-mask-registers
pub unsafe trait Mutex {
/// IMPLEMENTATION DETAIL. DO NOT USE THIS CONSTANT
#[doc(hidden)]
const CEILING: u8;
/// IMPLEMENTATION DETAIL. DO NOT USE THIS CONSTANT
#[doc(hidden)]
const NVIC_PRIO_BITS: u8;
pub trait Mutex {
/// Data protected by the mutex
type Data: Send;
/// IMPLEMENTATION DETAIL. DO NOT USE THIS METHOD
#[doc(hidden)]
unsafe fn priority(&self) -> &Cell<u8>;
/// IMPLEMENTATION DETAIL. DO NOT USE THIS METHOD
#[doc(hidden)]
fn ptr(&self) -> *mut Self::Data;
type T;
/// Creates a critical section and grants temporary access to the protected data
#[inline(always)]
#[cfg(armv7m)]
fn lock<R, F>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self::Data) -> R,
{
unsafe {
let current = self.priority().get();
F: FnOnce(&mut Self::T) -> R;
}
if self.priority().get() < Self::CEILING {
if Self::CEILING == (1 << Self::NVIC_PRIO_BITS) {
self.priority().set(u8::MAX);
let r = interrupt::free(|_| f(&mut *self.ptr()));
self.priority().set(current);
r
} else {
self.priority().set(Self::CEILING);
basepri::write(logical2hw(Self::CEILING, Self::NVIC_PRIO_BITS));
let r = f(&mut *self.ptr());
basepri::write(logical2hw(current, Self::NVIC_PRIO_BITS));
self.priority().set(current);
r
}
} else {
f(&mut *self.ptr())
}
}
}
impl<'a, M> Mutex for &'a mut M
where
M: Mutex,
{
type T = M::T;
/// Creates a critical section and grants temporary access to the protected data
#[cfg(not(armv7m))]
fn lock<R, F>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self::Data) -> R,
F: FnOnce(&mut Self::T) -> R,
{
unsafe {
let current = self.priority().get();
if self.priority().get() < Self::CEILING {
self.priority().set(u8::MAX);
let r = interrupt::free(|_| f(&mut *self.ptr()));
self.priority().set(current);
r
} else {
f(&mut *self.ptr())
}
}
(**self).lock(f)
}
}
#[cfg(armv7m)]
#[inline]
fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
/// Newtype over `&'a mut T` that implements the `Mutex` trait
///
/// The `Mutex` implementation for this type is a no-op, no critical section is created
pub struct Exclusive<'a, T>(pub &'a mut T);
impl<'a, T> Mutex for Exclusive<'a, T> {
type T = T;
fn lock<R, F>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self::T) -> R,
{
f(self.0)
}
}
impl<'a, T> fmt::Debug for Exclusive<'a, T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt(f)
}
}
impl<'a, T> fmt::Display for Exclusive<'a, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt(f)
}
}
impl<'a, T> ops::Deref for Exclusive<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.0
}
}
impl<'a, T> ops::DerefMut for Exclusive<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.0
}
}
/// Sets the given `interrupt` as pending

View file

@ -91,7 +91,7 @@ where
#[inline(always)]
pub fn isr<TQ, T, N, F>(mut tq: TQ, mut f: F)
where
TQ: Mutex<Data = TimerQueue<T, N>>,
TQ: Mutex<T = TimerQueue<T, N>>,
T: Copy + Send,
N: ArrayLength<NotReady<T>>,
F: FnMut(T, u8),

View file

@ -8,7 +8,7 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
use rtfm::{app, Exclusive};
#[app(device = lm3s6965)]
const APP: () = {
@ -59,11 +59,11 @@ const APP: () = {
// owned by interrupt == `&mut`
let _: &mut u32 = resources.O3;
// no `Mutex` when access from highest priority task
let _: &mut u32 = resources.S1;
// no `Mutex` proxy when access from highest priority task
let _: Exclusive<u32> = resources.S1;
// no `Mutex` when co-owned by cooperative (same priority) tasks
let _: &mut u32 = resources.S2;
// no `Mutex` proxy when co-owned by cooperative (same priority) tasks
let _: Exclusive<u32> = resources.S2;
// `&` if read-only
let _: &u32 = resources.S3;
@ -74,7 +74,7 @@ const APP: () = {
// owned by interrupt == `&` if read-only
let _: &u32 = resources.O5;
// no `Mutex` when co-owned by cooperative (same priority) tasks
let _: &mut u32 = resources.S2;
// no `Mutex` proxy when co-owned by cooperative (same priority) tasks
let _: Exclusive<u32> = resources.S2;
}
};

View file

@ -7,7 +7,7 @@ extern crate owned_singleton;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
use rtfm::{app, Exclusive};
#[app(device = lm3s6965)]
const APP: () = {
@ -27,7 +27,7 @@ const APP: () = {
#[Singleton]
static mut S1: u32 = 0;
#[Singleton]
static mut S2: u32 = 0;
static S2: u32 = 0;
#[init(resources = [O1, O2, O3, O4, O5, O6, S1, S2])]
fn init() {
@ -55,13 +55,13 @@ const APP: () = {
let _: &mut O3 = resources.O3;
let _: &O6 = resources.O6;
let _: &mut S1 = resources.S1;
let _: Exclusive<S1> = resources.S1;
let _: &S2 = resources.S2;
}
#[interrupt(resources = [S1, S2])]
fn UART1() {
let _: &mut S1 = resources.S1;
let _: Exclusive<S1> = resources.S1;
let _: &S2 = resources.S2;
}
};