GithubHelp home page GithubHelp logo

rtic-rs / rtic Goto Github PK

View Code? Open in Web Editor NEW
1.6K 30.0 189.0 32.33 MB

Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers

Home Page: https://rtic.rs

License: Apache License 2.0

Rust 99.80% HTML 0.12% JavaScript 0.01% Shell 0.07%
rust no-std arm cortex-m microcontroller bare-metal scheduler concurrency

rtic's People

Contributors

afoht avatar andresv avatar bors[bot] avatar burrbull avatar curiouslycurious avatar datdenkikniet avatar dcarosone avatar dependabot[bot] avatar finomnis avatar glaeqen avatar homunkulus avatar japaric avatar jonas-schievink avatar jordens avatar jorgeig-space avatar korken89 avatar kraai avatar mareq avatar n8tlarsen avatar nickray avatar nilfit avatar onsdagens avatar perlindgren avatar ptaylor-us avatar regexident avatar romancardenas avatar rursprung avatar tmplt avatar wiktorwieclaw avatar yandrik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rtic's Issues

Proper solution for the use of embedded-nrf24l01 driver in RTFM application

Hi,

I have been experimenting with a simple RTFM application using embedded-nrf24l01 crate. That crate implements driver for nRF24L01 radio chip using some kind of 'mutate object using self move' semantics:

// create driver in 'standby' state and perform initial configuration
let mut s = NRF24L01::new(ce, cs, spi).unwrap();
s.set_frequency(10).unwrap();
...
// 'move' into Tx state to send packets
let t = s.tx().unwrap()
t.send(&[0x1, 0x2]).unwrap();
// 'move' back into standby state after transmission is acknowledged
let s = t.standby().unwrap()

I had some difficulties with keeping this kind of driver as a resource in RTFM application. So far I could make it work only using RefCell<Option<...>> aproach:

...

type Standby = ...;

