This implements the "rooting" mechanism proposed in #47. However, it implements a `root` constructor
function instead of list of `roots` values as originally proposed.
In a nutshell:
- There's a new field, `root`, which takes a path to the "root" constructor function.
- This constructor has signature `fn() -> T`
- When the `root` field is used the signature of `init` changes to accommodate a `&'static mut T`
argument at the end. The `T` in that argument type matches the type returned by the "root"
constructor.
- The "root"-ed value is stack allocated.
This enables the safe creation of `&'static mut` references. Example below:
``` rust
//#![feature(proc_macro)]
//#![no_std]
extern crate blue_pill;
extern crate cortex_m_rt;
extern crate cortex_m_rtfm as rtfm;
extern crate heapless;
use blue_pill::stm32f103xx;
use heapless::RingBuffer;
use heapless::ring_buffer::{Consumer, Producer};
use rtfm::{app, Threshold};
use stm32f103xx::Interrupt;
app! {
device: stm32f103xx,
resources: {
static CONSUMER: Consumer<'static, u32, [u32; 8]>;
static PRODUCER: Producer<'static, u32, [u32; 8]>;
},
root: root,
idle: {
resources: [CONSUMER],
},
tasks: {
EXTI0: {
path: exti0,
resources: [PRODUCER],
},
}
}
struct Root {
rb: RingBuffer<u32, [u32; 8]>,
}
fn root() -> Root {
Root {
rb: RingBuffer::new(),
}
}
fn init(_p: init::Peripherals, root: &'static mut Root) -> init::LateResourceValues {
let (p, c) = root.rb.split();
init::LateResourceValues {
CONSUMER: c,
PRODUCER: p,
}
}
fn idle(_t: &mut Threshold, r: idle::Resources) -> ! {
rtfm::set_pending(Interrupt::EXTI0);
loop {
if r.CONSUMER.dequeue().is_some() {
rtfm::bkpt();
} else {
rtfm::wfi();
}
}
}
fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
r.PRODUCER.enqueue(42).ok();
rtfm::bkpt();
}
```
This produces the following machine code:
``` armasm
0800019c <EXTI0>:
800019c: f240 0000 movw r0, #0
80001a0: f2c2 0000 movt r0, #8192 ; 0x2000
80001a4: 6800 ldr r0, [r0, #0]
80001a6: 6803 ldr r3, [r0, #0]
80001a8: 6842 ldr r2, [r0, #4]
80001aa: 1c51 adds r1, r2, #1
80001ac: f001 0107 and.w r1, r1, #7
80001b0: 4299 cmp r1, r3
80001b2: d006 beq.n 80001c2 <EXTI0+0x26>
80001b4: eb00 0282 add.w r2, r0, r2, lsl #2
80001b8: 232a movs r3, #42 ; 0x2a
80001ba: 6093 str r3, [r2, #8]
80001bc: f3bf 8f5f dmb sy
80001c0: 6041 str r1, [r0, #4]
80001c2: be00 bkpt 0x0000
80001c4: 4770 bx lr
080001c6 <main>:
80001c6: b08a sub sp, #40 ; 0x28 ; Root allocation
80001c8: f240 1030 movw r0, #304 ; 0x130
80001cc: 4669 mov r1, sp
80001ce: 22f0 movs r2, #240 ; 0xf0
80001d0: f6c0 0000 movt r0, #2048 ; 0x800
80001d4: 7800 ldrb r0, [r0, #0]
80001d6: 2000 movs r0, #0
80001d8: e9cd 0000 strd r0, r0, [sp]
80001dc: f240 0000 movw r0, #0
80001e0: f2c2 0000 movt r0, #8192 ; 0x2000
80001e4: b672 cpsid i
80001e6: 6001 str r1, [r0, #0] ; PRODUCER = ..
80001e8: f240 0004 movw r0, #4
80001ec: f2c2 0000 movt r0, #8192 ; 0x2000
80001f0: 6001 str r1, [r0, #0] ; CONSUMER = ..
80001f2: f24e 4106 movw r1, #58374 ; 0xe406
80001f6: f2ce 0100 movt r1, #57344 ; 0xe000
80001fa: 700a strb r2, [r1, #0]
80001fc: f24e 1100 movw r1, #57600 ; 0xe100
8000200: 2240 movs r2, #64 ; 0x40
8000202: f2ce 0100 movt r1, #57344 ; 0xe000
8000206: 600a str r2, [r1, #0]
8000208: b662 cpsie i
800020a: f8c1 2100 str.w r2, [r1, #256] ; 0x100
800020e: e006 b.n 800021e <main+0x58>
8000210: f3bf 8f5f dmb sy
8000214: 3201 adds r2, #1
8000216: f002 0207 and.w r2, r2, #7
800021a: 600a str r2, [r1, #0]
800021c: be00 bkpt 0x0000
800021e: 6801 ldr r1, [r0, #0]
8000220: 684b ldr r3, [r1, #4]
8000222: 680a ldr r2, [r1, #0]
8000224: 429a cmp r2, r3
8000226: d1f3 bne.n 8000210 <main+0x4a>
8000228: bf30 wfi
800022a: e7f8 b.n 800021e <main+0x58>
```
Unresolved questions:
- Is this mechanism memory safe in presence of `panic!` unwinding?
- If not, can we generate a compile error if `panic = abort` is *not* used?
- How does this affect the DMA API proposed in rust-embedded/embedded-hal#14
cc @pftbest