Resource trait, docs, examples and rtfm-syntax related changes

This commit is contained in:
Jorge Aparicio 2017-07-20 22:53:44 -05:00
parent 23425f2f06
commit c7b9507a57
22 changed files with 1482 additions and 171 deletions

7
.cargo/config Normal file
View file

@ -0,0 +1,7 @@
[target.thumbv7m-none-eabi]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
"-Z", "linker-flavor=ld",
]

View file

@ -19,6 +19,16 @@ static-ref = "0.2.0"
[dependencies.cortex-m-rtfm-macros]
path = "macros"
[dev-dependencies]
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
compiletest_rs = "0.2.8"
stm32f103xx = "0.7.1"
[dev-dependencies.cortex-m-rt]
features = ["abort-on-panic"]
version = "0.3.3"
[dev-dependencies.stm32f103xx]
features = ["rt"]
version = "0.7.1"
[profile.release]
lto = true

5
Xargo.toml Normal file
View file

@ -0,0 +1,5 @@
[dependencies.core]
stage = 0
[dependencies.compiler_builtins]
stage = 1

81
examples/full-syntax.rs Normal file
View file

@ -0,0 +1,81 @@
//! A showcase of the `app!` macro syntax
#![deny(unsafe_code)]
#![feature(const_fn)]
#![feature(proc_macro)]
#![no_std]
#[macro_use(task)]
extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx;
use rtfm::{app, Resource, Threshold};
app! {
device: stm32f103xx,
resources: {
static CO_OWNED: u32 = 0;
static OWNED: bool = false;
static SHARED: bool = false;
},
init: {
path: init_, // this is a path to the "init" function
},
idle: {
locals: {
static COUNTER: u32 = 0;
},
path: idle_, // this is a path to the "idle" function
resources: [OWNED, SHARED],
},
tasks: {
SYS_TICK: {
priority: 1,
resources: [CO_OWNED, SHARED],
},
TIM2: {
enabled: true,
priority: 1,
resources: [CO_OWNED],
},
},
}
fn init_(_p: init::Peripherals, _r: init::Resources) {}
fn idle_(t: &mut Threshold, l: &mut idle::Locals, mut r: idle::Resources) -> ! {
loop {
*l.COUNTER += 1;
**r.OWNED != **r.OWNED;
if **r.OWNED {
if r.SHARED.claim(t, |shared, _| **shared) {
rtfm::wfi();
}
} else {
r.SHARED.claim_mut(t, |shared, _| **shared = !**shared);
}
}
}
task!(SYS_TICK, sys_tick, Local {
static STATE: bool = true;
});
fn sys_tick(_t: &mut Threshold, l: &mut Local, r: SYS_TICK::Resources) {
*l.STATE = !*l.STATE;
**r.CO_OWNED += 1;
}
task!(TIM2, tim2);
fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
**r.CO_OWNED += 1;
}

69
examples/generics.rs Normal file
View file

@ -0,0 +1,69 @@
//! Working with resources in a generic fashion
#![deny(unsafe_code)]
#![feature(proc_macro)]
#![no_std]
#[macro_use(task)]
extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx;
use rtfm::{app, Resource, Threshold};
use stm32f103xx::{SPI1, GPIOA};
app! {
device: stm32f103xx,
tasks: {
EXTI0: {
enabled: true,
priority: 1,
resources: [GPIOA, SPI1],
},
EXTI1: {
enabled: true,
priority: 2,
resources: [GPIOA, SPI1],
},
},
}
fn init(_p: init::Peripherals) {}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
// a generic function to use resources in any task (regardless of its priority)
fn work<G, S>(t: &mut Threshold, gpioa: &G, spi1: &S)
where
G: Resource<Data = GPIOA>,
S: Resource<Data = SPI1>,
{
gpioa.claim(t, |_gpioa, t| {
// drive NSS low
spi1.claim(t, |_spi1, _| {
// transfer data
});
// drive NSS high
});
}
task!(EXTI0, exti0);
// this task needs critical sections to access the resources
fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
work(t, &r.GPIOA, &r.SPI1);
}
task!(EXTI1, exti1);
// this task has direct access to the resources
fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
work(t, r.GPIOA, r.SPI1);
}

125
examples/nested.rs Normal file
View file

@ -0,0 +1,125 @@
//! Nesting claims and how the preemption threshold works
//!
//! If you run this program you'll hit the breakpoints as indicated by the
//! letters in the comments: A, then B, then C, etc.
#![deny(unsafe_code)]
#![feature(const_fn)]
#![feature(proc_macro)]
#![no_std]
#[macro_use(task)]
extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx;
use stm32f103xx::Interrupt;
use rtfm::{app, Resource, Threshold};
app! {
device: stm32f103xx,
resources: {
static LOW: u64 = 0;
static HIGH: u64 = 0;
},
tasks: {
EXTI0: {
enabled: true,
priority: 1,
resources: [LOW, HIGH],
},
EXTI1: {
enabled: true,
priority: 2,
resources: [LOW],
},
EXTI2: {
enabled: true,
priority: 3,
resources: [HIGH],
},
},
}
fn init(_p: init::Peripherals, _r: init::Resources) {}
fn idle() -> ! {
// sets task `exti0` as pending
//
// because `exti0` has higher priority than `idle` it will be executed
// immediately
rtfm::set_pending(Interrupt::EXTI0); // ~> exti0
loop {
rtfm::wfi();
}
}
task!(EXTI0, exti0);
fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
// because this task has a priority of 1 the preemption threshold is also 1
// A
rtfm::bkpt();
// because `exti1` has higher priority than `exti0` it can preempt it
rtfm::set_pending(Interrupt::EXTI1); // ~> exti1
// a claim creates a critical section
r.LOW.claim_mut(t, |_low, t| {
// this claim increases the preemption threshold to 2
// just high enough to not race with task `exti1` for access to the
// `LOW` resource
// C
rtfm::bkpt();
// now `exti1` can't preempt this task because its priority is equal to
// the current preemption threshold
rtfm::set_pending(Interrupt::EXTI1);
// but `exti2` can, because its priority is higher than the current
// preemption threshold
rtfm::set_pending(Interrupt::EXTI2); // ~> exti2
// E
rtfm::bkpt();
// claims can be nested
r.HIGH.claim_mut(t, |_high, _| {
// This claim increases the preemption threshold to 3
// now `exti2` can't preempt this task
rtfm::set_pending(Interrupt::EXTI2);
// F
rtfm::bkpt();
});
// upon leaving the critical section the preemption threshold drops to 2
// and `exti2` immediately preempts this task
// ~> exti2
});
// once again the preemption threshold drops to 1
// now the pending `exti1` can preempt this task
// ~> exti1
}
task!(EXTI1, exti1);
fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
// B, H
rtfm::bkpt();
}
task!(EXTI2, exti2);
fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
// D, G
rtfm::bkpt();
}