app! {
    device: hal::stm32f103xx,

    resources: {
        static NRF: RefCell<Option<Standby>>;
        ...
    },
...
fn timer_handler(_t: &mut Threshold, mut r: TIM3::Resources) {
    let data: [u8; 10] = [0x31; 10];
   
    let s = match r.NRF.replace(None) {
        None => panic!("booo"),
        Some(s) => {
            let mut t = s.tx().unwrap();
            t.send(&data).unwrap();
            t.standby().unwrap()
        }
    };

    r.NRF.replace(Some(s));
}

Full source is available here. Is there another, more appropriate and elegant way to handle such a driver in RTFM applications ?

Regards,
Sergey

minimal example fails to build

I've copied

https://github.com/japaric/cortex-m-rtfm/blob/master/examples/zero-tasks.rs

into my own repo, and adapted it to use the stm32f4 crate. The resulting code is here:

https://github.com/timbod7/rust-stm32f4-examples/blob/master/minimal-rtfm/src/main.rs

this fails to build with a suprising (to me) error:

   Compiling minimal-rtfm v0.0.1 (file:///home/timd/personal/projects/stm-f4/rust-stm32f4-examples/minimal-rtfm)      
error: function is never used: `init`
  --> src/main.rs:28:1
   |
28 | fn init(p: init::Peripherals) {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: function is never used: `idle`
  --> src/main.rs:41:1
   |
41 | fn idle() -> ! {
   | ^^^^^^^^^^^^^^

I would have expected these functions to be referenced from the code generated by the app! macro.

What am I doing wrong?

[RFC] & / &mut access semantics for resources

Motivation

Currently when you assign a resource to a task the task gains exclusive access
to the resource: it can both claim and claim_mut the resource.

This RFC proposes extending the access model to let tasks declare whether they
need exclusive access (&mut-) or shared access (&-) to their resources. This
extra information would let the app! macro optimize access to resources
reducing the number of required critical sections, at least in some scenarios.

Design

We extend, in a backward compatible fashion, the syntax around the resources
array. Today the syntax looks like this: [A, B, C]; here the task has
exclusive access to the resources A, B and C. The syntax will be extended
to allow specifying shared or exclusive access through the & and &mut
operators. So, [&A, &mut B, &C] indicates that the task has shared access to
the resources A and C, and exclusive access to the resource B.

Here's an example:

app! {
    resources: {
        static COUNTER: u64 = 0;
    },

    idle: {
        resources: [&mut COUNTER],
    },

    tasks: {
        EXTI0: {
            enabled: true,
            priority: 1,
            resources: [&COUNTER],
        },

        EXTI1: {
            enabled: true,
            priority: 2,
            resources: [&COUNTER],
        },
    },
}

In this case both tasks, EXTI0 and EXTI1, get lockless "read access" to
the COUNTER resource. In today's model exti0 would need a critical section
(claim) to read data.

task!(EXTI0, exti0);

fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
    let data: &u64 = r.COUNTER;
}

task!(EXTI1, exti1);

fn exti1(_t: &mut Threshold, r: EXTI1::Resources) {
    let data: &u64 = r.COUNTER;
}

Whereas idle, which has lower priority than both tasks, also gets lockless
"read access" to the resource but requires a critical section (claim_mut) to
mutate the data.

fn idle(t: &mut Threshold, r: idle::Resources) -> ! {
    loop {
        {
            let data: &u64 = r.COUNTER.deref();
        }

        r.COUNTER.claim_mut(t, |counter, _t| *counter += 1);

        {
            let data: &u64 = r.COUNTER.deref();
        }
    }
}

This new access model imposes a new constraint on the resource data: it must now
implement the Sync trait. This forbids interior mutability (e.g. Cell /
RefCell) which could lead to data races.

If you don't want the Sync constraint on your resource data you can continue
to use the old syntax with no access (&- or &mut-) information.

Possible applications

Multiple reader, single writer resource

This is the example shown in the example above. Only the writer needs to lock
the resource to modify it. The writer needs to run at the lowest priority,
though, or this pattern won't reduce the number of critical sections required.

Initialize once, then read-only

A resource shared by tasks where none needs exclusive access (&mut-) to it can
be "locklessly" shared (&-) by all them. IOW, the resource can only be "read"
by the tasks. The only place where one can mutate (&mut-) the resource is
in init.

app! {
    resources: {
        static FREQUENCY: u32 = 0;
    },

    idle: {
        resources: [&FREQUENCY],
    }

    tasks: {
        EXTI0: {
            enabled: true,
            priority: 1,
            resources: [&FREQUENCY],
        },
    },
}

fn init(_: init::Peripherals, r: init::Resources) {
    *r.FREQUENCY = 8_000_000;
}

fn idle(r: init::Resources) {
    let f: &u32 = r.FREQUENCY;
}

task!(EXTI0, exti0);

fn exti0(_: &mut Threshold, r: EXTI0::Resources) {
    let f: &u32 = r.FREQUENCY;
}

Something with atomics?

The previous examples assumed that tasks that have shared (&-) access to a
resource can't modify it. This is not always true; tasks can modify resources
through a shared (&-) reference under this new system if the mutation is
synchronized.

Atomic types are one of the few types that allow mutation through a shared
reference and implement the Sync trait. Given these properties you can
already use them safely in static variables without the help of the RTFM
system. However, combining atomic types with this new access model lets you
restrict which tasks have access to the atomic variables, which may be useful
to prevent logic bugs. With the current, exclusive access only, model you can't
do that because it requires you to lock the resource that contains the atomic
variable when you want to access it from the lower priority tasks.

cc @pftbest this may interest you since you are exploring atomics and lock-less queues.

use core::mem::MaybeUninit when stabilized

This would make all RTFM applications use less static memory (.bss / .data), reduce pre-initialization runtime and make them smaller (.text).

This would mainly involve replacing the MaybeUninit defined in rtfm::export with a re-export of core::mem::MaybeUninit and then removing all these runtime initializations.

port the app! macro to the latest syn crate

The latest version of the syn crate makes it easier to create parsers thanks to its parser
combinator (?) macros. It can also give better error (and warning) messages that have proper span
information. Let's port the app! macro to it to provide a better user experience.

Here's what needs to be done:

  • Pretty much rewrite the app! parser in the rtfm-syntax crate. You'll have to replace
    rtfm_syntax::App and friends with a new struct that implements the Synom trait. You can find
    the specification of the app! macro syntax in the crate documentation of the
    cortex-m-rtfm-macros crate.
  • Update the code that converts the AST into a syntax checked AST. Basically update the
    rtfm_syntax::check::app function. You should try to keep the structs in rtfm_syntax::check
    unchanged -- unless there's some improvement that requires changing them.

  • Update cortex-m-rtfm-macros to use the new version of rtfm-syntax. The changes should be
    minimal.

To get familiar with the parse combinator macros check out:

Be sure to enable the following features in your dependencies:

[dependencies.syn]
version = "0.12.12"
features = ["full"]

[dependencies.proc-macro2]
features = ["nightly"]
version = "0.2.2"

If you want to give this a stab leave a comment. If no one volunteers I'll probably get to this
before the week ends.

[RFC] drop the threshold token from tasks' signature

Tasks always start with a threshold level that matches the priority level of the task. It's safe to add a method to the priority token to synthesize a threshold token (at the level) from it:

impl Priority<N> {
    pub fn threshold(&self) -> Threshold<N> { .. }
}

then the threshold token could be dropped from tasks' signatures making them simpler:

fn t1(task: Exti0, ref prio: P1) {
    let thr = &prio.threshold();
    let r1 = R1.access(prio, thr);
}

Should we do this?

  • Does this change make things more complicated or simpler for newcomers?
  • This is a breaking change.
  • It's almost always the case that you'll have to call the threshold method in a task. Would that feel repetitive?
  • Should threshold be named differently? Perhaps base_threshold?

Switch to some Cortex-M0 device crate

Currently, the examples build against the stm32f103xx crate, which is for a family of Cortex-M3 microcontrollers. Cortex-M3 implements armv7, and due to recent changes in svd2rust such a crate can not be compiled for the thumbv6m-none-eabi target. While it's generally nonsensical to do that, it prevents us from building the examples in this repo for an armv6 target.

In #43, I've made the tests pass by replacing the linker for the thumbv6m-none-eabi target with true - a pretty horrible hack. The proper solution for this would be to switch to a device crate for some Cortex-M0 microcontroller, propably in the stm32f0 family.

NB: It makes even less sense to compile an M0 device crate for armv7, but it currently works.

Macros are not portable when supporting more than one board/cpu

I follow the copper book and your blog posts (great stuff!) to create a proof of concept for some STM boards we had lying around: https://github.com/hmvp/rust_poc

It seemed logical to make to modules within the poc library for board support. I used the peripherals! macro in both resulting in a conflict because both modules declared a GPIO peripheral with the same name.
I solved this by not using the macro:
https://github.com/hmvp/rust_poc/blob/master/src/stm32f3348_discovery/mod.rs

Uncommenting the macro stuff gives the error.

For the task! macro I did not even try to move the common stuff to the bsp modules but I suspect I will get similar issues.

NB: I am quite new to this so I might have missed an obvious way to to things that does not lead to issues..

NB2: the two binaries now are named the same as the board but ideally the nucleo one should be able to run on both boards (if the cpu specific stuff was moved to the bsp module)

quickstart/link other required reading

The original issue was about some build errors. I now realize that I missed some important pieces of documentation. My entry point into this was the cortex-m-rtfm package, whose documentation doesn't reference some other important parts. Maybe a cortex-m-rtfm-quickstart, or just a link or two would help others out are not familiar your related work.

Hard fault in code generated by app! macro for debug build

Trying to use rtfm with a blue pill board. I am having trouble with tasks. Adding a task causes a hard fault for debug builds. Release builds work. Debug builds work if opt-level is changed not to be 0.

Normal code (https://gist.github.com/nallar/49a1be1e1b6aa157dff7703b7ed61b19):

app! {
    device: device,

    tasks: {
        CAN1_RX0: {
            path: usb_interrupt,

            resources: [USB]
        }
    }
}

Expanded code:

#[allow(unsafe_code)]
fn main() {
    let init: fn(device::Peripherals) = init;
    rtfm::atomic(unsafe { &mut rtfm::Threshold::new(0) },
                 |_t|
                     unsafe {
                         let _late_resources =
                             init(device::Peripherals::all());
                         let nvic = &*device::NVIC.get();
                         let prio_bits = device::NVIC_PRIO_BITS;
                         let hw = ((1 << prio_bits) - 1u8) << (8 - prio_bits);
                         nvic.set_priority(device::Interrupt::CAN1_RX0, hw);
                         nvic.enable(device::Interrupt::CAN1_RX0);
                     });
    let idle: fn() -> ! = idle;
    idle();
}

The hard fault happens on let hw = ((1 << prio_bits) - 1u8) << (8 - prio_bits);. Removing that line and hardcoding a priority of 1 prevents the hardfault. In particular, (1 << prio_bits) - 1u8 seems to cause it.

Program received signal SIGTRAP, Trace/breakpoint trap.
cortex_m_rt::default_handler::hb779949c33da971d (ef=0x20004df0)
    at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rt-0.3.8\src\lib.rs:459
459         asm::bkpt();
(gdb) bt full
#0  cortex_m_rt::default_handler::hb779949c33da971d (ef=0x20004df0)
    at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rt-0.3.8\src\lib.rs:459
No locals.
#1  <signal handler called>
No symbol table info available.
#2  0x0800025a in blue_pill_usb_keyboard::main::_$u7b$$u7b$closure$u7d$$u7d$::h6df592e3e3c4b436 (_t=0x20004f77) at src\main.rs:110
        init = 0x8000301 <blue_pill_usb_keyboard::init::h7fbaa15cdf06d57f>
#3  0x0800577e in cortex_m_rtfm::atomic::h04d4c70a3b2d7412 (t=0x20004f87,
    f=...)
    at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rtfm-0.2.2\src/lib.rs:110
No locals.
#4  0x080001ea in blue_pill_usb_keyboard::main::h70c85532f64f8efa ()
    at src\main.rs:102
        init = 0x8000301 <blue_pill_usb_keyboard::init::h7fbaa15cdf06d57f>
#5  0x08005730 in cortex_m_rt::lang_items::start::hb2ca1dee2fb9b063 (
    main=0x80001c3 <blue_pill_usb_keyboard::main::h70c85532f64f8efa>,
    _argc=0, _argv=0x0)
    at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rt-0.3.8\src/lang_items.rs:31
No locals.
#6  0x08001286 in main ()
        cortex_m_rt::RESET_VECTOR::haccb3bf5c3f20a4b = 0x8000131 <cortex_m_rt::reset_handler::h4aae2e8c4d94cc8b>
        EXCEPTIONS = {{RUST$ENCODED$ENUM$0$None = {
              __0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
            RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}}
(gdb) x 0xe000ed04
0xe000ed04:     0x00000803
        (icsr register, least significant byte = 0x03 = hard fault?)
(gdb) p ef
$3 = (struct ExceptionFrame *) 0x20004df0
(gdb) x/32
0x20004df0:     0xffffffff      0x00000010      0x00000001      0x08006200
0x20004e00:     0x08006200      0x08000235      0x0800025a      0x61000000
0x20004e10:     0x40006400      0x40007400      0xe0042000      0x40004c00
0x20004e20:     0x00000010      0x20004e44      0x20004f80      0x20004f77
0x20004e30:     0x08000301      0x20004f80      0x20004e34      0x20004f77
0x20004e40:     0x00000021      0xe000ed00      0xe000edf0      0xe0001000
0x20004e50:     0xe0002000      0xe000ef30      0xe0000000      0xe000ed90
0x20004e60:     0xe000e100      0xe000ed04      0xe000e010      0xe0040000
(gdb)

The backtrace shows it's faulting on this instruction:

.text:0800025a 50 fa 81 f0 uxtab r0, r0, r1

I don't have enough experience to debug further.

rtfm based stm32f030 debug build causes relcation truncated error

here is an example project:
https://gitlab.com/xnor/stm32f0308-disco-rust

If I build it without --release I get

target/thumbv6m-none-eabi/debug   /deps/libstm32f030-5466fdead1a18a6d.rlib(stm32f030-5466fdead1a18a6d.0.o): In function `WWDG':
      stm32f030.cgu-0.rs:(.text+0x0): relocation truncated to fit: R_ARM_THM_JUMP11 against symbol `DEFAULT_HANDLER' defined in .text.DEFAULT_HANDLER section in /home/alex/projects/modular/threshpan/target/thumbv6m-none-eabi/debug/deps/libcortex_m_rt-881d17200def560b.rlib(cortex_m_rt-881d17200def560b.0.o)

work around Cortex-M7 BASEPRI erratum

See erratum 837070 in "ARM Processor Cortex-M7 (AT610) and Cortex-M7 with FPU (AT611), Product revision r0, Sofware Developers Errata Notice".

Basically the problem is that msr BASEPRI won't immediately take effect and the instruction following it won't have the new / desired dynamic priority and thus it can be preempted as if BASEPRI hadn't been modified.

The suggested workaround is to temporarily disable interrupts when msr BASEPRI is issued, so basically something like this:

cpsid i
msr BASEPRI
cpsie i

We can directly apply this fix in claim since claim only modifies BASEPRI when interrupts are enabled. (If interrupts are disabled, claim is a no-operation)

However, It would be ideal if we only applied this fix when targeting Cortex-M7 devices. And I think we can do that but we'll need some help from svd2rust. SVD files contains these <cpu> and <revision> fields that would let us identify chips with the problem. Device crates could expose CPU and REVISION constants that claim could check to decide whether to apply the workaround or not.

Thanks @pftbest for reporting this.
cc @perlindgren

Panic handler?

I tried latest hello.rs from blue-pill . However got build error "error: language item required, but not found: panic_fmt". Fixed the app by adding panic handler related code as per "cortex-m-quickstart" examples/panic.rs. However earlier version of cortex-m-rtfm didn't require such modification. Looked into the code of cortext-m-rtfm. I found that panic handler was part of cortex-m-rtfm earlier version. However there is no panic handler in the latest cortex-m-rtfm.

So am my missing any thing? or should panic handler be part of latest cortex-m-rtfm?

[RFC] The `Static` wrapper is dead, long live safe `&'static mut` references!

History: The Static wrapper

If you have been using RTFM claims you probably have noticed this "pattern":

r.FOO.claim_mut(|foo| {
    **foo += 1;
});

Here you need a double dereference because claim returns a &mut Static<T>, instead of a plain
mutable reference (&mut T). Static<T> is a newtype over T that Derefs to T. You normally
won't notice the Static wrapper when using methods because of the Deref implementation, but the
wrapper becomes apparent when you need to assign some new value to a resource.

DMA transfers

So, why is Static being used here? The main reason is that I needed some (zero cost) abstraction
to make DMA transfers memory safe. You can't build a safe DMA API on top of plain (non-static)
references. See below:

impl Serial {
    fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> Transfer<'a> { .. }
}

impl<'a> Transfer<'a> {
    fn wait(self) {
        drop(self)
    }
}

impl<'a> Drop for Transfer<'a> {
    fn drop(&mut self) {
        // waits until the DMA transfer finishes
    }
}

let mut on_the_stack = [0; 16];
{
    let transfer = serial.read_exact(&mut on_the_stack);

    // meanwhile, do some other stuff

    // on_the_stack[0] = 1;
    //~^ error `on_the_stack`

    transfer.wait();
}

// use `on_the_stack`

At first glance, this looks like an OK DMA API. While the DMA transfer is writing to the buffer you
can't access the buffer (on_the_stack is "frozen" by the outstanding borrow). The Transfer value
represents the on-going transfer and upon destruction (when dropped) it waits for the transfer to
finish. You can use the wait method to make the wait operation more explicit.

However, this API is not safe because you can safely mem::forget the Transfer value to gain
access to the buffer while the transfer is on-going:

let mut on_the_stack = [0; 16];
{
    let transfer = serial.read_exact(&mut on_the_stack);

    // destructor not run
    mem::forget(transfer);
}

// the transfer may not be over at this point
on_the_stack[0] = 1;
assert_eq!(on_the_stack[0], 1);

This doesn't look too dangerous but it's a violation of Rust aliasing model and will result in UB.
In the last line two mutable references to on_the_stack exist: one is being used in the indexing
operation, and the other is owned by the DMA (external hardware).

It gets much worse though because this mem::forget hole can be used to corrupt stack memory:

fn foo() {
    let mut on_the_stack = [0; 16];

    mem::forget(serial.read_exact(&mut on_the_stack));
}

fn bar() {
    // do stuff while the DMA transfer is on going
}

foo();
bar();

Here foo starts a DMA transfer that modifies some stack allocation but then immediately returns,
releasing the stack allocation. Next bar starts while the DMA is still on going; the problem is
that the DMA transfer will write into the stack potentially overwriting bar's local variables and
causing undefined behavior.

How does Static help?

We can prevent the memory corruption by having the API only accept references that point into memory
that will never be de-allocated. And that's what the Static wrapper represents: &Static<T> is a
reference into a statically allocated (i.e. stored in a static variable) value of type T. With
this change the API would look like this:

impl Serial {
    fn read_all<'a>(&'a mut self, buf: &'a mut Static<[u8]>) -> Transfer<'a> { .. }
}

(Note that this change is not enough to prevent the aliasing problem caused by mem::forget.
Discussing a solution for that issue is out of scope for this RFC though. The RefCell-like
Buffer abstraction in the blue-pill crate prevents the mem::forget aliasing problem showcased
above but it still has other issues like mem::swap aliasing and that you can e.g. still use
Serial while the transfer is in progress)

A value can't be safely wrapped in Static but RTFM does that for you in every claim and that
lets you use the memory safer DMA API from above.

Changing buf's type to &'static mut would also have worked but there's no way to safely create
&'static mut references, or rather there wasn't a way before this RFC.

Motivation

Being able to safely create &'static mut references.

Why? &'static mut references have interesting properties that I think will enable the creation of
novel APIs:

&'static mut T has move semantics. See below:

fn reborrow<'a, T>(x: &'a mut T) { .. }

fn consume<T>(x: &'static mut T) { .. }

fn foo<T>(x: &'static mut T) {
    reborrow(x);

    // OK to call again
    reborrow(x);

    // actually the compiler is doing this in each `reborrow` call
    reborrow(&mut *x);

    // this is different: this takes ownership of `x`
    consume(x);

    // now you can't use `x` anymore
    //consume(x);
    //~^ error `x` has been already moved

    //reborrow(x);
    //~^ error `x` has been already moved
}

&'static mut T has 'static lifetime (gasp!) so, unlike its non-static cousin, it can be stored
in a static variable and thus we can have a resource that protects a &'static mut T.

&'static mut T is pointer sized. If you need to send (transfer ownership) of a buffer from one
task (execution context) to another then it's cheaper to send &'static mut [u8; 256] than to send
[u8; 256] even though they are both semantically a move.

So &'static mut T is a bit like Box<T>: both have move semantics and are
pointer sized but the former doesn't need dynamic memory allocation (it's statically allocated!) and
we know that T's destructor will never run ('static lifetime).

Use case: memory safe DMA transfer

We can revise the DMA API to make it truly memory safe:

impl Serial {
    fn read_exact(self, buf: &'static mut [u8]) -> Transfer { .. }
}

impl Transfer {
    fn wait(self) -> (Serial, &'static mut [u8]) { .. }
}

let buf: &'static mut [u8] = /* created somehow */;

let transfer = serial.read_exact(&mut on_the_stack);

// can't use Serial while the DMA transfer is in progress
// let byte = serial.read();
//~^ error `serial` has been moved

// can't access `buf` while the transfer is in progress
// buf[0] = 1;
//~^ error `buf` has been moved

// meanwhile, do other stuff

// block until the transfer finishes
let (serial, buf) = transfer.wait();

// now you can use `serial` and access the `buf`fer again

Here if you mem::forget the transfer then you can't never access serial or the buffer again,
which may seem overkill but fulfills the memory safety requirement.

Use case: SPSC ring buffer

This use case prompted the original RFC (cf. #47). Basically a ring buffer queue can be split into
one producer end point and one consumer end point. Each end point can locklessly enqueue or dequeue
items into / from the same queue -- even if the end points are in different execution contexts (e.g.
threads or interrupts).

The API for this already exists in the heapless crate but the Consumer and Producer end
points have a lifetime parameter that matches the lifetime of the ring buffer queue. To put these
end points in resources the lifetime would have to be 'static and that requires a &'static mut RingBuffer, which can't be safely created without this RFC.

Detailed design

init.resources

We add a resources field to app.init. The value of this field is a list of resources, previously
declared in app.resources, that init will own for the rest of the program. The resources in
this list will appear under the init::Resources struct as &'static mut references. Example:

app! {
    device: stm32f103xx,

    resources: {
        static BUFFER: [u8; 16] = [0; 16];
        static SHARED: bool = false;
    },

    init: {
        // NEW!
        resources: [BUFFER],
    },

    idle: {
        resources: [SHARED],
    },

    tasks: {
        EXTI0: {
            path: exti0,
            resources: [SHARED],
        },
    }
}

fn init(p: init::Peripherals, r: init::Resources) {
    // static lifetime
    let buf: &'static mut [u8; 16] = r.BUFFER;

    // non-static lifetime
    let shared: &mut bool = r.SHARED;
}

// ..

Some constraints apply to init owned resources:

  • These resources must have an initial value; i.e. they can't be "late" resources.

  • Resources assigned to init can't appear in idle.resources or in tasks.$T.resources.
    Basically, the resources are owned by init so they can't be shared with other tasks, or with
    idle.

These constraints will be enforced by the app! macro. An error will be thrown before expansion if
any constraint is violated.

Drop the Static wrapper

Since this RFC brings proper support for &'static mut references to the table I think the Static
wrapper is no longer useful -- memory safe DMA APIs can be built without it and that was its main
reason for existing.

This will be implementing by changing all &[mut] Static<T> to &[mut] T. This means you will no
longer need to doubly dereference to assign a new value to a resource.

Downsides

This is a breaking change, but we are breaking things due to #50 so it's not much of a problem.

Alternatives

A pre_init function

A pre_init function with signature fn() -> T could be run before init. The value returned by
this function would be passed as &'static mut T to init. Unlike the main proposal this value
would be created at runtime so const eval limitations would not apply; also the value would be
allocated in the stack (in the first frame, which will never be deallocated), not in .bss /
.data.

With code it would look like this:

app! {
    device: stm32f103xx,

    pre_init: pre_init,
}

struct Root {
    buffer: [u8; 16],
}

fn pre_init() -> Root {
    Root { buffer: [0; 16] }
}

fn init(root: &'static mut Root, p: init::Peripherals, r: init::Resources) { .. }

I think it may make sense to also support this because it potentially lets you use a different
memory region -- think of the case of microcontrollers with two RAM regions the stack could be on
one region and .bss / .data could be on the other. However, if we get better support for multiple
memory regions in cortex-m-rt and support for placing resources in custom linker sections in
cortex-m-rtfm then there is no longer a need for this, I think, because then you can place an
init owned resource in any memory region (in RAM, e.g. .bss1, or in core-coupled RAM, .bss2).

I'm not too concerned about the const eval limitation that affects the main proposal because, in my
limited experience, the T in the &'static mut T references one creates is usually an array ([T; N]) or a thin abstraction over uninitialized arrays (e.g. heapless::RingBuffer).

Implementation

See #58

cc @pftbest @jonas-schievink @hannobraun

Provide reset vector

A standard trick to avoid deinitialization of controller's peripherals one by one before jump to bootloader is to set a cookie somewhere in RAM unaffected by reset, perform reset of the chip and check that cookie in reset vector jumping to bootloader if set. Right now quite a lot is happening before call to init, so it's possible for that code to interfere with bootloader call. It would be nice to have an ability to run user code just before anything else for such use case.

Another approach would be adding RCC deinitialization and peripheral reset functions to HAL.

See also: japaric/stm32f30x-hal#24

Add support for exceptions

I don't know how much value would be to support all possible exceptions (as tasks), but at least sys_tick should be supported, I think.

How to claim resources without a token?

I am trying to use the clean architecture for my device - i.e. the main application logic is not dependent on the hardware, but is written with a generic output trait that can be instantiated with different types.

pub trait Output {
    fn write(Message);
}
pub struct App<O: Output> {
    out: O,
    โ€ฆ
}
impl<O: Output> App<O> {
    pub fn new(out: O) -> Self {
        Self { out, โ€ฆ }
    }
    pub fn process(&mut self, msg: Message) {
        โ€ฆ // heavy lifting
        self.out.write(โ€ฆ);
        โ€ฆ
    }
}

For testing, I can instantiate the Output with a growing vector where I can inspect the results after processing various example inputs.
In the application, I can instantiate the Output with a producer that writes into a buffer, getting consumed from the serial port interrupt:

// pseudo code :-)
use my_library::App;
use buffer::{Buffer, BufferConsumer, BufferProducer};
use hal::Uart;

struct SerialApp {
    input: BufferConsumer,
    app: App<SerialBufferProducer>
}
struct SerialBufferProducer(BufferProducer);
rtfm::app! {
    resources: {
        static app: SerialApp;
        static serial_rx: BufferProducer;
        static serial_tx: BufferConsumer;
        static uart: Uart;
    },
    idle: {
        resources: [app],
    },
    tasks: {
        USART1: {
            path: rxtx,
            resources: [uart, serial_rx, serial_tx],
            enabled: true,
        }
    },
}
fn init(mut p: init::Peripherals) -> init::LateResources {
    static mut rx_buffer: Buffer = Buffer::empty();
    static mut tx_buffer: Buffer = Buffer::empty();
    let (rx_consumer, rx_producer) = unsafe { rx_buffer.split() };
    let (tx_consumer, tx_producer) = unsafe { tx_buffer.split() };
    init::LateResources {
        app: SerialApp::new(rx_consumer, SerialBufferProducer(tx_producer)),
        serial_rx: rx_producer,
        serial_tx: tx_consumer,
        uart: Uart::init(p.USART1),
    }
}
fn idle(t: &mut Threshold, mut r: idle::Resources) -> ! {
    loop {
        if let Some(msg) = r.serial_app.input.read() {
            r.serial_app.app.process(msg);
        }
    }
}
pub fn rxtx(_: &mut ::Threshold, mut r: ::USART1::Resources) {
    if let Some(byte) = r.uart.received() {
        r.serial_rx.write(byte);
    }
    if r.uart.can_send() {
        if let Some(byte) = r.serial_tx.read() {
            r.uart.send(byte);
        } else {
            r.uart.disable_tx_event();
        }
    }
}

Here's the catch: the SerialBufferProducer wrapper that implements the app Output is supposed to enable the uart tx event after writing to the buffer, and it would need to claim the uart resource for that!

Let's assume that

  • I want to use the app in other tasks as well, so I can't just instantiate it within idle and use its token and uart resource in the construction of the SerialBufferProducer.
  • there is no other way to guarantee atomical access, like splitting the uart resource or using bit banding - I really need to prevent other uart-using interrupts

I think I need a way to access a uart resource in the init function so that I can put it into the SerialBufferProducer, and then a way to claim it without being passed a token. The clean architecture prevents me from passing the token that is accessible at the call site of process through to the write call. What do you think?

Build issues with STM32F042 target

Hi,

I'm new to rust and the embedded rust eco-system, so forgive me if I've missed a simple initial step which would resolve my issues. I've uploaded a single task RTFM application here:
https://github.com/osterwood/rtfm-app
It should blink an LED on a STM32F042 board I have. It's based on the rtfm-one.rsexample in this repository.

I've run into a compile issue that I don't know how to resolve.

error[E0432]: unresolved import `_initResources`                                                                                                             
  --> examples/rtfm-one.rs:28:1                                                                                                                              
   |                                                                                                                                                         
28 | / app! {                                                                                                                                                
29 | |     device: stm32f0::stm32f0x2,                                                                                                                       
30 | |                                                                                                                                                       
31 | |     // Here data resources are declared                                                                                                               
...  |                                                                                                                                                       
57 | |     }                                                                                                                                                 
58 | | }                                                                                                                                                     
   | |_^ no `_initResources` external crate                                                                                                                  
                                                                                                                                                             
error[E0433]: failed to resolve. Could not find `init` in `{{root}}`                                                                                         
  --> examples/rtfm-one.rs:28:1                                                                                                                              
   |                                                                                                                                                         
28 | / app! {                                                                                                                                                
29 | |     device: stm32f0::stm32f0x2,                                                                                                                       
30 | |                                                                                                                                                       
31 | |     // Here data resources are declared                                                                                                               
...  |                                                                                                                                                       
57 | |     }                                                                                                                                                 
58 | | }                                                                                                                                                     
   | |_^ Could not find `init` in `{{root}}`                                                                                                                 
                                                                                                                                                             
error[E0425]: cannot find value `ON` in the crate root                                                                                                       
  --> examples/rtfm-one.rs:28:1                                                                                                                              
   |                                                                                                                                                         
28 | / app! {                                                                                                                                                
29 | |     device: stm32f0::stm32f0x2,                                                                                                                       
30 | |                                                                                                                                                       
31 | |     // Here data resources are declared                                                                                                               
...  |                                                                                                                                                       
57 | |     }                                                                                                                                                 
58 | | }                                                                                                                                                     
   | |_^ not found in the crate root                                                                                                                         
                                                                                                                                                             
error: unused import: `stm32f0::stm32f0x2`                                                                                                                   
  --> examples/rtfm-one.rs:25:5                                                                                                                              
   |                                                                                                                                                         
25 | use stm32f0::stm32f0x2;                                                                                                                                 
   |     ^^^^^^^^^^^^^^^^^^                                                                                                                                  
   |                                                                                                                                                         
note: lint level defined here                                                                                                                                
  --> examples/rtfm-one.rs:3:9                                                                                                                               
   |                                                                                                                                                         
3  | #![deny(warnings)]                                                                                                                                      
   |         ^^^^^^^^                                                                                                                                        
   = note: #[deny(unused_imports)] implied by #[deny(warnings)]                                                                                              
                                                                                                                                                             
error[E0308]: mismatched types                                                                                                                               
  --> examples/rtfm-one.rs:28:1                                                                                                                              
   |                                                                                                                                                         
28 | / app! {                                                                                                                                                
29 | |     device: stm32f0::stm32f0x2,                                                                                                                       
30 | |                                                                                                                                                       
31 | |     // Here data resources are declared                                                                                                               
...  |                                                                                                                                                       
57 | |     }                                                                                                                                                 
58 | | }                                                                                                                                                     
   | |_^ expected u32, found u8                                                                                                                              
help: you can cast an `u8` to `u32`, which will zero-extend the source value                                                                                 
   |                                                                                                                                                         
28 | app! {                                                                                                                                                  
29 |     device: stm32f0::stm32f0x2,                                                                                                                         
30 |                                                                                                                                                         
31 |     // Here data resources are declared                                                                                                                 
32 |     //                                                                                                                                                  
33 |     // Data resources are static variables that are safe to share across tasks                                                                          
 ...                                                                                                                                                         
                                                                                                                                                             
error: aborting due to 5 previous errors                                                                                                                                                          

It seems there are multiple issues with the app! macro. Most of the examples I've found for RTFM are based on the stm32f103xx and stm32f103_hal crates, not stm32f0. Is there something different about the stm32f0 crate which breaks RTFM?

Thanks in advance for any ideas you have.

[RFC] Add a `Resource.access_mut` method

Problem

Currently, when you want to share some data structure that needs to be mutated through a mutable (&mut-) reference between tasks you have no option but to wrap it in a RefCell because the Resource.access method only returns shared (&-) references. This involves runtime overhead because the RefCell does runtime checks to ensure that accessing the inner data preserves Rust's borrowing semantics (one &mut- reference OR several &- references)

static R1: Resource<RefCell<Thing>, C1> = Resource::new(RefCell::new(Thing::new()));

fn t1(_: Exti0, ref prio: P1, ref thr: T1) {
    let r1 = R1.access(prio, thr);
    r1.borrow().foo(); // runtime check
    r1.borrow_mut().foo_mut(); // runtime check
}

Due to how RTFM works it will be always be the case that a resource that contains a RefCell will have no outstanding borrows when it's first accessed in a task. This means that provided that you don't break borrow semantics within a task then executing the task will never fail a runtime borrow check. The compiler doesn't know this though, and will still perform the borrow checks at runtime even if they are effectively infallible.

Workaround (?)

To remove the runtime checks the assume intrinsic can be used to tell the compiler about the "no outstanding borrow on first access" property:

static R1: Resource<RefCell<Thing>, C1> = Resource::new(RefCell::new(Thing::new()));

fn t1(_: Exti0, ref prio: P1, ref thr: T1) {
    let r1 = R1.access(prio, thr);
    unsafe { intrinsics::assume(r1.borrow_state() == BorrowState::Unused) }
    r1.borrow().foo(); // no runtime check
    r1.borrow_mut().foo_mut(); // no runtime check
}

With this information the compiler is able to optimize away the runtime checks. The downside is that this requires unsafe, borrow_state is a deprecated API and that this doesn't optimize away the RefCell reference counter from the Resource<RefCell<_>> memory representation.

Possible solution

A possible solution is to add an access_mut method to Resource that's similar to Local.borrow_mut and that has the following signature:

// trait bounds not shown for brevity
impl<T, RC> Resource<T, RC> {
    // it must hold that: TP <= RC <= PT
    fn access_mut(&'static self, priority: &mut TP, threshold: &PT) -> &mut T {
        // ..
    }
}

The problem here are the lifetime constraints. The borrow &mut T can't outlive the threshold token, or it would be possible for it to outlive a Threshold.raise critical section and that would lead to data races. Additionally, the borrow &mut T must also freeze the priority token; this is required to avoid mutable aliasing -- without this it would be possible to get two or more mutable references to the data protected by the Resource abstraction. Alternatively, this method could request a mutable reference to the task token to prevent aliasing but the priority token will still be required (as a shared reference) so the signature would require 3 tokens.

Whatever we end up doing these operations must result in compile errors:

{
    // reference to inner data escaped the critical section
    let bad_r1 = threshold.raise(&R1, |threshold| {
        R1.access_mut(..)
    });
}
let r1: &mut Foo = R1.access_mut(..);
let r1_alias: &mut Foo = R1.access_mut(..);

This solution suffers from the problem that it's overly restrictive when it comes to borrowing different resources. You can't take a mutable reference to resource A and within the same scope take a shared reference to resource B (where A != B). Local has the same problem so let's use it to illustrate the problem:

fn t1(mut task: Exti0, ref prio: P1, ref thr: T1) {
    static A: Local<(), Exti0> = Local::new(());
    static B: Local<(), Exti0> = Local::new(());

    {
        // This is perfectly legal but the API prevents it
        let a = A.borrow(&task);
        let b = B.borrow_mut(&mut task);
    }

    {
        // what the API really wants to prevent is this
        let a = A.borrow(&task);
        let a_alias = A.borrow_mut(&mut task);
    }
}

The workaround for this borrow checker problem is to minimize the borrows to free up the task token ASAP. So something like this:

fn t1(mut task: Exti0, ref prio: P1, ref thr: T1) {
    static A: Local<(), Exti0> = Local::new(());
    static B: Local<(), Exti0> = Local::new(());

    A.borrow(&task).foo();
    B.borrow_mut(&mut task).foo_mut();
}

But this very unergonomic and doesn't work if you have a function call that must be called with a shared reference to resource A and a mutable reference to resource B:

fn t1(mut task: Exti0, ref prio: P1, ref thr: T1) {
    static A: Local<(), Exti0> = Local::new(());
    static B: Local<(), Exti0> = Local::new(());

    bar(A.borrow(&task), B.borrow_mut(&mut task));
    //~^ error
}

Ideal solution

The ideal solution should allow mutably borrowing resources without requiring runtime checks and without preventing borrowing other resources. In my mind such solution would involve some sort of compile time task local borrow checker which I have no idea of how to implement but that would probably require making every single resource have a different type (static A: Resource<(), C1> and static B: Resource<(), C1> would need to have different types). I expect the implementation to be rather unergonomic as well, unless we implement it as a compiler plugin of sorts.

app! is broken

on my nightly I get:

cargo build --target thumbv7em-none-eabihf --examples
Compiling cortex-m-rtfm v0.3.2 (file:///Users/eugene.tolmachev/sources/cortex-m-rtfm)
error[E0658]: procedural macros cannot expand to modules (see issue #38356)
--> examples/safe-static-mut-ref.rs:12:1
|
12 | / app! {
13 | | device: stm32f103xx,
14 | |
15 | | resources: {
... |
21 | | },
22 | | }
| |_^
|
= help: add #![feature(proc_macro_gen)] to the crate attributes to enable

Anyone knows which toolchain/dependencies combo does work?

Don't initialize memory for late resources

Late resources are initialized at runtime so their memory doesn't need to be initialized before main / init. However, today, late resources are placed in the .data section so not only they get initialized (using memcpy) but they also use up FLASH memory (because everything in .data has some initial value stored in FLASH).

To fix this we'll have to create a new section, let's call it .uninit, in cortex-m-rt and then place the static variables that will be used with late resources in that section. The change in this repo is minimal; here's the patch (macros/src/trans.rs):

                 None => quote! {
                     // Resource initialized in `init`
-                    static mut #_name: #krate::UntaggedOption<#ty> = #krate::UntaggedOption { none: () };
+                    #[link_section = ".uninit"]
+                    static mut #_name: #krate::UntaggedOption<#ty> =
+                        #krate::UntaggedOption { none: () };
                 },

But this change requires updating cortex-m-rt, specially if rust-embedded/cortex-m-rt#43 lands. It's probably best to wait until rust-embedded/cortex-m-rt#43 lands.

`lock` probably needs some kind of barrier

Either a compiler, memory or an instruction one or a mixture of them. To prevent the closure code from being executed outside the modified BASEPRI context. This should become more apparent when dealing with non-volatile resources (i.e. not peripherals)

revisit type level integers

One of the ways how RTFMv2 became simpler than v1 was the removal of type level
integers along with the various associated tokens. However this simplification
comes at a price: heavy reliance on LLVM for proper optimization.

Right now, there's only one token that the user has to deal with: the preemption
Threshold token. This token is actually a newtype over u8 that tracks the
preemption threshold of the system through the whole application. This token is
used to unlock Resources through the borrow{,_mut} and claim{,_mut}
methods. All these methods have branches and assertions in them for memory
safety. In a correctly written and properly optimized program the assertions in
borrow{,_mut}, all but one branch in claim{,_mut} and all the Threshold
tokens should be optimized away. However this requires that LLVM knows the
exact value of the Threshold token in every single node of the function
call graph. In complex enough call graphs LLVM "gives up" and generates code to
track the value of Threshold at runtime; this destroy performance: panicking
branches as well as all the branches in a claim{,_mut} call are kept in. In
the worst case scenario this can triple the size of the output .text section.

The only way I can think of to fix the problems outline above is to turn
Threshold into a type level integer. This way the token is guaranteed to not
exist at runtime; the token would become a zero sized type. This change would
turn the panicking branch in borrow{,_mut} into a compile error and make it
very easy for LLVM to optimize away the branches in claim{,_mut} because the
"value" of Threshold would be "local" to a function rather than require
the close-to global program analysis that v2 requires.

In principle we can move the API to type level integers today (see appendix) by
using the typenum crate, but it seems that the implementation is blocked by
a rustc bug (cf. rust-lang/rust#43580).

Downsides

The main downside of implementing this change is that generic code that uses the
Resource trait becomes much more complicated to write and to read.

This is generic code that deals with two resources, today:

fn foo<A, B>(t: &mut Threshold, a: A, b: B)
where
    A: Resource<Data = u8>,
    B: Resource<Data = u8>,
{
    a.claim(t, |a, t| {
        b.claim(t, |b, t| {
            // ..
        });
    });
}

This is how that generic code would look like with the proposed typenum-based
API:

fn foo<A, B, CA, CB, THRESHOLD>(t: &mut T<THRESHOLD>, a: A, b: B)
where
    A: Resource<Data = u8, Ceiling = CA>,
    B: Resource<Data = u8, Ceiling = CB>,
    CA: Unsigned,
    CB: Unsigned,
    THRESHOLD: Max<CA> + Unsigned, // needed for the outer claim
    Maximum<THRESHOLD, CA>: Max<CB> + Unsigned, // needed for the inner claim
    Maximum<Maximum<THRESHOLD, CA>, CB>: Unsigned, // also for the inner claim
{
    a.claim(t, |a, t| {
        b.claim(t, |b, t| {
            // ..
        });
    });
}

Effectively every claim can add one bound to the where clause of the generic
function. The bounds give no extra information to the reader; they are just
there to please the compiler.

I think the only way to avoid this downside would be to build the API on top of
proper type level integers baked into the language (*). The problem is that they are
not implemented and that the API requires a type level cmp::max(A, B)
operator (**) -- it's unknown when / if that operator will be implemented.

EDIT1: (*) because, in theory, in that case no bound should be required. const N: u8 is known to be an integer and type level operations on it (where N1 > N2) would be baked into the language.

EDIT1: (**) A type level if a > b operator / clause is also required for borrow.

The other minor downside of the change is that the signature of task functions
would have to include the exact threshold level. Like this:

// priority = 3
fn exti0(t: &mut T3, r: EXTI0::Resources) { .. }

// priority = 4
fn exti1(t: &mut T4, r: EXTI0::Resources) { .. }

This doesn't seem too bad but changing the priority of a task will require you
to change the type of the threshold token as well.

Appendix

The whole new typenum-based API

#![no_std]

extern crate typenum;

use core::marker::PhantomData;

use typenum::{IsGreaterOrEqual, Max, Maximum, True, Unsigned};

/// A resource, a mechanism to safely share data between tasks
pub trait Resource {
    /// The data protected by the resource
    type Data: Send;

    /// The ceiling of the resource
    type Ceiling: Unsigned;

    /// Borrows the resource data for the span of the current context (which may
    /// be a critical section)
    ///
    /// The current preemption threshold must be greater than the resource
    /// ceiling for this to work; otherwise this call will cause the program to
    /// not compile.
    fn borrow<'cs, THRESHOLD>(
        &'cs self,
        t: &'cs T<THRESHOLD>,
    ) -> &'cs R<Self::Data, Self::Ceiling>
    where
        THRESHOLD: IsGreaterOrEqual<Self::Ceiling, Output = True> + Unsigned;

    // plus `borrow_mut`

    /// Grants access to the resource data for the span of the closure `f`
    ///
    /// The closure may be executed in a critical section, created by raising
    /// the preemption threshold, if required
    fn claim<RTY, THRESHOLD, F>(&self, t: &mut T<THRESHOLD>, f: F) -> RTY
    where
        F: FnOnce(
            &R<Self::Data, Self::Ceiling>,
            &mut Maximum<THRESHOLD, Self::Ceiling>,
        ) -> RTY,
        THRESHOLD: Unsigned + Max<Self::Ceiling>;

    // plus `claim_mut`
}

/// An unlocked resource
pub struct R<DATA, CEILING>
where
    CEILING: Unsigned,
    DATA: Send,
{
    data: DATA,
    _ceiling: PhantomData<CEILING>,
}

/// Preemption threshold token
pub struct T<THRESHOLD>
where
    THRESHOLD: Unsigned,
{
    _threshold: PhantomData<THRESHOLD>,
}

cc @cr1901 this would fix the misoptimization you have been seeing in your AT2XT program. Actually implementing this for MSP430 may be possible / easy because effectively there's only two priority levels in that architecture.

app!() task interrupts not being called

(git clone https://github.com/jamwaffles/esp8266-at && cd esp8266-at && cargo run --example blink should exhibit this error on a Blue Pill)

Tested on nightly-2018-08-31 and nightly-2018-09-01.

I have the following code which should blink the onboard LED on an STM31F103 "blue pill" board:

#![no_std]
#![no_main]

extern crate cortex_m;
#[macro_use(entry, exception)]
extern crate cortex_m_rt;
extern crate cortex_m_rtfm as rtfm;
extern crate cortex_m_semihosting as semihosting;
extern crate panic_itm;
extern crate stm32f103xx_hal as blue_pill;

use blue_pill::gpio::gpioc::PC13;
use blue_pill::gpio::{Output, PushPull};
use blue_pill::prelude::*;
use blue_pill::stm32f103xx;
use blue_pill::timer::{Event as TimerEvent, Timer};
use core::fmt::Write;
use cortex_m::asm;
use cortex_m_rt::ExceptionFrame;
use rtfm::{app, Threshold};
use semihosting::hio;

app! {
    device: stm32f103xx,

    resources: {
        static LED: PC13<Output<PushPull>>;
        static HSTDOUT: hio::HStdout;
    },

    tasks: {
        SYS_TICK: {
            path: sys_tick,
            resources: [LED, HSTDOUT],
        },
    },
}

// Wrap `app!()`-generated `main()` to fix return type
fn ent() -> ! {
    main();
    loop {}
}
entry!(ent);

fn init(p: init::Peripherals) -> init::LateResources {
    let mut flash = p.device.FLASH.constrain();
    let mut rcc = p.device.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut gpioc = p.device.GPIOC.split(&mut rcc.apb2);

    Timer::syst(p.core.SYST, 10.hz(), clocks).listen(TimerEvent::Update);

    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    led.set_low();
    let mut hstdout = hio::hstdout().unwrap();

    writeln!(hstdout, "INIT").unwrap();

    init::LateResources {
        LED: led,
        HSTDOUT: hstdout,
    }
}

fn idle() -> ! {
    loop {
        rtfm::wfi();
    }
}

fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
    writeln!(r.HSTDOUT, "TICK, LED low: {}", r.LED.is_set_low()).unwrap();

    if r.LED.is_set_low() {
        r.LED.set_high()
    } else {
        r.LED.set_low()
    }
}

exception!(HardFault, hard_fault);

fn hard_fault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

exception!(*, default_handler);

fn default_handler(irqn: i16) {
    panic!("Unhandled exception (IRQn = {})", irqn);
}

The INIT print is output to the OpenOCD console, but the debug statement in sys_tick never gets called (and the LED never changes state). I also have some code that should respond to a DMA USART interrupt and that doesn't work either. I believe it's known that the app!() macro doesn't work due to the examples in japaric/stm32f103xx-hal being marked as outdated for some time. What can be done to get app!() working again? I'd be happy to take a stab if someone can give me some pointers ๐Ÿ˜„

make declaration of `Peripheral`s safe

Today, assigning celing to resources is unsafe because there's no mechanism to stop you from aliasing a peripheral and assign to each instance a different ceiling. Like this:

static GPIOA: Peripheral<Gpioa, C1> = unsafe { Peripheral::new(stm32f30x::GPIOA) };
static GPIOA2: Peripheral<Gpioa, C2> = unsafe { Peripheral::new(stm32f30x::GPIOA) };

I have come up with a macro that would fix the problem:

peripherals!(stm32f30x ,{
    GPIOA: Peripheral {
        register_block: Gpioa,
        ceiling: C1,
    }
});

Which should expand to:

#[export_name = "Gpioa"]  // or maybe `"stm32f30x.Gpioa"`?
static GPIOA: Peripheral<Gpioa, C1> = unsafe { Peripheral::new(stm32f30x::GPIOA) };

The key here is the export_name. With this we can prevent aliasing of Peripherals even if the declaration are in different modules / crates. For instance, this:

peripherals!(stm32f30x ,{
    GPIOA: Peripheral {
        register_block: Gpioa,
        ceiling: C1,
    }
});

mod foo {
    peripherals!(stm32f30x ,{
        GPIOA: Peripheral {
            register_block: Gpioa,
            ceiling: C1,
        }
    });
}

Would be rejected at compile time (not at link time) with the following error:

error: symbol `Gpioa` is already defined

Sadly, this macro is not implementable today because this is not a valid macro expansion:

#[export_name = stringify($RegisterBlock)]
static $PERIPHERAL: ...

AIUI, we need early expansion support to allow stringify! in that position.

[book] does it worth to pack tasks into crates?

Upcoming book contains very good messaging example: https://japaric.github.io/cortex-m-rtfm/user/messages.html. There are some common cases (like block transfers via uart/serial/i2c buses), had to be repeated in many project. It looks attractive to arrange those to crates somehow. Questions are:

  • Are there any recommendations how to arrange such RTFM extensions?
  • Should we move in such direction or isn't it better to push such feature into HAL (or some HAL extender)?
    • If yes, what should we know about intersecting such libs (with interrupts and processing inside) with RFTM priorities logic?

In other words, as user, i'd like to have "ready solution to read strings from UART" (that's only an example). And use it in RTFM. What is the optimal way to develop such crates? May be worth add some words about into the book.

PS. i'm not sure. Feel free to close anytime.

Interoperation with cortex_m::peripheral::Peripheral.borrow is unsound

Background

The cortex-m crate defines a Peripheral type which is a new type around the address block address of the peripheral. We use that Peripheral in our Peripheral constructor. svd2rust generates a bunch of cortex_m::Peripheral "instances" (as pub consts) which the user later uses to create a rtfm::Peripheral static.

Problem

The problem is that cortex_m::Peripheral provides a borrow method that grants access to the peripheral within a cortex_m::interrupt::free critical section but that borrow doesn't know about the SRP or task priorities. Problematic code looks like this:

use rtfm::{C1, P1, P2, Peripheral}
use stm32f30x::interrupt;

// RESOURCES
static GPIOA: Peripheral<stm32f30x::Gpioa, C1> = unsafe { Peripheral::new(stm32f30x::GPIOA) };

// TASKS
tasks!(stm32f30x, {
    t1: Task {
        interrupt: Exti0,
        priority: P1,
        enabled: true,
    },
    t2: Task {
        interrupt: Exti0,
        priority: P2,
        enabled: true,
    },
});

fn t1(_task: interrupt::Gpioa, prio: P1) {
    let ceil = prio.as_ceiling();

    // NOTE GPIOA has type `cortex_m_rtfm::Peripheral`
    let gpioa = GPIOA.access(&prio, ceil);

    // Immediately preempts
    rtfm::request(t2);
}

fn t2(_task: interrupt::Gpiob, _prio: P2) {
    cortex_m::interrupt::free(|cs| {
        // this GPIOA has type `cortex_m::peripheral::Peripheral`
        // this breaks SRP because GPIOA has ceiling C1 which is less than this task priority (P2)
        let gpioa = stm32f30x::GPIOA.borrow(cs);
    });
}

Possible solutions

  • Remove cortex_m::interrupt::free or have svd2rust generated code not create cortex_m::peripheral::Peripheral instances. Instead svd2rust should generate some other type for peripheral instances but that doesn't have a borrow method that works with cortex_m::interrupt::free

  • Make cortex_m::Peripheral::borrow unsafe if and only if the cortex-m-rtfm crate is used with the cortex-m crate. I don't know if this is possible at all: Perhaps add a "rtfm" Cargo feature to cortex-m that makes cortex_m::Peripheral::borrow unsafe and have cortex-m-rtfm depend on cortex-m with that feature enabled. Because Cargo features are additive if one cortex-m instance in the dependency graph has the "rtfm" feature enabled then the cortex-m crate will always be compiled with that feature enabled. If this does work, I think it would probably be considered a misuse of Cargo features...

Experiment: timing semantics

The original RTFM language had timing semantics baked into it. It was possible
to e.g. schedule task A to run N milliseconds after task B started where A and B
could even be the same task.

This issue will be used to track progress on incrementally reproducing this
functionality in cortex-m-rtfm.

Requirements

The implementation must only use core peripherals and must not require dynamic
allocations.

Milestones

  • recurrency: Have a single task execute periodically e.g. every second.
    "Task A must run N milliseconds after task A started"

  • multiple: Have two tasks with different priorities execute periodically at
    different frequencies.

  • dispatcher: Same as before but the tasks will run at the same priority.
    This time both tasks must be dispatched from a single interrupt.

  • offset: Have a task A execute periodically, and a task B execute after N
    milliseconds from the start of task A.

  • conditional: Same as before but have task B only be scheduled under some
    condition: for example every other execution of the task A.

  • srp: Make sure all tasks have their own resources and local data and that
    they adhere to SRP semantics.

Implementation

The implementation will live in a feature branch for the duration of the
experiment. The implementation should use DWT.CYCCNT as a monotonic timer and
SYS_TICK to generate timeouts interrupts.

RFC: Provide access to read-only registers without asserting data-race freedom

Summary

Provide a way to access read-only registers without using unsafe code.

Motivation

Currently, rtfm provides methods to access peripherals in a concurrent way.
This means that even when we know a read is safe (and doesn't change the value), rtfm doesn't know because there is no way for us to tell that a register is read-only (and safe).

Example

This example shows one way how to do this in rtfm v0.1.1
(It is rather dumb in the way it is setup, but it was the simplest way I could demonstrate this)

A button is setup on PA1, another button is on PA2 and a led on PA5.
The button on PA1 controls the led on PA5 inside the idle loop. The button on PA2 is setup to trigger an exti interrupt that controls the led on PA5.

extern crate cortex_m;
extern crate cortex_m_rt;
#[macro_use]
extern crate cortex_m_rtfm;
extern crate pac; // Our peripheral access crate

use rtfm::{C1, P0, P1, Resource, T0, T1, TMax};

peripherals! {
    GPIOA: Peripheral {
        register_block: GPIOA,
        ceiling: C1,
    },
    EXTI: Peripheral {
       register_block: EXTI,
       ceiling: C1,
    },
}

fn init(prio: T0, thres: &TMax) {
    // setup exti on PA2 ...
}

fn exti_pa2(mut task: EXTI0, ref prio: P1, ref thres: T1) {
    let exti = EXTI.access(prio, thres);
    let gpioa = GPIOA.access(prio, thres);
    
    // Clear pending exti and change led PA5
}

fn idle(ref prio: T0, ref thres: T0) -> ! {
    let gpioa_ro = pac::GPIOA.get();
    loop {
        if unsafe { (*gpioa_ro).idr.read().idr1().is_set() } {
            thres.raise(&GPIOA, |threshold: &T1| {
                let gpioa = GPIOA.access(prio, threshold);
                // Change state of led on PA5
            }
        }
    }
}

Design

With information on read-only registers, rtfm should enable us to write something like the following.

registers_ro! {
        GPIOA: [IDR,],
}

fn idle(ref prio: T0, ref thres: T0) -> ! {
    let gpioa_idr = GPIOA.access_read::<GPIOA_IDR>();
    loop {
        if gpio_idr.read().idr1().is_set() {
            thres.raise(&GPIOA, |threshold: &T1| {
                let gpioa = GPIOA.access(prio, threshold);
                // Change state of led on PA5
            }
        }
    }
}

gist of codes

Here, the register_ro macro provides an static object called GPIOA_IDR that is bare-metal::Register<T, RW> where RW: bare-metal::Read + !bare-metal::Write.

The function Peripheral::access_read<Register<T, RW> where RW: _ returns T.

Drawbacks & Questions

  • This seems to require a big change in the current ecosystem for such a small improvement.
  • Is it possible to combine this with the ideas coming from the [embedded-hal] project?
    Can you generalize over this?
  • Are all reads atomic?

Work on stable

This keeps track of all the unstable features that are required to use RTFM framework.

  • asm
  • compiler_builtins_lib
  • const_fn
  • optin_builtin_traits. The compiler suggest using the NoSend marker
    (struct) but that doesn't seem to exist anymore (?).
  • used
  • lang_items. For panic_fmt.
  • linkage. Could maybe be made optional
  • naked_functions. Could maybe be made optional
  • Xargo. Could be dropped if we had a rust-std component for the
    thumbv*m targets that contained the core and the compiler_builtins
    (compiled with the "mem" Cargo feature) crates

(I though this list would be longer)

Name collision on task / resource / peripheral name.

I had a bit of trouble with a task named RTC that uses a resource named RTC that is a peripheral named RTC. I am using RTFM 0.3.1.

The workaround I applied was to rename the resource RTC_R and not import the peripheral name directly.

The main problem seems to revolve around the fact that the task and resource are simply prefixed by a single underscore, resulting in a collision.

Conceptually, do you see tasks as living in a different namespace than resources ? Is there any other way similar use cases could be made simpler ?

app! {
    device: stm32f103xx,

    resources: {
        static RTC_R: stm32f103xx::RTC;
        static PWR: PWR;
        static SCB: SCB;
    },

    tasks: {
        RTC: {
            path: isr_rtc,
            resources: [RTC_R, PWR, SCB],
        },
    },
}

[RFC] Resources initialized at runtime

Motivation

In the current version of RTFM every resource must be assigned an initial value
at compile time and in const / static context. This requirement can sometimes be
too strict given the current limitations of const evaluation (no loops, no
conditionals, etc.).

In other cases one may want to initialize a resource according to the outcome of
the init function. For instance one may want to store the frequency of the
processor, product of the initialization routine, in a variable; in the current
system this requires the programmer to manually keep a copy of the value (as a
literal) in the resources section of the app! macro, which is error prone.

The workaround for these issues is to use an Option for the resource and to
select None as the initial value. Then a value can be assigned to the resource
resource in the init function. The downside of this approach is that it
requires the program to unwrap or match the Option value whenever they
want to access the resource data. And, of course, there's no compile time
guarantee that the programmer won't forget to actually initialize the Option
resource to some value in init; forgetting to do so would cause a panic! at
runtime.

Design

We make the initial value of resources optional in the app! macro:

app! {
    resources: {
        static BUFFER: [u8; 32] = [0; 32];
        static FREQUENCY: u32;
        static FOO: Vec<u8>;
    },
}

When the initial value of any resource is omitted the signature of init will
be changed to force the programmer to assign a value to these resources during
the initialization phase:

fn init(p: init::Peripherals, r: init::Resources) -> init::ResourceValues {
    // .. initialize stuff like the heap allocator ..

    // These are the initial values of the "uninitialized" resources
    init::ResourceValues {
        FREQUENCY: frequency,
        FOO: vec![a, b, c],
    }
}

Apart from this change in init there's no difference between using resources
with different initialization strategies.

What goes on under the hood?

Under the hood the resource without initial values will actually be static
variables with type equal to Option and initialized to None. However, since
we know that the resources will be initialized in init and before any task
(or idle) can run we can expose them to the user without the Option wrapper,
plus we can apply unsafe optimizations like intrinsics::unreachable under the
hood to eliminate branches and avoid unwrapping.

Possible extensions

Optimizing the pre-main initialization of static variables

Since the runtime initialized resources will be represented as static
variables containing an Option they'll still be initialized before init by
the runtime even though those initial values won't be read. This useless
initialization can be optimized away with the help of linker script magic:

// runtime initialized resources
#[link_section = ".uninit"]
static mut FREQUENCY: Option<u32> = None;

#[link_section = ".uninit"]
static mut FOO: Option<Vec<u8>> = None;

// "normal" resources
static mut BUFFER: [u8; 32] = [0; 32];

static variables placed in the .uninit linker section won't be initialized
before init. This may require changes in cortex-m-rt to ensure that static
variables placed in the .uninit section end up having a valid address in the
RAM region though. cf. rust-embedded/cortex-m-rt#32

cc @cr1901

Language item required, but not found: panic_fmt

Hello,

Trying to update to the last version of this crate, I had the following error at compilation :

[clement@clement-desktop-home i2c_magnetic]$ xargo build 
   Compiling i2c_magnetic v0.1.0 (file:///home/clement/atom-projects/cortex/i2c_magnetic)
error: language item required, but not found: `panic_fmt`

error: aborting due to previous error

error: Could not compile `i2c_magnetic`.

I also updated the f3 crate to the 0.5.1 version.

[RFC] "rooting" memory (`&'static mut-` references in `init`)

Background

I was revisiting the original use case that @pftbest once told me about -- a lock free single
producer single consumer ring buffer -- and that led me to submit #37 and noticed that RTFM still
can't fulfill that use case in 100% safe code. (I hope I'm wrong)

Below is a program that explains the use case:

#![deny(warnings)]
#![feature(proc_macro)]
#![no_std]

extern crate blue_pill;
extern crate cortex_m_rtfm as rtfm;
// https://github.com/japaric/spscrb
extern crate spscrb;

use blue_pill::stm32f103xx::{self, Interrupt};
use rtfm::{app, Threshold};
use spscrb::{Consumer, Producer, RingBuffer};

app! {
    device: stm32f103xx,

    resources: {
        static PRODUCER: Producer<u32, [u32; 32]>;
        static CONSUMER: Consumer<u32, [u32; 32]>;
    },

    tasks: {
        EXTI0: {
            path: exti0,
            resources: [PRODUCER],
            priority: 1,
        },

        EXTI1: {
            path: exti1,
            resources: [CONSUMER],
            priority: 2,
        },
    },
}

fn init(_p: init::Peripherals) -> init::LateResourceValues {
    let rb: &'static mut RingBuffer<u32, [u32; 32]>;

    // this part needs to be hidden from the user, or rather the user shouldn't need to do this
    rb = {
        static mut RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
        unsafe { &mut RB }
    };

    // NOTE this method *consumes* a `&'static mut` reference
    let (p, c) = rb.spsc();
    // let (p2, c2) = rb.spsc();
    // ^ this would be an error

    init::LateResourceValues { PRODUCER: p, CONSUMER: c }
}

fn idle() -> ! {
    rtfm::set_pending(Interrupt::EXTI0);
    rtfm::set_pending(Interrupt::EXTI1);

    loop {
        rtfm::wfi();
    }
}

fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
    // lock-free operation
    r.PRODUCER.enqueue(0xdead_beef).unwrap();
}

fn exti1(_t: &mut Threshold, r: EXTI1::Resources) {
    // lock-free operation
    r.CONSUMER.dequeue().unwrap();
}

Basically you have a statically allocated ring buffer which is hidden from the user. To enqueue and
dequeue elements into the buffer you have to use the "producer" and "consumer" end points,
respectively. These end points can be used from different execution contexts, which can run at
different priorities, in a lock free manner. This is memory safe, without locking, because the
producer and the consumer, each, own different cursors into the ring buffer and because the cursors
are word sized (they can be atomically read) -- check the implementation for details.

To construct the producer and consumer there's this spsc method with signature fn(&'static mut RingBuffer<_, _>) -> ... This signature ensures that (a) the ring buffer is statically allocated,
and thus it will never be destroyed, and (b) that once you have called this method the reference to
the ring buffer becomes invalidated (required to avoid mutable aliasing).

Thanks to #43 (thanks @jonas-schievink!) we can defer initialization of resources and initialize the
producer and consumer end points in init (see example above). However, one problem remains:
there's no way to safely get / create a &'static mut RingBuffer<_, _> in init. The only place
where &'static mut - references are available right now is in idle but that's too late for
initialization of resources (without wrapping them in Option).

Here's my proposal for solving this:

Proposal

Add a roots field to app!. This field contains a list of static variables with initial values.
The syntax of its contents is similar to the contents of resources.

app! {
    roots: {
        static RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
    },
}

The user will get a list of &'static mut- references to these roots in init:

// auto-generated: struct Roots { RB: &'static mut RingBuffer<u32, [u32; 32]>, }
fn init(roots: init::Roots, ..) {
    let (p, c) = roots.RB.spsc();
    // ..
}

Implementation

This can be implemented by creating a static mut variable for each root.

// auto-generated
fn main() {
    interrupt::free(|_| {
        static mut RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();

        init(init::Roots { RB: &mut RB }, ..);
    });
}

Alternatively, the roots could be allocated in the lowest / first stack frame (hence "rooting" in
the proposal name), which is known to never be deallocated: (I haven't tested if this works)

// auto-generated
fn main() {
    interrupt::free(|_| {
        let mut RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
        let roots = init::Roots { RB: &mut *(&mut RB as *mut _) };
        mem::forget(RB); // ???

        init(roots, ..);

        // ..
    });

    // idle is a divergent function
    idle();
}

Other use cases

Another use case for having &'static mut- references in init is being able to set up periodic
DMA transfers in init. An example of this use case is setting up a circular DMA transfer that
reads ADC or Serial data:

app! {
    roots: {
        static BUFFER: [[u16; 64]; 2] = [[0; 64]; 2];
    },

    resources: {
        // cf. japaric/embedded-hal#14 and the blue-pill `dma` module
        static BUFFER: dma::CircBuffer<[u16; 64]>;
    }
}

fn init(roots: init::Roots, ..) -> init::LateResourceValues {
    let buffer = adc1.start(dma1, roots.BUFFER);

    init::LateResourceValues {
        BUFFER: buffer,
    }
}

Thoughts? Can these use cases be safely achieved through some other means?

Experiment: message passing

In the original RTFM language it was possible to execute functions
asynchronously. This operation would queue the execution of the function with
some arguments. A dispatcher task would then execute the function according to
its priority.

This let you organize your programs as processes where each process was a
sequence of smaller tasks. Each task would communicate with the following task
through message passing reducing the need for shared global state.

This issue will be used to track progress on incrementally reproducing this
functionality in cortex-m-rtfm.

Requirements

The implementation must only use core peripherals and must not require dynamic
allocations.

Milestones

  • single: have a task execute a function f(a, b, c) asynchronously with
    priority lower than the task priority. The function will be executed after the
    current task ends.

  • buffered: as before but have the task execute several instances of the
    same function.

  • multiple: have a task execute more than one function asynchronously. Each
    function will run at a different priority.

  • dispatcher: as before but have both functions execute at the same
    priority. Both functions must be dispatched from a single interrupt.

  • srp: Make sure all tasks have their own resources and local data and that
    they adhere to SRP semantics.

  • timing-semantics: incorporate the work from #32 and add timing semantics.
    "Execute function f with arguments a, b and c after N milliseconds from the
    start of the current task."

Implementation

The implementation will live in a feature branch for the duration of the
experiment.

Which nightly should I be using?

I cannot get anything to link with rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20). More specifically the GNU linker fails with:

= note: arm-none-eabi-ld:
          BUG(cortex-m-rt): the reset vector is missing
          arm-none-eabi-ld:
          BUG(cortex-m-rt): the exception vectors are missing

and the rustc lld cannot even find lld, even with llvm-tools-preview-x86_64-unknown-linux-gnu

I would really like try Rust for a project, but it might be better to use C++ instead if I cannot even get my environment setup.

Cortex-M0(+) support

There's no BASEPRI on this architecture.

We can start with just supporting cooperative tasks and then support lock by manually masking the other tasks using NVIC.

Need advices for complex logic design approach

As far as i understand, in existing RTFM approach "tasks" are low-level units (driven by hardware interrupts). That may "conflict" with "normal" architecture design process (from top to bottom), when tasks are business-logic oriented units (because interrupt-driven approach is "from bottom to top").

Everything is great for simple case, when "user actions" and interrupts are equal:

  • User push button -> interrupt happens (event) -> task executed

What about more complex cases? Let's consider several samples:

  1. Device waits for input string with command (\n-limited) on UART
    • Interrupt happens on each char. But separate char means nothing useful, only accumulated string should emit event (run logical task)
  2. User push button, but we need filter jitter to drop noise (or jitter filter for EC11 quatratic encoder)
    • We have to setup additional timer interrupts, and do some black magic inside. Consumer should receive a simple result somehow (and be isolated from jitter filtering logic)

Of cause, we can make each RTFM task more and more complex, adding FSM inside and so on. But that still keep us on "from bottom to top" design - difficult and inconvenient for complex things.

Are there some pattern to solve such collision (move from "bottom to top" to "top to bottom" design)? May be virtual tasks/evens or something else...

Weird behavior with trivial 'idle' function

I'm observing weird behavior if I have a very trivial idle function (just an infinite loop): "init" function gets called twice.

What I observe is that:

  1. Second time init is called ::cortex_m::register::primask::read().is_active() returns true (which means interrupts are enabled, as I understand).
  2. If "idle" is trivial, I cannot set breakpoint to it (optimized away by compiler?)
  3. If "idle" is not trivial (loop with NOP or hprintln! at the beginning), everything works as expected.

I inserted breakpoint just before the "idle(p0, t0);" invocation and here is what I see in GDB:

0x80007a4 <x2_feed::main+8>                     bkpt   0x0000 ; Breakpoint I inserted just before the "idle" call
; Hey, where is my infinite loop?
; I think this piece of code is "closure" that calls "init'... That kind of explains it                                                                                                                                             
0x80007a6 <x2_feed::main::{{closure}}>          push   {r7, lr}                                                                                                                                        
0x80007a8 <x2_feed::main::{{closure}}+2>        mov    r7, sp                                                                                                                                          
0x80007aa <x2_feed::main::{{closure}}+4>        movw   r0, #8594       ; 0x2192

(this is probably not RTFM issue per se)

Errors running simple example

Hello there. I'm sure the issue is something I overlooked, but I'm attempting to get the zero-tasks.rs example working with the blue pill.

Cargo.toml:

[dependencies]
cortex-m-rtfm = "0.3.4"

[dependencies.stm32f103xx]
features = ["rt"]
version = "0.10.0"

main.rs: literally copied from examples/zero-tasks.rs in repo

Rust & Cargo versions:

rustc 1.30.0-nightly (5c875d938 2018-09-24)
cargo 1.31.0-nightly (de314a8b2 2018-09-21)

cargo build output:

error: `#[panic_handler]` function required, but not found

error: aborting due to previous error

error: Could not compile `testing`.

Update to cortex-m 0.5

With this crate on cortex-m-0.4, it's impossible to use any tooling which also includes cortex-m-0.5 - you get errors like

warning: Linking globals named 'CORE_PERIPHERALS': symbol multiply defined!
error: failed to load bc of "cortex_m.22u1h9r5-cgu.2":

Because (I'm pretty sure) both versions of the cortex-m libraries define those symbols and they clash. An example of this in action is:
https://travis-ci.org/wez/atsamd21-rs/builds/421535430

  = note: rust-lld: error: duplicate symbol: CORE_PERIPHERALS
          >>> defined at mod.rs:148 (/home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.4.3/src/peripheral/mod.rs:148)
          >>>            cortex_m-89a330ceffd88411.cortex_m.6bne0cac-cgu.0.rcgu.o:(CORE_PERIPHERALS) in archive /home/travis/build/wez/atsamd21-rs/metro_m0/target/thumbv6m-none-eabi/debug/deps/libcortex_m-89a330ceffd88411.rlib
          >>> defined at mod.rs:154 (/home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.5.6/src/peripheral/mod.rs:154)
          >>>            cortex_m-202b80cd0a33d787.cortex_m.er0kkjw4-cgu.0.rcgu.o:(.bss.CORE_PERIPHERALS+0x0) in archive /home/travis/build/wez/atsamd21-rs/metro_m0/target/thumbv6m-none-eabi/debug/deps/libcortex_m-202b80cd0a33d787.rlib         

Use toml like syntax instead of json like in app!

Hi @japaric @jonas-schievink. First of all, thanks for this extraordinary work.

What do you think of having toml like syntax in app!.

app! {
    device = blue_pill::stm32f103xx,
    
    [resources]
    static ON: bool = false;
    
    [tasks.SYS_TICK]
    path = toggle
    resources = [ON]
}

vs

app! {
    device: blue_pill::stm32f103xx,

    resources: {
        static ON: bool = false;
    },

    tasks: {
        SYS_TICK: {
            path: toggle,
            resources: [ON],
        },
    },
}

I'm don't know if this is possible at all but I feel the top one is much cleaner. What do you think?

Interrupt handlers from app macro don't appear to work in debug mode

I've converted a project I'm tinkering with to v2 but a basic example of a timer interrupt doesn't work in debug mode.

In release mode it works as expected, never hits the default_handler and a counter the timer interrupt is incrementing goes up every 10 seconds as expected.

In debug mode it goes straight to the default handler. The exception is 55, which is the correct interrupt.

I've double checked the "rt" feature flag stuff is getting generated in my device crate and the xargo expand output contains what looks like an override for the weak symbol defined in the device support crate:

#[allow(non_snake_case)]
#[allow(unsafe_code)]
#[export_name = "TIM7"]
pub unsafe extern "C" fn _TIM7() {
    let f: fn(&mut rtfm::Threshold, TIM7::Resources) = tim7_int;
    f(
        &mut if 1u8 == 1 << stm32f7x6::NVIC_PRIO_BITS {
            rtfm::Threshold::new(::core::u8::MAX)
        } else {
            rtfm::Threshold::new(1u8)
        },
        TIM7::Resources::new(),
    )
}

I'm a bit hazy on the details of how linking/weak symbols/etc works so please let me know if there are any commands i can run on the ELF files to get more info. This seemed relevant:

Release:
arm-none-eabi-objdump -t target/thumbv7em-none-eabihf/release/examples/adc | grep TIM7
08000222 g     F .text	0000002a TIM7

Then in gdb:
x 0x8000222
0x8000222 <adc::_TIM7>
info functions
<snip>
File examples/adc.rs:
fn adc::_TIM7();
  
Debug: 
arm-none-eabi-objdump -t target/thumbv7em-none-eabihf/debug/examples/adc | grep TIM7
0800539e g     F .text	00000012 _ZN59_$LT$stm32f7x6..TIM7$u20$as$u20$core..ops..deref..Deref$GT$5deref17h4b245b5fe55c3366E

Then in gdb:
x 0x0800539e
0x800539e <stm32f7x6::{{impl}}::deref>:	0x4601b083
info functions
<snip>
File examples/adc.rs:
static fn adc::_initResources::new() -> adc::_initResources;
static fn adc::idle() -> !;
static fn adc::init(stm32f7x6::Peripherals, adc::_initResources);
static fn adc::main();
static fn adc::main::{{closure}}(rtfm_core::Threshold *);

I've updated my arm and rust toolchains and double checked the various xargo and cargo config files for all projects and everything seems to be in line with your blue-pill repo.

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.