91
examples/one-task.rs Normal file
View file

@ -0,0 +1,91 @@
//! An application with one task
#![deny(unsafe_code)]
#![feature(const_fn)]
#![feature(proc_macro)]
#![no_std]
extern crate cortex_m;
#[macro_use(task)]
extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx;
use cortex_m::peripheral::SystClkSource;
use rtfm::{app, Threshold};
app! {
device: stm32f103xx,
// Here tasks are declared
//
// Each task corresponds to an interrupt or an exception. Every time the
// interrupt or exception becomes *pending* the corresponding task handler
// will be executed.
tasks: {
// Here we declare that we'll use the SYS_TICK exception as a task
SYS_TICK: {
// This is the priority of the task.
// 1 is the lowest priority a task can have.
// The maximum priority is determined by the number of priority bits
// the device has. This device has 4 priority bits so 16 is the
// maximum value.
priority: 1,
// These are the *resources* associated with this task
//
// The peripherals that the task needs can be listed here
resources: [GPIOC],
},
}
}
fn init(p: init::Peripherals) {
// power on GPIOC
p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
// configure PC13 as output
p.GPIOC.bsrr.write(|w| w.bs13().set());
p.GPIOC
.crh
.modify(|_, w| w.mode13().output().cnf13().push());
// configure the system timer to generate one interrupt every second
p.SYST.set_clock_source(SystClkSource::Core);
p.SYST.set_reload(8_000_000); // 1s
p.SYST.enable_interrupt();
p.SYST.enable_counter();
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
// This binds the `sys_tick` handler to the `SYS_TICK` task
//
// This particular handler has local state associated to it. The value of the
// `STATE` variable will be preserved across invocations of this handler
task!(SYS_TICK, sys_tick, Locals {
static STATE: bool = false;
});
// This is the task handler of the SYS_TICK exception
//
// `t` is the preemption threshold token. We won't use it this time.
// `l` is the data local to this task. The type here must match the one declared
// in `task!`.
// `r` is the resources this task has access to. `SYS_TICK::Resources` has one
// field per resource declared in `app!`.
fn sys_tick(_t: &mut Threshold, l: &mut Locals, r: SYS_TICK::Resources) {
// toggle state
*l.STATE = !*l.STATE;
if *l.STATE {
// set the pin PC13 high
r.GPIOC.bsrr.write(|w| w.bs13().set());
} else {
// set the pin PC13 low
r.GPIOC.bsrr.write(|w| w.br13().reset());
}
}

70
examples/preemption.rs Normal file
View file

@ -0,0 +1,70 @@
//! Two tasks running at different priorities with access to the same resource
#![deny(unsafe_code)]
#![feature(const_fn)]
#![feature(proc_macro)]
#![no_std]
#[macro_use(task)]
extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx;
use rtfm::{app, Resource, Threshold};
app! {
device: stm32f103xx,
resources: {
static COUNTER: u64 = 0;
},
tasks: {
// the task `SYS_TICK` has higher priority than `TIM2`
SYS_TICK: {
priority: 2,
resources: [COUNTER],
},
TIM2: {
enabled: true,
priority: 1,
resources: [COUNTER],
},
},
}
fn init(_p: init::Peripherals, _r: init::Resources) {
// ..
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
task!(SYS_TICK, sys_tick);
fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
// ..
// this task can't be preempted by `tim2` so it has direct access to the
// resource data
**r.COUNTER += 1;
// ..
}
task!(TIM2, tim2);
fn tim2(t: &mut Threshold, mut r: TIM2::Resources) {
// ..
// as this task runs at lower priority it needs a critical section to
// prevent `sys_tick` from preempting it while it modifies this resource
// data. The critical section is required to prevent data races which can
// lead to data corruption or data loss
r.COUNTER.claim_mut(t, |counter, _t| { **counter += 1; });
// ..
}

75
examples/two-tasks.rs Normal file
View file

@ -0,0 +1,75 @@
//! Two tasks running at the same priority with access to the same resource
#![deny(unsafe_code)]
#![feature(const_fn)]
#![feature(proc_macro)]
#![no_std]
#[macro_use(task)]
extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx;
use rtfm::{app, Threshold};
app! {
device: stm32f103xx,
// Resources that are plain data, not peripherals
resources: {
// Declaration of resources looks like the declaration of `static`
// variables
static COUNTER: u64 = 0;
},
tasks: {
SYS_TICK: {
priority: 1,
// Both this task and TIM2 have access to the `COUNTER` resource
resources: [COUNTER],
},
// An interrupt as a task
TIM2: {
// For interrupts the `enabled` field must be specified. It
// indicates if the interrupt will be enabled or disabled once
// `idle` starts
enabled: true,
priority: 1,
resources: [COUNTER],
},
},
}
// when data resources are declared in the top `resources` field, `init` will
// have full access to them
fn init(_p: init::Peripherals, _r: init::Resources) {
// ..
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
task!(SYS_TICK, sys_tick);
// As both tasks are running at the same priority one can't preempt the other.
// Thus both tasks have direct access to the resource
fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
// ..
**r.COUNTER += 1;
// ..
}
task!(TIM2, tim2);
fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
// ..
**r.COUNTER += 1;
// ..
}

49
examples/zero-tasks.rs Normal file
View file

@ -0,0 +1,49 @@
//! Minimal example with zero tasks
#![deny(unsafe_code)]
#![feature(proc_macro)] // IMPORTANT always include this feature gate
#![no_std]
extern crate cortex_m_rtfm as rtfm; // IMPORTANT always do this rename
extern crate stm32f103xx; // the device crate
// import the procedural macro
use rtfm::app;
// This macro call indicates that this is a RTFM application
//
// This macro will expand to a `main` function so you don't need to supply
// `main` yourself.
app! {
// this is a path to the device crate
device: stm32f103xx,
}
// The initialization phase.
//
// This runs first and within a *global* critical section. Nothing can preempt
// this function.
fn init(p: init::Peripherals) {
// This function has access to all the peripherals of the device
p.GPIOA;
p.RCC;
// ..
// You'll hit this breakpoint first
rtfm::bkpt();
}
// The idle loop.
//
// This runs afterwards and has a priority of 0. All tasks can preempt this
// function. This function can never return so it must contain some sort of
// endless loop.
fn idle() -> ! {
// And then this breakpoint
rtfm::bkpt();
loop {
// This puts the processor to sleep until there's a task to service
rtfm::wfi();
}
}

55
gen-examples.sh Normal file
View file

@ -0,0 +1,55 @@
# Converts the examples in the `examples` directory into documentation in the
# `examples` module (`src/examples/*.rs`)
set -ex
main() {
local examples=(
zero-tasks
one-task
two-tasks
preemption
nested
generics
full-syntax
)
rm -rf src/examples
mkdir src/examples
cat >src/examples/mod.rs <<'EOF'
//! Examples
// Auto-generated. Do not modify.
EOF
local i=0 out=
for ex in ${examples[@]}; do
name=_${i}_${ex//-/_}
out=src/examples/${name}.rs
echo "pub mod $name;" >> src/examples/mod.rs
grep '//!' examples/$ex.rs > $out
echo '//!' >> $out
echo '//! ```' >> $out
grep -v '//!' examples/$ex.rs | (
IFS=''
while read line; do
echo "//! $line" >> $out;
done
)
echo '//! ```' >> $out
echo '// Auto-generated. Do not modify.' >> $out
chmod -x $out
i=$(( i + 1 ))
done
chmod -x src/examples/mod.rs
}
main

View file

@ -64,15 +64,16 @@ fn idle(
let ty = &resource.ty;
lfields.push(quote! {
pub #name: #ty,
pub #name: #krate::Static<#ty>,
});
lexprs.push(quote! {
#name: #expr,
#name: unsafe { #krate::Static::new(#expr) },
});
}
mod_items.push(quote! {
#[allow(non_snake_case)]
pub struct Locals {
#(#lfields)*
}
@ -114,19 +115,24 @@ fn idle(
let ty = &resource.ty;
rfields.push(quote! {
pub #name: &'static mut #ty,
pub #name: &'static mut ::#krate::Static<#ty>,
});
rexprs.push(quote! {
#name: &mut *#super_::#name.get(),
#name: #krate::Static::ref_mut(
&mut *#super_::#name.get(),
),
});
} else {
rfields.push(quote! {
pub #name: &'static mut ::#device::#name,
pub #name:
&'static mut ::#krate::Static<::#device::#name>,
});
rexprs.push(quote! {
#name: &mut *::#device::#name.get(),
#name: ::krate::Static::ref_mut(
&mut *::#device::#name.get(),
),
});
}
} else {
@ -329,8 +335,8 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let ty = &resource.ty;
root.push(quote! {
static #name: #krate::Resource<#ty> =
#krate::Resource::new(#expr);
static #name: #krate::Cell<#ty> =
#krate::Cell::new(#expr);
});
} else {
// Peripheral
@ -343,26 +349,30 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let ty = &resource.ty;
root.push(quote! {
static #name: #krate::Resource<#ty> =
#krate::Resource::new(#expr);
static #name: #krate::Cell<#ty> =
#krate::Cell::new(#expr);
});
impl_items.push(quote! {
pub fn borrow<'cs>(
type Data = #ty;
fn borrow<'cs>(
&'cs self,
cs: &'cs #krate::CriticalSection,
_cs: &'cs #krate::CriticalSection,
) -> &'cs #krate::Static<#ty> {
unsafe { #name.borrow(cs) }
unsafe { #krate::Static::ref_(&*#name.get()) }
}
pub fn borrow_mut<'cs>(
fn borrow_mut<'cs>(
&'cs mut self,
cs: &'cs #krate::CriticalSection,
_cs: &'cs #krate::CriticalSection,
) -> &'cs mut #krate::Static<#ty> {
unsafe { #name.borrow_mut(cs) }
unsafe {
#krate::Static::ref_mut(&mut *#name.get())
}
}
pub fn claim<R, F>(
fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
@ -373,16 +383,18 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
&mut #krate::Threshold) -> R
{
unsafe {
#name.claim(
#krate::claim(
#name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
|data| #krate::Static::ref_(&*data),
)
}
}
pub fn claim_mut<R, F>(
fn claim_mut<R, F>(
&mut self,
t: &mut #krate::Threshold,
f: F,
@ -393,45 +405,77 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
&mut #krate::Threshold) -> R
{
unsafe {
#name.claim_mut(
#krate::claim(
#name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
|data| #krate::Static::ref_mut(&mut *data),
)
}
}
});
} else {
root.push(quote! {
static #name: #krate::Peripheral<#device::#name> =
#krate::Peripheral::new(#device::#name);
});
impl_items.push(quote! {
pub fn borrow<'cs>(
type Data = #device::#name;
fn borrow<'cs>(
&'cs self,
cs: &'cs #krate::CriticalSection,
) -> &'cs #device::#name {
unsafe { #name.borrow(cs) }
_cs: &'cs #krate::CriticalSection,
) -> &'cs #krate::Static<#name> {
unsafe { #krate::Static::ref_(&*#name.get()) }
}
pub fn claim<R, F>(
fn borrow_mut<'cs>(
&'cs mut self,
_cs: &'cs #krate::CriticalSection,
) -> &'cs mut #krate::Static<#name> {
unsafe {
#krate::Static::ref_mut(&mut *#name.get())
}
}
fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&#device::#name,
&#krate::Static<#name>,
&mut #krate::Threshold) -> R
{
unsafe {
#name.claim(
#krate::claim(
#device::#name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
|data| #krate::Static::ref_(&*data),
)
}
}
fn claim_mut<R, F>(
&mut self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&mut #krate::Static<#name>,
&mut #krate::Threshold) -> R
{
unsafe {
#krate::claim(
#device::#name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
|data| #krate::Static::ref_mut(&mut *data),
)
}
}
@ -439,9 +483,8 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
}
impls.push(quote! {
#[allow(dead_code)]
#[allow(unsafe_code)]
impl _resource::#name {
impl #krate::Resource for _resource::#name {
#(#impl_items)*
}
});
@ -512,11 +555,14 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
});
} else {
fields.push(quote! {
pub #name: &'a mut ::#device::#name,
pub #name:
&'a mut ::#krate::Static<::#device::#name>,
});
exprs.push(quote! {
#name: &mut *::#device::#name.get(),
#name: ::#krate::Static::ref_mut(
&mut *::#device::#name.get(),
),
});
}
}
@ -558,12 +604,13 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let priority = task.priority;
root.push(quote!{
#[allow(dead_code)]
#[allow(non_snake_case)]
#[allow(unsafe_code)]
mod #name {
#[deny(dead_code)]
pub const #name: u8 = #priority;
#[allow(dead_code)]
#[deny(const_err)]
const CHECK_PRIORITY: (u8, u8) = (
#priority - 1,

6
memory.x Normal file
View file

@ -0,0 +1,6 @@
/* STM32F103C8V6 */
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View file

@ -0,0 +1,53 @@
//! Minimal example with zero tasks
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(proc_macro)] // IMPORTANT always include this feature gate
//! #![no_std]
//!
//! extern crate cortex_m_rtfm as rtfm; // IMPORTANT always do this rename
//! extern crate stm32f103xx; // the device crate
//!
//! // import the procedural macro
//! use rtfm::app;
//!
//! // This macro call indicates that this is a RTFM application
//! //
//! // This macro will expand to a `main` function so you don't need to supply
//! // `main` yourself.
//! app! {
//! // this is a path to the device crate
//! device: stm32f103xx,
//! }
//!
//! // The initialization phase.
//! //
//! // This runs first and within a *global* critical section. Nothing can preempt
//! // this function.
//! fn init(p: init::Peripherals) {
//! // This function has access to all the peripherals of the device
//! p.GPIOA;
//! p.RCC;
//! // ..
//!
//! // You'll hit this breakpoint first
//! rtfm::bkpt();
//! }
//!
//! // The idle loop.
//! //
//! // This runs afterwards and has a priority of 0. All tasks can preempt this
//! // function. This function can never return so it must contain some sort of
//! // endless loop.
//! fn idle() -> ! {
//! // And then this breakpoint
//! rtfm::bkpt();
//!
//! loop {
//! // This puts the processor to sleep until there's a task to service
//! rtfm::wfi();
//! }
//! }
//! ```
// Auto-generated. Do not modify.

View file

@ -0,0 +1,95 @@
//! An application with one task
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(const_fn)]
//! #![feature(proc_macro)]
//! #![no_std]
//!
//! extern crate cortex_m;
//! #[macro_use(task)]
//! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f103xx;
//!
//! use cortex_m::peripheral::SystClkSource;
//! use rtfm::{app, Threshold};
//!
//! app! {
//! device: stm32f103xx,
//!
//! // Here tasks are declared
//! //
//! // Each task corresponds to an interrupt or an exception. Every time the
//! // interrupt or exception becomes *pending* the corresponding task handler
//! // will be executed.
//! tasks: {
//! // Here we declare that we'll use the SYS_TICK exception as a task
//! SYS_TICK: {
//! // This is the priority of the task.
//! // 1 is the lowest priority a task can have.
//! // The maximum priority is determined by the number of priority bits
//! // the device has. This device has 4 priority bits so 16 is the
//! // maximum value.
//! priority: 1,
//!
//! // These are the *resources* associated with this task
//! //
//! // The peripherals that the task needs can be listed here
//! resources: [GPIOC],
//! },
//! }
//! }
//!
//! fn init(p: init::Peripherals) {
//! // power on GPIOC
//! p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
//!
//! // configure PC13 as output
//! p.GPIOC.bsrr.write(|w| w.bs13().set());
//! p.GPIOC
//! .crh
//! .modify(|_, w| w.mode13().output().cnf13().push());
//!
//! // configure the system timer to generate one interrupt every second
//! p.SYST.set_clock_source(SystClkSource::Core);
//! p.SYST.set_reload(8_000_000); // 1s
//! p.SYST.enable_interrupt();
//! p.SYST.enable_counter();
//! }
//!
//! fn idle() -> ! {
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! // This binds the `sys_tick` handler to the `SYS_TICK` task
//! //
//! // This particular handler has local state associated to it. The value of the
//! // `STATE` variable will be preserved across invocations of this handler
//! task!(SYS_TICK, sys_tick, Locals {
//! static STATE: bool = false;
//! });
//!
//! // This is the task handler of the SYS_TICK exception
//! //
//! // `t` is the preemption threshold token. We won't use it this time.
//! // `l` is the data local to this task. The type here must match the one declared
//! // in `task!`.
//! // `r` is the resources this task has access to. `SYS_TICK::Resources` has one
//! // field per resource declared in `app!`.
//! fn sys_tick(_t: &mut Threshold, l: &mut Locals, r: SYS_TICK::Resources) {
//! // toggle state
//! *l.STATE = !*l.STATE;
//!
//! if *l.STATE {
//! // set the pin PC13 high
//! r.GPIOC.bsrr.write(|w| w.bs13().set());
//! } else {
//! // set the pin PC13 low
//! r.GPIOC.bsrr.write(|w| w.br13().reset());
//! }
//! }
//! ```
// Auto-generated. Do not modify.

View file

@ -0,0 +1,79 @@
//! Two tasks running at the same priority with access to the same resource
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(const_fn)]
//! #![feature(proc_macro)]
//! #![no_std]
//!
//! #[macro_use(task)]
//! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f103xx;
//!
//! use rtfm::{app, Threshold};
//!
//! app! {
//! device: stm32f103xx,
//!
//! // Resources that are plain data, not peripherals
//! resources: {
//! // Declaration of resources looks like the declaration of `static`
//! // variables
//! static COUNTER: u64 = 0;
//! },
//!
//! tasks: {
//! SYS_TICK: {
//! priority: 1,
//! // Both this task and TIM2 have access to the `COUNTER` resource
//! resources: [COUNTER],
//! },
//!
//! // An interrupt as a task
//! TIM2: {
//! // For interrupts the `enabled` field must be specified. It
//! // indicates if the interrupt will be enabled or disabled once
//! // `idle` starts
//! enabled: true,
//! priority: 1,
//! resources: [COUNTER],
//! },
//! },
//! }
//!
//! // when data resources are declared in the top `resources` field, `init` will
//! // have full access to them
//! fn init(_p: init::Peripherals, _r: init::Resources) {
//! // ..
//! }
//!
//! fn idle() -> ! {
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! task!(SYS_TICK, sys_tick);
//!
//! // As both tasks are running at the same priority one can't preempt the other.
//! // Thus both tasks have direct access to the resource
//! fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
//! // ..
//!
//! **r.COUNTER += 1;
//!
//! // ..
//! }
//!
//! task!(TIM2, tim2);
//!
//! fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
//! // ..
//!
//! **r.COUNTER += 1;
//!
//! // ..
//! }
//! ```
// Auto-generated. Do not modify.

View file

@ -0,0 +1,74 @@
//! Two tasks running at different priorities with access to the same resource
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(const_fn)]
//! #![feature(proc_macro)]
//! #![no_std]
//!
//! #[macro_use(task)]
//! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f103xx;
//!
//! use rtfm::{app, Resource, Threshold};
//!
//! app! {
//! device: stm32f103xx,
//!
//! resources: {
//! static COUNTER: u64 = 0;
//! },
//!
//! tasks: {
//! // the task `SYS_TICK` has higher priority than `TIM2`
//! SYS_TICK: {
//! priority: 2,
//! resources: [COUNTER],
//! },
//!
//! TIM2: {
//! enabled: true,
//! priority: 1,
//! resources: [COUNTER],
//! },
//! },
//! }
//!
//! fn init(_p: init::Peripherals, _r: init::Resources) {
//! // ..
//! }
//!
//! fn idle() -> ! {
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! task!(SYS_TICK, sys_tick);
//!
//! fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
//! // ..
//!
//! // this task can't be preempted by `tim2` so it has direct access to the
//! // resource data
//! **r.COUNTER += 1;
//!
//! // ..
//! }
//!
//! task!(TIM2, tim2);
//!
//! fn tim2(t: &mut Threshold, mut r: TIM2::Resources) {
//! // ..
//!
//! // as this task runs at lower priority it needs a critical section to
//! // prevent `sys_tick` from preempting it while it modifies this resource
//! // data. The critical section is required to prevent data races which can
//! // lead to data corruption or data loss
//! r.COUNTER.claim_mut(t, |counter, _t| { **counter += 1; });
//!
//! // ..
//! }
//! ```
// Auto-generated. Do not modify.

129
src/examples/_4_nested.rs Normal file
View file

@ -0,0 +1,129 @@
//! Nesting claims and how the preemption threshold works
//!
//! If you run this program you'll hit the breakpoints as indicated by the
//! letters in the comments: A, then B, then C, etc.
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(const_fn)]
//! #![feature(proc_macro)]
//! #![no_std]
//!
//! #[macro_use(task)]
//! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f103xx;
//!
//! use stm32f103xx::Interrupt;
//! use rtfm::{app, Resource, Threshold};
//!
//! app! {
//! device: stm32f103xx,
//!
//! resources: {
//! static LOW: u64 = 0;
//! static HIGH: u64 = 0;
//! },
//!
//! tasks: {
//! EXTI0: {
//! enabled: true,
//! priority: 1,
//! resources: [LOW, HIGH],
//! },
//!
//! EXTI1: {
//! enabled: true,
//! priority: 2,
//! resources: [LOW],
//! },
//!
//! EXTI2: {
//! enabled: true,
//! priority: 3,
//! resources: [HIGH],
//! },
//! },
//! }
//!
//! fn init(_p: init::Peripherals, _r: init::Resources) {}
//!
//! fn idle() -> ! {
//! // sets task `exti0` as pending
//! //
//! // because `exti0` has higher priority than `idle` it will be executed
//! // immediately
//! rtfm::set_pending(Interrupt::EXTI0); // ~> exti0
//!
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! task!(EXTI0, exti0);
//!
//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
//! // because this task has a priority of 1 the preemption threshold is also 1
//!
//! // A
//! rtfm::bkpt();
//!
//! // because `exti1` has higher priority than `exti0` it can preempt it
//! rtfm::set_pending(Interrupt::EXTI1); // ~> exti1
//!
//! // a claim creates a critical section
//! r.LOW.claim_mut(t, |_low, t| {
//! // this claim increases the preemption threshold to 2
//! // just high enough to not race with task `exti1` for access to the
//! // `LOW` resource
//!
//! // C
//! rtfm::bkpt();
//!
//! // now `exti1` can't preempt this task because its priority is equal to
//! // the current preemption threshold
//! rtfm::set_pending(Interrupt::EXTI1);
//!
//! // but `exti2` can, because its priority is higher than the current
//! // preemption threshold
//! rtfm::set_pending(Interrupt::EXTI2); // ~> exti2
//!
//! // E
//! rtfm::bkpt();
//!
//! // claims can be nested
//! r.HIGH.claim_mut(t, |_high, _| {
//! // This claim increases the preemption threshold to 3
//!
//! // now `exti2` can't preempt this task
//! rtfm::set_pending(Interrupt::EXTI2);
//!
//! // F
//! rtfm::bkpt();
//! });
//!
//! // upon leaving the critical section the preemption threshold drops to 2
//! // and `exti2` immediately preempts this task
//! // ~> exti2
//! });
//!
//! // once again the preemption threshold drops to 1
//! // now the pending `exti1` can preempt this task
//! // ~> exti1
//! }
//!
//! task!(EXTI1, exti1);
//!
//! fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
//! // B, H
//! rtfm::bkpt();
//! }
//!
//! task!(EXTI2, exti2);
//!
//! fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
//! // D, G
//! rtfm::bkpt();
//! }
//! ```
// Auto-generated. Do not modify.

View file

@ -0,0 +1,73 @@
//! Working with resources in a generic fashion
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(proc_macro)]
//! #![no_std]
//!
//! #[macro_use(task)]
//! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f103xx;
//!
//! use rtfm::{app, Resource, Threshold};
//! use stm32f103xx::{SPI1, GPIOA};
//!
//! app! {
//! device: stm32f103xx,
//!
//! tasks: {
//! EXTI0: {
//! enabled: true,
//! priority: 1,
//! resources: [GPIOA, SPI1],
//! },
//!
//! EXTI1: {
//! enabled: true,
//! priority: 2,
//! resources: [GPIOA, SPI1],
//! },
//! },
//! }
//!
//! fn init(_p: init::Peripherals) {}
//!
//! fn idle() -> ! {
//! loop {
//! rtfm::wfi();
//! }
//! }
//!
//! // a generic function to use resources in any task (regardless of its priority)
//! fn work<G, S>(t: &mut Threshold, gpioa: &G, spi1: &S)
//! where
//! G: Resource<Data = GPIOA>,
//! S: Resource<Data = SPI1>,
//! {
//! gpioa.claim(t, |_gpioa, t| {
//! // drive NSS low
//!
//! spi1.claim(t, |_spi1, _| {
//! // transfer data
//! });
//!
//! // drive NSS high
//! });
//! }
//!
//! task!(EXTI0, exti0);
//!
//! // this task needs critical sections to access the resources
//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
//! work(t, &r.GPIOA, &r.SPI1);
//! }
//!
//! task!(EXTI1, exti1);
//!
//! // this task has direct access to the resources
//! fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
//! work(t, r.GPIOA, r.SPI1);
//! }
//! ```
// Auto-generated. Do not modify.

View file

@ -0,0 +1,85 @@
//! A showcase of the `app!` macro syntax
//!
//! ```
//!
//! #![deny(unsafe_code)]
//! #![feature(const_fn)]
//! #![feature(proc_macro)]
//! #![no_std]
//!
//! #[macro_use(task)]
//! extern crate cortex_m_rtfm as rtfm;
//! extern crate stm32f103xx;
//!
//! use rtfm::{app, Resource, Threshold};
//!
//! app! {
//! device: stm32f103xx,
//!
//! resources: {
//! static CO_OWNED: u32 = 0;
//! static OWNED: bool = false;
//! static SHARED: bool = false;
//! },
//!
//! init: {
//! path: init_, // this is a path to the "init" function
//! },
//!
//! idle: {
//! locals: {
//! static COUNTER: u32 = 0;
//! },
//! path: idle_, // this is a path to the "idle" function
//! resources: [OWNED, SHARED],
//! },
//!
//! tasks: {
//! SYS_TICK: {
//! priority: 1,
//! resources: [CO_OWNED, SHARED],
//! },
//!
//! TIM2: {
//! enabled: true,
//! priority: 1,
//! resources: [CO_OWNED],
//! },
//! },
//! }
//!
//! fn init_(_p: init::Peripherals, _r: init::Resources) {}
//!
//! fn idle_(t: &mut Threshold, l: &mut idle::Locals, mut r: idle::Resources) -> ! {
//! loop {
//! *l.COUNTER += 1;
//!
//! **r.OWNED != **r.OWNED;
//!
//! if **r.OWNED {
//! if r.SHARED.claim(t, |shared, _| **shared) {
//! rtfm::wfi();
//! }
//! } else {
//! r.SHARED.claim_mut(t, |shared, _| **shared = !**shared);
//! }
//! }
//! }
//!
//! task!(SYS_TICK, sys_tick, Local {
//! static STATE: bool = true;
//! });
//!
//! fn sys_tick(_t: &mut Threshold, l: &mut Local, r: SYS_TICK::Resources) {
//! *l.STATE = !*l.STATE;
//!
//! **r.CO_OWNED += 1;
//! }
//!
//! task!(TIM2, tim2);
//!
//! fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
//! **r.CO_OWNED += 1;
//! }
//! ```
// Auto-generated. Do not modify.

9
src/examples/mod.rs Normal file
View file

@ -0,0 +1,9 @@
//! Examples
// Auto-generated. Do not modify.
pub mod _0_zero_tasks;
pub mod _1_one_task;
pub mod _2_two_tasks;
pub mod _3_preemption;
pub mod _4_nested;
pub mod _5_generics;
pub mod _6_full_syntax;

View file

@ -1,3 +1,57 @@
//! Real Time For the Masses (RTFM), a framework for building concurrent
//! applications, for ARM Cortex-M microcontrollers
//!
//! This crate is based on [the RTFM framework] created by the Embedded Systems
//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren,
//! and uses a simplified version of the Stack Resource Policy as scheduling
//! policy (check the [references] for details).
//!
//! [the RTFM framework]: http://www.rtfm-lang.org/
//! [ltu]: https://www.ltu.se/?l=en
//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
//! [references]: ./index.html#references
//!
//! # Features
//!
//! - **Event triggered tasks** as the unit of concurrency.
//! - Support for prioritization of tasks and, thus, **preemptive
//! multitasking**.
//! - **Efficient and data race free memory sharing** through fine grained *non
//! global* critical sections.
//! - **Deadlock free execution** guaranteed at compile time.
//! - **Minimal scheduling overhead** as the scheduler has no "software
//! component": the hardware does all the scheduling.
//! - **Highly efficient memory usage**: All the tasks share a single call stack
//! and there's no hard dependency on a dynamic memory allocator.
//! - **All Cortex M devices are fully supported**.
//! - This task model is amenable to known WCET (Worst Case Execution Time)
//! analysis and scheduling analysis techniques. (Though we haven't yet
//! developed Rust friendly tooling for that.)
//!
//! # Constraints
//!
//! - Tasks must run to completion. That's it, tasks can't contain endless
//! loops. However, you can run an endless event loop in the `idle` function.
//!
//! - Task priorities must remain constant at runtime.
//!
//! # Dependencies
//!
//! - A device crate generated using [`svd2rust`] v0.11.x. The input SVD file
//! *must* contain [`<cpu>`] information.
//! - A `start` lang time: Vanilla `main` must be supported in binary crates.
//! You can use the [`cortex-m-rt`] crate to fulfill the requirement
//!
//! [`svd2rust`]: https://docs.rs/svd2rust/0..0/svd2rust/
//! [`<cpu>`]: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_cpu.html
//! [`cortex-m-rt`]: https://docs.rs/cortex-m-rt/0.3.0/cortex_m_rt/
//!
//! # Examples
//!
//! In increasing grade of complexity: [examples](./examples/index.html)
#![deny(missing_docs)]
#![deny(warnings)]
#![feature(asm)]
#![feature(const_fn)]
#![feature(optin_builtin_traits)]
@ -17,11 +71,73 @@ pub use cortex_m::interrupt::free as atomic;
pub use static_ref::Static;
use cortex_m::interrupt::Nr;
#[cfg(not(armv6m))]
use cortex_m::register::{basepri_max, basepri};
use cortex_m::register::{basepri, basepri_max};
#[inline(always)]
unsafe fn claim<T, U, R, F, G>(
data: T,
pub mod examples;
/// A resource, a means to share data between tasks
pub trait Resource {
/// The data protected by the resource
type Data;
/// Borrows the resource data for the duration of a *global* critical
/// section
fn borrow<'cs>(
&'cs self,
cs: &'cs CriticalSection,
) -> &'cs Static<Self::Data>;
/// Mutable variant of `borrow`
fn borrow_mut<'cs>(
&'cs mut self,
cs: &'cs CriticalSection,
) -> &'cs mut Static<Self::Data>;
/// Claims the resource data for the span of the closure `f`. For the
/// duration of the closure other tasks that may access the resource data
/// are prevented from preempting the current task.
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
where
F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R;
/// Mutable variant of `claim`
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
where
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R;
}
impl<T> Resource for Static<T> {
type Data = T;
fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static<T> {
self
}
fn borrow_mut<'cs>(
&'cs mut self,
_cs: &'cs CriticalSection,
) -> &'cs mut Static<T> {
self
}
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
where
F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R,
{
f(self, t)
}
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
where
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R,
{
f(self, t)
}
}
#[doc(hidden)]
pub unsafe fn claim<T, U, R, F, G>(
data: *mut T,
ceiling: u8,
nvic_prio_bits: u8,
t: &mut Threshold,
@ -30,10 +146,10 @@ unsafe fn claim<T, U, R, F, G>(
) -> R
where
F: FnOnce(U, &mut Threshold) -> R,
G: FnOnce(T) -> U,
G: FnOnce(*mut T) -> U,
{
let max_priority = 1 << nvic_prio_bits;
if ceiling > t.0 {
if ceiling > t.value {
match () {
#[cfg(armv6m)]
() => {
@ -47,7 +163,7 @@ where
let old = basepri::read();
let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
basepri_max::write(hw);
let ret = f(g(data), &mut Threshold(ceiling));
let ret = f(g(data), &mut Threshold::new(ceiling));
basepri::write(old);
ret
}
@ -58,139 +174,44 @@ where
}
}
pub struct Peripheral<P>
where
P: 'static,
{
// FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
// claims (the ceiling value gets loaded at runtime rather than inlined)
// ceiling: u8,
peripheral: cortex_m::peripheral::Peripheral<P>,
}
impl<P> Peripheral<P> {
pub const fn new(peripheral: cortex_m::peripheral::Peripheral<P>) -> Self {
Peripheral { peripheral }
}
#[inline(always)]
pub unsafe fn borrow<'cs>(
&'static self,
_cs: &'cs CriticalSection,
) -> &'cs P {
&*self.peripheral.get()
}
#[inline(always)]
pub unsafe fn claim<R, F>(
&'static self,
ceiling: u8,
nvic_prio_bits: u8,
t: &mut Threshold,
f: F,
) -> R
where
F: FnOnce(&P, &mut Threshold) -> R,
{
claim(
&self.peripheral,
ceiling,
nvic_prio_bits,
t,
f,
|peripheral| &*peripheral.get(),
)
}
pub fn get(&self) -> *mut P {
self.peripheral.get()
}
}
unsafe impl<P> Sync for Peripheral<P>
where
P: Send,
{
}
pub struct Resource<T> {
// FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
// claims (the ceiling value gets loaded at runtime rather than inlined)
// ceiling: u8,
#[doc(hidden)]
pub struct Cell<T> {
data: UnsafeCell<T>,
}
impl<T> Resource<T> {
pub const fn new(value: T) -> Self {
Resource {
data: UnsafeCell::new(value),
#[doc(hidden)]
impl<T> Cell<T> {
pub const fn new(data: T) -> Self {
Cell {
data: UnsafeCell::new(data),
}
}
#[inline(always)]
pub unsafe fn borrow<'cs>(
&'static self,
_cs: &'cs CriticalSection,
) -> &'cs Static<T> {
Static::ref_(&*self.data.get())
}
#[inline(always)]
pub unsafe fn borrow_mut<'cs>(
&'static self,
_cs: &'cs CriticalSection,
) -> &'cs mut Static<T> {
Static::ref_mut(&mut *self.data.get())
}
#[inline(always)]
pub unsafe fn claim<R, F>(
&'static self,
ceiling: u8,
nvic_prio_bits: u8,
t: &mut Threshold,
f: F,
) -> R
where
F: FnOnce(&Static<T>, &mut Threshold) -> R,
{
claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
Static::ref_(&*data.get())
})
}
#[inline(always)]
pub unsafe fn claim_mut<R, F>(
&'static self,
ceiling: u8,
nvic_prio_bits: u8,
t: &mut Threshold,
f: F,
) -> R
where
F: FnOnce(&mut Static<T>, &mut Threshold) -> R,
{
claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
Static::ref_mut(&mut *data.get())
})
}
pub fn get(&self) -> *mut T {
self.data.get()
}
}
unsafe impl<T> Sync for Resource<T>
unsafe impl<T> Sync for Cell<T>
where
T: Send,
{
}
pub struct Threshold(u8);
/// Preemption threshold token
///
/// The preemption threshold indicates the priority a task must have to preempt
/// the current context. For example a threshold of 2 indicates that only
/// interrupts / exceptions with a priority of 3 or greater can preempt the
/// current context
pub struct Threshold {
value: u8,
}
impl Threshold {
#[doc(hidden)]
pub unsafe fn new(value: u8) -> Self {
Threshold(value)
Threshold { value }
}
}
@ -206,14 +227,15 @@ where
nvic.set_pending(interrupt);
}
/// Binds a task `$handler` to the interrupt / exception `$NAME`
#[macro_export]
macro_rules! task {
($NAME:ident, $body:path) => {
($NAME:ident, $handler:path) => {
#[allow(non_snake_case)]
#[allow(unsafe_code)]
#[no_mangle]
pub unsafe extern "C" fn $NAME() {
let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $body;
let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $handler;
f(
&mut $crate::Threshold::new(::$NAME::$NAME),
@ -221,11 +243,13 @@ macro_rules! task {
);
}
};
($NAME:ident, $body:path, $local:ident {
$($var:ident: $ty:ty = $expr:expr;)+
($NAME:ident, $handler:path, $locals:ident {
$(static $var:ident: $ty:ty = $expr:expr;)+
}) => {
struct $local {
$($var: $ty,)+
#[allow(non_snake_case)]
struct $locals {
$($var: $crate::Static<$ty>,)+
}
#[allow(non_snake_case)]
@ -234,17 +258,17 @@ macro_rules! task {
pub unsafe extern "C" fn $NAME() {
let f: fn(
&mut $crate::Threshold,
&mut $local,
&mut $locals,
::$NAME::Resources,
) = $body;
) = $handler;
static mut LOCAL: $local = $local {
$($var: $expr,)+
static mut LOCALS: $locals = $locals {
$($var: unsafe { $crate::Static::new($expr) },)+
};
f(
&mut $crate::Threshold::new(::$NAME::$NAME),
&mut LOCAL,
&mut LOCALS,
::$NAME::Resources::new(),
);
}