mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-25 19:39:32 +01:00
Merge #390
390: Spawn and schedule from anywhere r=AfoHT a=korken89 This PR moves RTIC to the spawn and schedule from anywhere syntax. Notable changes: * We do no longer support non-`Send` types. * Some extra code is generated as any task may spawn/schedule any task. However Rust/LLVM does a great job optimizing away non used instantiations (no real code-size difference observed). * Worst case priority inversion has increased, but it is now predictable. Upsides: * With this we should be able to support async/await. * RTIC tasks can now be callbacks (spawned and scheduled). * RTIC tasks can be stored. Needs the following PR to land first: https://github.com/rtic-rs/rtic-syntax/pull/34 The following now works: ```rust #[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)] mod app { #[init] fn init(mut cx: init::Context) -> init::LateResources { // Init stuff... // New spawn syntax foo::spawn().unwrap(); // New schedule syntax bar::schedule(now + 4_000_000.cycles()).unwrap(); init::LateResources {} } #[task] fn foo(_: foo::Context) {} #[task] fn bar(_: bar::Context) {} extern "C" { fn SSI0(); } } ``` Co-authored-by: Per Lindgren <per.lindgren@ltu.se> Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
This commit is contained in:
commit
355cb82d06
37 changed files with 415 additions and 891 deletions
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
|
@ -251,7 +251,6 @@ jobs:
|
||||||
capacity
|
capacity
|
||||||
|
|
||||||
types
|
types
|
||||||
not-send
|
|
||||||
not-sync
|
not-sync
|
||||||
shared-with-init
|
shared-with-init
|
||||||
|
|
||||||
|
|
|
@ -12,19 +12,19 @@ use panic_semihosting as _;
|
||||||
// NOTE: does NOT properly work on QEMU
|
// NOTE: does NOT properly work on QEMU
|
||||||
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(spawn = [foo])]
|
#[init]
|
||||||
fn init(cx: init::Context) -> init::LateResources {
|
fn init(cx: init::Context) -> init::LateResources {
|
||||||
// omitted: initialization of `CYCCNT`
|
// omitted: initialization of `CYCCNT`
|
||||||
|
|
||||||
hprintln!("init(baseline = {:?})", cx.start).unwrap();
|
hprintln!("init(baseline = {:?})", cx.start).unwrap();
|
||||||
|
|
||||||
// `foo` inherits the baseline of `init`: `Instant(0)`
|
// `foo` inherits the baseline of `init`: `Instant(0)`
|
||||||
cx.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(schedule = [foo])]
|
#[task]
|
||||||
fn foo(cx: foo::Context) {
|
fn foo(cx: foo::Context) {
|
||||||
static mut ONCE: bool = true;
|
static mut ONCE: bool = true;
|
||||||
|
|
||||||
|
@ -39,12 +39,12 @@ mod app {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = UART0, spawn = [foo])]
|
#[task(binds = UART0)]
|
||||||
fn uart0(cx: uart0::Context) {
|
fn uart0(cx: uart0::Context) {
|
||||||
hprintln!("UART0(baseline = {:?})", cx.start).unwrap();
|
hprintln!("UART0(baseline = {:?})", cx.start).unwrap();
|
||||||
|
|
||||||
// `foo` inherits the baseline of `UART0`: its `start` time
|
// `foo` inherits the baseline of `UART0`: its `start` time
|
||||||
cx.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTIC requires that unused interrupts are declared in an extern block when
|
// RTIC requires that unused interrupts are declared in an extern block when
|
||||||
|
|
|
@ -18,14 +18,14 @@ mod app {
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = UART0, spawn = [foo, bar])]
|
#[task(binds = UART0)]
|
||||||
fn uart0(c: uart0::Context) {
|
fn uart0(_: uart0::Context) {
|
||||||
c.spawn.foo(0).unwrap();
|
foo::spawn(0).unwrap();
|
||||||
c.spawn.foo(1).unwrap();
|
foo::spawn(1).unwrap();
|
||||||
c.spawn.foo(2).unwrap();
|
foo::spawn(2).unwrap();
|
||||||
c.spawn.foo(3).unwrap();
|
foo::spawn(3).unwrap();
|
||||||
|
|
||||||
c.spawn.bar().unwrap();
|
bar::spawn().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(capacity = 4)]
|
#[task(capacity = 4)]
|
||||||
|
|
|
@ -19,10 +19,10 @@ mod app {
|
||||||
count: u32,
|
count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[init(spawn = [foo])]
|
#[init]
|
||||||
fn init(cx: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
cx.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
cx.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ mod app {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(capacity = 2, resources = [count], spawn = [log])]
|
#[task(capacity = 2, resources = [count])]
|
||||||
fn foo(_cx: foo::Context) {
|
fn foo(_cx: foo::Context) {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
*_cx.resources.count += 1;
|
*_cx.resources.count += 1;
|
||||||
|
|
||||||
_cx.spawn.log(*_cx.resources.count).unwrap();
|
log::spawn(*_cx.resources.count).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this wouldn't compile in `release` mode
|
// this wouldn't compile in `release` mode
|
||||||
|
|
|
@ -16,21 +16,21 @@ mod app {
|
||||||
nothing: (),
|
nothing: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[init(spawn = [task1])]
|
#[init]
|
||||||
fn init(cx: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
cx.spawn.task1().ok();
|
task1::spawn().ok();
|
||||||
|
|
||||||
init::LateResources { nothing: () }
|
init::LateResources { nothing: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(schedule = [task2])]
|
#[task]
|
||||||
fn task1(_cx: task1::Context) {
|
fn task1(_cx: task1::Context) {
|
||||||
_cx.schedule.task2(_cx.scheduled + 100.cycles()).ok();
|
task2::schedule(_cx.scheduled + 100.cycles()).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(schedule = [task1])]
|
#[task]
|
||||||
fn task2(_cx: task2::Context) {
|
fn task2(_cx: task2::Context) {
|
||||||
_cx.schedule.task1(_cx.scheduled + 100.cycles()).ok();
|
task1::schedule(_cx.scheduled + 100.cycles()).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -10,39 +10,39 @@ use panic_semihosting as _;
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(spawn = [foo])]
|
#[init]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
c.spawn.foo(/* no message */).unwrap();
|
foo::spawn(/* no message */).unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(spawn = [bar])]
|
#[task]
|
||||||
fn foo(c: foo::Context) {
|
fn foo(_: foo::Context) {
|
||||||
static mut COUNT: u32 = 0;
|
static mut COUNT: u32 = 0;
|
||||||
|
|
||||||
hprintln!("foo").unwrap();
|
hprintln!("foo").unwrap();
|
||||||
|
|
||||||
c.spawn.bar(*COUNT).unwrap();
|
bar::spawn(*COUNT).unwrap();
|
||||||
*COUNT += 1;
|
*COUNT += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(spawn = [baz])]
|
#[task]
|
||||||
fn bar(c: bar::Context, x: u32) {
|
fn bar(_: bar::Context, x: u32) {
|
||||||
hprintln!("bar({})", x).unwrap();
|
hprintln!("bar({})", x).unwrap();
|
||||||
|
|
||||||
c.spawn.baz(x + 1, x + 2).unwrap();
|
baz::spawn(x + 1, x + 2).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(spawn = [foo])]
|
#[task]
|
||||||
fn baz(c: baz::Context, x: u32, y: u32) {
|
fn baz(_: baz::Context, x: u32, y: u32) {
|
||||||
hprintln!("baz({}, {})", x, y).unwrap();
|
hprintln!("baz({}, {})", x, y).unwrap();
|
||||||
|
|
||||||
if x + y > 4 {
|
if x + y > 4 {
|
||||||
debug::exit(debug::EXIT_SUCCESS);
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTIC requires that unused interrupts are declared in an extern block when
|
// RTIC requires that unused interrupts are declared in an extern block when
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
//! `examples/not-send.rs`
|
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
use cortex_m_semihosting::debug;
|
|
||||||
use panic_halt as _;
|
|
||||||
use rtic::app;
|
|
||||||
|
|
||||||
pub struct NotSend {
|
|
||||||
_0: PhantomData<*const ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[app(device = lm3s6965)]
|
|
||||||
mod app {
|
|
||||||
use super::NotSend;
|
|
||||||
|
|
||||||
#[resources]
|
|
||||||
struct Resources {
|
|
||||||
#[init(None)]
|
|
||||||
shared: Option<NotSend>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[init(spawn = [baz, quux])]
|
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
|
||||||
c.spawn.baz().unwrap();
|
|
||||||
c.spawn.quux().unwrap();
|
|
||||||
|
|
||||||
init::LateResources {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(spawn = [bar])]
|
|
||||||
fn foo(c: foo::Context) {
|
|
||||||
// scenario 1: message passed to task that runs at the same priority
|
|
||||||
c.spawn.bar(NotSend { _0: PhantomData }).ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task]
|
|
||||||
fn bar(_: bar::Context, _x: NotSend) {
|
|
||||||
// scenario 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(priority = 2, resources = [shared])]
|
|
||||||
fn baz(c: baz::Context) {
|
|
||||||
// scenario 2: resource shared between tasks that run at the same priority
|
|
||||||
*c.resources.shared = Some(NotSend { _0: PhantomData });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task(priority = 2, resources = [shared])]
|
|
||||||
fn quux(c: quux::Context) {
|
|
||||||
// scenario 2
|
|
||||||
let _not_send = c.resources.shared.take().unwrap();
|
|
||||||
|
|
||||||
debug::exit(debug::EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// RTIC requires that unused interrupts are declared in an extern block when
|
|
||||||
// using software tasks; these free interrupts will be used to dispatch the
|
|
||||||
// software tasks.
|
|
||||||
extern "C" {
|
|
||||||
fn SSI0();
|
|
||||||
fn QEI0();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,21 +15,21 @@ const PERIOD: u32 = 8_000_000;
|
||||||
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
mod app {
|
mod app {
|
||||||
|
|
||||||
#[init(schedule = [foo])]
|
#[init]
|
||||||
fn init(cx: init::Context) -> init::LateResources {
|
fn init(cx: init::Context) -> init::LateResources {
|
||||||
// omitted: initialization of `CYCCNT`
|
// omitted: initialization of `CYCCNT`
|
||||||
|
|
||||||
cx.schedule.foo(cx.start + PERIOD.cycles()).unwrap();
|
foo::schedule(cx.start + PERIOD.cycles()).unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(schedule = [foo])]
|
#[task]
|
||||||
fn foo(cx: foo::Context) {
|
fn foo(cx: foo::Context) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
hprintln!("foo(scheduled = {:?}, now = {:?})", cx.scheduled, now).unwrap();
|
hprintln!("foo(scheduled = {:?}, now = {:?})", cx.scheduled, now).unwrap();
|
||||||
|
|
||||||
cx.schedule.foo(cx.scheduled + PERIOD.cycles()).unwrap();
|
foo::schedule(cx.scheduled + PERIOD.cycles()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTIC requires that unused interrupts are declared in an extern block when
|
// RTIC requires that unused interrupts are declared in an extern block when
|
||||||
|
|
|
@ -36,16 +36,16 @@ mod app {
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = I2C0, priority = 2, spawn = [foo, bar])]
|
#[task(binds = I2C0, priority = 2)]
|
||||||
fn i2c0(c: i2c0::Context) {
|
fn i2c0(_: i2c0::Context) {
|
||||||
// claim a memory block, leave it uninitialized and ..
|
// claim a memory block, leave it uninitialized and ..
|
||||||
let x = P::alloc().unwrap().freeze();
|
let x = P::alloc().unwrap().freeze();
|
||||||
|
|
||||||
// .. send it to the `foo` task
|
// .. send it to the `foo` task
|
||||||
c.spawn.foo(x).ok().unwrap();
|
foo::spawn(x).ok().unwrap();
|
||||||
|
|
||||||
// send another block to the task `bar`
|
// send another block to the task `bar`
|
||||||
c.spawn.bar(P::alloc().unwrap().freeze()).ok().unwrap();
|
bar::spawn(P::alloc().unwrap().freeze()).ok().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
|
|
|
@ -10,9 +10,9 @@ use panic_semihosting as _;
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(spawn = [bar])]
|
#[init]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
c.spawn.bar().unwrap();
|
foo::spawn().unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,9 @@ mod app {
|
||||||
// run this task from RAM
|
// run this task from RAM
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[link_section = ".data.bar"]
|
#[link_section = ".data.bar"]
|
||||||
#[task(priority = 2, spawn = [foo])]
|
#[task(priority = 2)]
|
||||||
fn bar(c: bar::Context) {
|
fn bar(_: bar::Context) {
|
||||||
c.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rtic::cyccnt::{Instant, U32Ext as _};
|
||||||
// NOTE: does NOT work on QEMU!
|
// NOTE: does NOT work on QEMU!
|
||||||
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(schedule = [foo, bar])]
|
#[init()]
|
||||||
fn init(mut cx: init::Context) -> init::LateResources {
|
fn init(mut cx: init::Context) -> init::LateResources {
|
||||||
// Initialize (enable) the monotonic timer (CYCCNT)
|
// Initialize (enable) the monotonic timer (CYCCNT)
|
||||||
cx.core.DCB.enable_trace();
|
cx.core.DCB.enable_trace();
|
||||||
|
@ -28,10 +28,10 @@ mod app {
|
||||||
hprintln!("init @ {:?}", now).unwrap();
|
hprintln!("init @ {:?}", now).unwrap();
|
||||||
|
|
||||||
// Schedule `foo` to run 8e6 cycles (clock cycles) in the future
|
// Schedule `foo` to run 8e6 cycles (clock cycles) in the future
|
||||||
cx.schedule.foo(now + 8_000_000.cycles()).unwrap();
|
foo::schedule(now + 8_000_000.cycles()).unwrap();
|
||||||
|
|
||||||
// Schedule `bar` to run 4e6 cycles in the future
|
// Schedule `bar` to run 4e6 cycles in the future
|
||||||
cx.schedule.bar(now + 4_000_000.cycles()).unwrap();
|
bar::schedule(now + 4_000_000.cycles()).unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
35
examples/spawn.rs
Normal file
35
examples/spawn.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//! examples/spawn.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_semihosting::{debug, hprintln};
|
||||||
|
use panic_semihosting as _;
|
||||||
|
|
||||||
|
#[rtic::app(device = lm3s6965)]
|
||||||
|
mod app {
|
||||||
|
#[init]
|
||||||
|
fn init(_c: init::Context) -> init::LateResources {
|
||||||
|
foo::spawn(1, 2).unwrap();
|
||||||
|
|
||||||
|
init::LateResources {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task()]
|
||||||
|
fn foo(_c: foo::Context, x: i32, y: u32) {
|
||||||
|
hprintln!("foo {}, {}", x, y).unwrap();
|
||||||
|
if x == 2 {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
foo::spawn(2, 3).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTIC requires that unused interrupts are declared in an extern block when
|
||||||
|
// using software tasks; these free interrupts will be used to dispatch the
|
||||||
|
// software tasks.
|
||||||
|
extern "C" {
|
||||||
|
fn SSI0();
|
||||||
|
}
|
||||||
|
}
|
41
examples/spawn2.rs
Normal file
41
examples/spawn2.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//! examples/spawn2.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_semihosting::{debug, hprintln};
|
||||||
|
use panic_semihosting as _;
|
||||||
|
|
||||||
|
#[rtic::app(device = lm3s6965)]
|
||||||
|
mod app {
|
||||||
|
#[init]
|
||||||
|
fn init(_c: init::Context) -> init::LateResources {
|
||||||
|
foo::spawn(1, 2).unwrap();
|
||||||
|
|
||||||
|
init::LateResources {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn foo(_c: foo::Context, x: i32, y: u32) {
|
||||||
|
hprintln!("foo {}, {}", x, y).unwrap();
|
||||||
|
if x == 2 {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
foo2::spawn(2).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn foo2(_c: foo2::Context, x: i32) {
|
||||||
|
hprintln!("foo2 {}", x).unwrap();
|
||||||
|
foo::spawn(x, 0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTIC requires that unused interrupts are declared in an extern block when
|
||||||
|
// using software tasks; these free interrupts will be used to dispatch the
|
||||||
|
// software tasks.
|
||||||
|
extern "C" {
|
||||||
|
fn SSI0();
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,13 +32,13 @@ mod app {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(resources = [foo], schedule = [quux], spawn = [quux])]
|
#[task(resources = [foo])]
|
||||||
fn foo(_: foo::Context) {
|
fn foo(_: foo::Context) {
|
||||||
#[cfg(never)]
|
#[cfg(never)]
|
||||||
static mut BAR: u32 = 0;
|
static mut BAR: u32 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(priority = 3, resources = [foo], schedule = [quux], spawn = [quux])]
|
#[task(priority = 3, resources = [foo])]
|
||||||
fn bar(_: bar::Context) {
|
fn bar(_: bar::Context) {
|
||||||
#[cfg(never)]
|
#[cfg(never)]
|
||||||
static mut BAR: u32 = 0;
|
static mut BAR: u32 = 0;
|
||||||
|
|
|
@ -10,45 +10,45 @@ use rtic::cyccnt::{Instant, U32Ext as _};
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(schedule = [foo, bar, baz])]
|
#[init]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(c: init::Context) -> init::LateResources {
|
||||||
let _: Result<(), ()> = c.schedule.foo(c.start + 10.cycles());
|
let _: Result<(), ()> = foo::schedule(c.start + 10.cycles());
|
||||||
let _: Result<(), u32> = c.schedule.bar(c.start + 20.cycles(), 0);
|
let _: Result<(), u32> = bar::schedule(c.start + 20.cycles(), 0);
|
||||||
let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 30.cycles(), 0, 1);
|
let _: Result<(), (u32, u32)> = baz::schedule(c.start + 30.cycles(), 0, 1);
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(schedule = [foo, bar, baz])]
|
#[idle]
|
||||||
fn idle(c: idle::Context) -> ! {
|
fn idle(_: idle::Context) -> ! {
|
||||||
let _: Result<(), ()> = c.schedule.foo(Instant::now() + 40.cycles());
|
let _: Result<(), ()> = foo::schedule(Instant::now() + 40.cycles());
|
||||||
let _: Result<(), u32> = c.schedule.bar(Instant::now() + 50.cycles(), 0);
|
let _: Result<(), u32> = bar::schedule(Instant::now() + 50.cycles(), 0);
|
||||||
let _: Result<(), (u32, u32)> = c.schedule.baz(Instant::now() + 60.cycles(), 0, 1);
|
let _: Result<(), (u32, u32)> = baz::schedule(Instant::now() + 60.cycles(), 0, 1);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = SVCall, schedule = [foo, bar, baz])]
|
#[task(binds = SVCall)]
|
||||||
fn svcall(c: svcall::Context) {
|
fn svcall(c: svcall::Context) {
|
||||||
let _: Result<(), ()> = c.schedule.foo(c.start + 70.cycles());
|
let _: Result<(), ()> = foo::schedule(c.start + 70.cycles());
|
||||||
let _: Result<(), u32> = c.schedule.bar(c.start + 80.cycles(), 0);
|
let _: Result<(), u32> = bar::schedule(c.start + 80.cycles(), 0);
|
||||||
let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 90.cycles(), 0, 1);
|
let _: Result<(), (u32, u32)> = baz::schedule(c.start + 90.cycles(), 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = UART0, schedule = [foo, bar, baz])]
|
#[task(binds = UART0)]
|
||||||
fn uart0(c: uart0::Context) {
|
fn uart0(c: uart0::Context) {
|
||||||
let _: Result<(), ()> = c.schedule.foo(c.start + 100.cycles());
|
let _: Result<(), ()> = foo::schedule(c.start + 100.cycles());
|
||||||
let _: Result<(), u32> = c.schedule.bar(c.start + 110.cycles(), 0);
|
let _: Result<(), u32> = bar::schedule(c.start + 110.cycles(), 0);
|
||||||
let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 120.cycles(), 0, 1);
|
let _: Result<(), (u32, u32)> = baz::schedule(c.start + 120.cycles(), 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(schedule = [foo, bar, baz])]
|
#[task]
|
||||||
fn foo(c: foo::Context) {
|
fn foo(c: foo::Context) {
|
||||||
let _: Result<(), ()> = c.schedule.foo(c.scheduled + 130.cycles());
|
let _: Result<(), ()> = foo::schedule(c.scheduled + 130.cycles());
|
||||||
let _: Result<(), u32> = c.schedule.bar(c.scheduled + 140.cycles(), 0);
|
let _: Result<(), u32> = bar::schedule(c.scheduled + 140.cycles(), 0);
|
||||||
let _: Result<(), (u32, u32)> = c.schedule.baz(c.scheduled + 150.cycles(), 0, 1);
|
let _: Result<(), (u32, u32)> = baz::schedule(c.scheduled + 150.cycles(), 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
|
|
|
@ -9,45 +9,45 @@ use panic_halt as _;
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(spawn = [foo, bar, baz])]
|
#[init]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
let _: Result<(), ()> = c.spawn.foo();
|
let _: Result<(), ()> = foo::spawn();
|
||||||
let _: Result<(), u32> = c.spawn.bar(0);
|
let _: Result<(), u32> = bar::spawn(0);
|
||||||
let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
|
let _: Result<(), (u32, u32)> = baz::spawn(0, 1);
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(spawn = [foo, bar, baz])]
|
#[idle]
|
||||||
fn idle(c: idle::Context) -> ! {
|
fn idle(_: idle::Context) -> ! {
|
||||||
let _: Result<(), ()> = c.spawn.foo();
|
let _: Result<(), ()> = foo::spawn();
|
||||||
let _: Result<(), u32> = c.spawn.bar(0);
|
let _: Result<(), u32> = bar::spawn(0);
|
||||||
let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
|
let _: Result<(), (u32, u32)> = baz::spawn(0, 1);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = SVCall, spawn = [foo, bar, baz])]
|
#[task(binds = SVCall)]
|
||||||
fn svcall(c: svcall::Context) {
|
fn svcall(_: svcall::Context) {
|
||||||
let _: Result<(), ()> = c.spawn.foo();
|
let _: Result<(), ()> = foo::spawn();
|
||||||
let _: Result<(), u32> = c.spawn.bar(0);
|
let _: Result<(), u32> = bar::spawn(0);
|
||||||
let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
|
let _: Result<(), (u32, u32)> = baz::spawn(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = UART0, spawn = [foo, bar, baz])]
|
#[task(binds = UART0)]
|
||||||
fn uart0(c: uart0::Context) {
|
fn uart0(_: uart0::Context) {
|
||||||
let _: Result<(), ()> = c.spawn.foo();
|
let _: Result<(), ()> = foo::spawn();
|
||||||
let _: Result<(), u32> = c.spawn.bar(0);
|
let _: Result<(), u32> = bar::spawn(0);
|
||||||
let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
|
let _: Result<(), (u32, u32)> = baz::spawn(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(spawn = [foo, bar, baz])]
|
#[task]
|
||||||
fn foo(c: foo::Context) {
|
fn foo(_: foo::Context) {
|
||||||
let _: Result<(), ()> = c.spawn.foo();
|
let _: Result<(), ()> = foo::spawn();
|
||||||
let _: Result<(), u32> = c.spawn.bar(0);
|
let _: Result<(), u32> = bar::spawn(0);
|
||||||
let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
|
let _: Result<(), (u32, u32)> = baz::spawn(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
|
|
|
@ -8,9 +8,9 @@ use panic_semihosting as _;
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(spawn = [taskmain])]
|
#[init]
|
||||||
fn init(cx: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
cx.spawn.taskmain().ok();
|
taskmain::spawn().ok();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,27 +10,27 @@ use panic_semihosting as _;
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965)]
|
||||||
mod app {
|
mod app {
|
||||||
#[init(spawn = [foo])]
|
#[init]
|
||||||
fn init(c: init::Context) -> init::LateResources {
|
fn init(_: init::Context) -> init::LateResources {
|
||||||
c.spawn.foo().unwrap();
|
foo::spawn().unwrap();
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(spawn = [bar, baz])]
|
#[task]
|
||||||
fn foo(c: foo::Context) {
|
fn foo(_: foo::Context) {
|
||||||
hprintln!("foo - start").unwrap();
|
hprintln!("foo - start").unwrap();
|
||||||
|
|
||||||
// spawns `bar` onto the task scheduler
|
// spawns `bar` onto the task scheduler
|
||||||
// `foo` and `bar` have the same priority so `bar` will not run until
|
// `foo` and `bar` have the same priority so `bar` will not run until
|
||||||
// after `foo` terminates
|
// after `foo` terminates
|
||||||
c.spawn.bar().unwrap();
|
bar::spawn().unwrap();
|
||||||
|
|
||||||
hprintln!("foo - middle").unwrap();
|
hprintln!("foo - middle").unwrap();
|
||||||
|
|
||||||
// spawns `baz` onto the task scheduler
|
// spawns `baz` onto the task scheduler
|
||||||
// `baz` has higher priority than `foo` so it immediately preempts `foo`
|
// `baz` has higher priority than `foo` so it immediately preempts `foo`
|
||||||
c.spawn.baz().unwrap();
|
baz::spawn().unwrap();
|
||||||
|
|
||||||
hprintln!("foo - end").unwrap();
|
hprintln!("foo - end").unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,44 +17,35 @@ mod app {
|
||||||
shared: u32,
|
shared: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[init(schedule = [foo], spawn = [foo])]
|
#[init]
|
||||||
fn init(cx: init::Context) -> init::LateResources {
|
fn init(cx: init::Context) -> init::LateResources {
|
||||||
let _: cyccnt::Instant = cx.start;
|
let _: cyccnt::Instant = cx.start;
|
||||||
let _: rtic::Peripherals = cx.core;
|
let _: rtic::Peripherals = cx.core;
|
||||||
let _: lm3s6965::Peripherals = cx.device;
|
let _: lm3s6965::Peripherals = cx.device;
|
||||||
let _: init::Schedule = cx.schedule;
|
|
||||||
let _: init::Spawn = cx.spawn;
|
|
||||||
|
|
||||||
debug::exit(debug::EXIT_SUCCESS);
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
|
||||||
init::LateResources {}
|
init::LateResources {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(schedule = [foo], spawn = [foo])]
|
#[idle]
|
||||||
fn idle(cx: idle::Context) -> ! {
|
fn idle(_: idle::Context) -> ! {
|
||||||
let _: idle::Schedule = cx.schedule;
|
|
||||||
let _: idle::Spawn = cx.spawn;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = UART0, resources = [shared], schedule = [foo], spawn = [foo])]
|
#[task(binds = UART0, resources = [shared])]
|
||||||
fn uart0(cx: uart0::Context) {
|
fn uart0(cx: uart0::Context) {
|
||||||
let _: cyccnt::Instant = cx.start;
|
let _: cyccnt::Instant = cx.start;
|
||||||
let _: resources::shared = cx.resources.shared;
|
let _: resources::shared = cx.resources.shared;
|
||||||
let _: uart0::Schedule = cx.schedule;
|
|
||||||
let _: uart0::Spawn = cx.spawn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(priority = 2, resources = [shared], schedule = [foo], spawn = [foo])]
|
#[task(priority = 2, resources = [shared])]
|
||||||
fn foo(cx: foo::Context) {
|
fn foo(cx: foo::Context) {
|
||||||
let _: cyccnt::Instant = cx.scheduled;
|
let _: cyccnt::Instant = cx.scheduled;
|
||||||
let _: &mut u32 = cx.resources.shared;
|
let _: &mut u32 = cx.resources.shared;
|
||||||
let _: foo::Resources = cx.resources;
|
let _: foo::Resources = cx.resources;
|
||||||
let _: foo::Schedule = cx.schedule;
|
|
||||||
let _: foo::Spawn = cx.spawn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTIC requires that unused interrupts are declared in an extern block when
|
// RTIC requires that unused interrupts are declared in an extern block when
|
||||||
|
|
|
@ -29,7 +29,6 @@ pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> {
|
||||||
.software_tasks
|
.software_tasks
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|task| Some(task.args.priority))
|
.filter_map(|task| Some(task.args.priority))
|
||||||
.chain(analysis.timer_queues.first().map(|tq| tq.priority))
|
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
if !priorities.is_empty() {
|
if !priorities.is_empty() {
|
||||||
|
|
|
@ -19,35 +19,7 @@ impl<'a> Extra<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
pub fn app<'a>(app: &'a App, _analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
||||||
// Check that all exceptions are valid; only exceptions with configurable priorities are
|
|
||||||
// accepted
|
|
||||||
for (name, task) in &app.hardware_tasks {
|
|
||||||
let name_s = task.args.binds.to_string();
|
|
||||||
match &*name_s {
|
|
||||||
"SysTick" => {
|
|
||||||
// If the timer queue is used, then SysTick is unavailable
|
|
||||||
if !analysis.timer_queues.is_empty() {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
name.span(),
|
|
||||||
"this exception can't be used because it's being used by the runtime",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// OK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"NonMaskableInt" | "HardFault" => {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
name.span(),
|
|
||||||
"only exceptions with configurable priority can be used as hardware tasks",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that external (device-specific) interrupts are not named after known (Cortex-M)
|
// Check that external (device-specific) interrupts are not named after known (Cortex-M)
|
||||||
// exceptions
|
// exceptions
|
||||||
for name in app.extern_interrupts.keys() {
|
for name in app.extern_interrupts.keys() {
|
||||||
|
@ -76,7 +48,6 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
||||||
first = Some(name);
|
first = Some(name);
|
||||||
Some(task.args.priority)
|
Some(task.args.priority)
|
||||||
})
|
})
|
||||||
.chain(analysis.timer_queues.first().map(|tq| tq.priority))
|
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let need = priorities.len();
|
let need = priorities.len();
|
||||||
|
@ -141,11 +112,32 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !&analysis.timer_queues.is_empty() && monotonic.is_none() {
|
// Check that all exceptions are valid; only exceptions with configurable priorities are
|
||||||
return Err(parse::Error::new(
|
// accepted
|
||||||
Span::call_site(),
|
for (name, task) in &app.hardware_tasks {
|
||||||
"a `monotonic` timer must be specified to use the `schedule` API",
|
let name_s = task.args.binds.to_string();
|
||||||
));
|
match &*name_s {
|
||||||
|
"SysTick" => {
|
||||||
|
// If the timer queue is used, then SysTick is unavailable
|
||||||
|
if monotonic.is_some() {
|
||||||
|
return Err(parse::Error::new(
|
||||||
|
name.span(),
|
||||||
|
"this exception can't be used because it's being used by the runtime",
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"NonMaskableInt" | "HardFault" => {
|
||||||
|
return Err(parse::Error::new(
|
||||||
|
name.span(),
|
||||||
|
"only exceptions with configurable priority can be used as hardware tasks",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(device) = device {
|
if let Some(device) = device {
|
||||||
|
|
|
@ -15,11 +15,7 @@ mod post_init;
|
||||||
mod pre_init;
|
mod pre_init;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod resources_struct;
|
mod resources_struct;
|
||||||
mod schedule;
|
|
||||||
mod schedule_body;
|
|
||||||
mod software_tasks;
|
mod software_tasks;
|
||||||
mod spawn;
|
|
||||||
mod spawn_body;
|
|
||||||
mod timer_queue;
|
mod timer_queue;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
@ -115,15 +111,9 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
||||||
) = software_tasks::codegen(app, analysis, extra);
|
) = software_tasks::codegen(app, analysis, extra);
|
||||||
|
|
||||||
let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra);
|
let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra);
|
||||||
|
|
||||||
let mod_app_spawn = spawn::codegen(app, analysis, extra);
|
|
||||||
|
|
||||||
let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra);
|
let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra);
|
||||||
|
let user_imports = &app.user_imports;
|
||||||
let mod_app_schedule = schedule::codegen(app, extra);
|
let user_code = &app.user_code;
|
||||||
|
|
||||||
let user_imports = app.user_imports.clone();
|
|
||||||
let user_code = app.user_code.clone();
|
|
||||||
let name = &app.name;
|
let name = &app.name;
|
||||||
let device = extra.device;
|
let device = extra.device;
|
||||||
|
|
||||||
|
@ -187,12 +177,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
||||||
|
|
||||||
#(#mod_app_dispatchers)*
|
#(#mod_app_dispatchers)*
|
||||||
|
|
||||||
#(#mod_app_spawn)*
|
|
||||||
|
|
||||||
#(#mod_app_timer_queue)*
|
#(#mod_app_timer_queue)*
|
||||||
|
|
||||||
#(#mod_app_schedule)*
|
|
||||||
|
|
||||||
#(#mains)*
|
#(#mains)*
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
enum #t {
|
pub enum #t {
|
||||||
#(#variants,)*
|
#(#variants,)*
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
@ -57,27 +57,9 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
);
|
);
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
static mut #rq: #rq_ty = #rq_expr;
|
pub static mut #rq: #rq_ty = #rq_expr;
|
||||||
));
|
));
|
||||||
|
|
||||||
if let Some(ceiling) = channel.ceiling {
|
|
||||||
items.push(quote!(
|
|
||||||
struct #rq<'a> {
|
|
||||||
priority: &'a rtic::export::Priority,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
items.push(util::impl_mutex(
|
|
||||||
extra,
|
|
||||||
&[],
|
|
||||||
false,
|
|
||||||
&rq,
|
|
||||||
rq_ty,
|
|
||||||
ceiling,
|
|
||||||
quote!(&mut #rq),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let arms = channel
|
let arms = channel
|
||||||
.tasks
|
.tasks
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -88,7 +70,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
let inputs = util::inputs_ident(name);
|
let inputs = util::inputs_ident(name);
|
||||||
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
|
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
|
||||||
|
|
||||||
let (let_instant, instant) = if app.uses_schedule() {
|
let (let_instant, instant) = if extra.monotonic.is_some() {
|
||||||
let instants = util::instants_ident(name);
|
let instants = util::instants_ident(name);
|
||||||
|
|
||||||
(
|
(
|
||||||
|
|
|
@ -32,9 +32,7 @@ pub fn codegen(
|
||||||
let mut hardware_tasks_imports = vec![];
|
let mut hardware_tasks_imports = vec![];
|
||||||
|
|
||||||
for (name, task) in &app.hardware_tasks {
|
for (name, task) in &app.hardware_tasks {
|
||||||
let (let_instant, instant) = if app.uses_schedule() {
|
let (let_instant, instant) = if let Some(m) = extra.monotonic {
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
(
|
(
|
||||||
Some(quote!(let instant = <#m as rtic::Monotonic>::now();)),
|
Some(quote!(let instant = <#m as rtic::Monotonic>::now();)),
|
||||||
Some(quote!(, instant)),
|
Some(quote!(, instant)),
|
||||||
|
@ -97,6 +95,7 @@ pub fn codegen(
|
||||||
Context::HardwareTask(name),
|
Context::HardwareTask(name),
|
||||||
needs_lt,
|
needs_lt,
|
||||||
app,
|
app,
|
||||||
|
analysis,
|
||||||
extra,
|
extra,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,13 @@ pub fn codegen(
|
||||||
root_idle.push(locals);
|
root_idle.push(locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
root_idle.push(module::codegen(Context::Idle, needs_lt, app, extra));
|
root_idle.push(module::codegen(
|
||||||
|
Context::Idle,
|
||||||
|
needs_lt,
|
||||||
|
app,
|
||||||
|
analysis,
|
||||||
|
extra,
|
||||||
|
));
|
||||||
|
|
||||||
let attrs = &idle.attrs;
|
let attrs = &idle.attrs;
|
||||||
let context = &idle.context;
|
let context = &idle.context;
|
||||||
|
|
|
@ -116,7 +116,13 @@ pub fn codegen(
|
||||||
quote!(let late = crate::#name(#(#locals_new,)* #name::Context::new(core.into()));),
|
quote!(let late = crate::#name(#(#locals_new,)* #name::Context::new(core.into()));),
|
||||||
);
|
);
|
||||||
|
|
||||||
root_init.push(module::codegen(Context::Init, needs_lt, app, extra));
|
root_init.push(module::codegen(
|
||||||
|
Context::Init,
|
||||||
|
needs_lt,
|
||||||
|
app,
|
||||||
|
analysis,
|
||||||
|
extra,
|
||||||
|
));
|
||||||
|
|
||||||
(mod_app, root_init, user_init, user_init_imports, call_init)
|
(mod_app, root_init, user_init, user_init_imports, call_init)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,9 +2,15 @@ use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use rtic_syntax::{ast::App, Context};
|
use rtic_syntax::{ast::App, Context};
|
||||||
|
|
||||||
use crate::{check::Extra, codegen::util};
|
use crate::{analyze::Analysis, check::Extra, codegen::util};
|
||||||
|
|
||||||
pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) -> TokenStream2 {
|
pub fn codegen(
|
||||||
|
ctxt: Context,
|
||||||
|
resources_tick: bool,
|
||||||
|
app: &App,
|
||||||
|
analysis: &Analysis,
|
||||||
|
extra: &Extra,
|
||||||
|
) -> TokenStream2 {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
let mut fields = vec![];
|
let mut fields = vec![];
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
|
@ -15,9 +21,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
||||||
let mut lt = None;
|
let mut lt = None;
|
||||||
match ctxt {
|
match ctxt {
|
||||||
Context::Init => {
|
Context::Init => {
|
||||||
if extra.monotonic.is_some() {
|
if let Some(m) = extra.monotonic {
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
fields.push(quote!(
|
fields.push(quote!(
|
||||||
/// System start time = `Instant(0 /* cycles */)`
|
/// System start time = `Instant(0 /* cycles */)`
|
||||||
pub start: <#m as rtic::Monotonic>::Instant
|
pub start: <#m as rtic::Monotonic>::Instant
|
||||||
|
@ -61,9 +65,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
||||||
Context::Idle => {}
|
Context::Idle => {}
|
||||||
|
|
||||||
Context::HardwareTask(..) => {
|
Context::HardwareTask(..) => {
|
||||||
if app.uses_schedule() {
|
if let Some(m) = extra.monotonic {
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
fields.push(quote!(
|
fields.push(quote!(
|
||||||
/// Time at which this handler started executing
|
/// Time at which this handler started executing
|
||||||
pub start: <#m as rtic::Monotonic>::Instant
|
pub start: <#m as rtic::Monotonic>::Instant
|
||||||
|
@ -76,9 +78,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::SoftwareTask(..) => {
|
Context::SoftwareTask(..) => {
|
||||||
if app.uses_schedule() {
|
if let Some(m) = extra.monotonic {
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
fields.push(quote!(
|
fields.push(quote!(
|
||||||
/// The time at which this task was scheduled to run
|
/// The time at which this task was scheduled to run
|
||||||
pub scheduled: <#m as rtic::Monotonic>::Instant
|
pub scheduled: <#m as rtic::Monotonic>::Instant
|
||||||
|
@ -126,139 +126,6 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
||||||
values.push(quote!(resources: Resources::new(#priority)));
|
values.push(quote!(resources: Resources::new(#priority)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctxt.uses_schedule(app) {
|
|
||||||
let doc = "Tasks that can be `schedule`-d from this context";
|
|
||||||
if ctxt.is_init() {
|
|
||||||
items.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Schedule {
|
|
||||||
_not_send: core::marker::PhantomData<*mut ()>,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
fields.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
pub schedule: Schedule
|
|
||||||
));
|
|
||||||
|
|
||||||
values.push(quote!(
|
|
||||||
schedule: Schedule { _not_send: core::marker::PhantomData }
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
lt = Some(quote!('a));
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Schedule<'a> {
|
|
||||||
priority: &'a rtic::export::Priority,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Schedule<'a> {
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn priority(&self) -> &rtic::export::Priority {
|
|
||||||
&self.priority
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
fields.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
pub schedule: Schedule<'a>
|
|
||||||
));
|
|
||||||
|
|
||||||
values.push(quote!(
|
|
||||||
schedule: Schedule { priority }
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctxt.uses_spawn(app) {
|
|
||||||
let doc = "Tasks that can be `spawn`-ed from this context";
|
|
||||||
if ctxt.is_init() {
|
|
||||||
fields.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
pub spawn: Spawn
|
|
||||||
));
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Spawn {
|
|
||||||
_not_send: core::marker::PhantomData<*mut ()>,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
values.push(quote!(spawn: Spawn { _not_send: core::marker::PhantomData }));
|
|
||||||
} else {
|
|
||||||
lt = Some(quote!('a));
|
|
||||||
|
|
||||||
fields.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
pub spawn: Spawn<'a>
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut instant_method = None;
|
|
||||||
if ctxt.is_idle() {
|
|
||||||
items.push(quote!(
|
|
||||||
#[doc = #doc]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Spawn<'a> {
|
|
||||||
priority: &'a rtic::export::Priority,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
values.push(quote!(spawn: Spawn { priority }));
|
|
||||||
} else {
|
|
||||||
let instant_field = if app.uses_schedule() {
|
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
needs_instant = true;
|
|
||||||
instant_method = Some(quote!(
|
|
||||||
pub unsafe fn instant(&self) -> <#m as rtic::Monotonic>::Instant {
|
|
||||||
self.instant
|
|
||||||
}
|
|
||||||
));
|
|
||||||
Some(quote!(instant: <#m as rtic::Monotonic>::Instant,))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
/// Tasks that can be spawned from this context
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Spawn<'a> {
|
|
||||||
#instant_field
|
|
||||||
priority: &'a rtic::export::Priority,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
let _instant = if needs_instant {
|
|
||||||
Some(quote!(, instant))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
values.push(quote!(
|
|
||||||
spawn: Spawn { priority #_instant }
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
impl<'a> Spawn<'a> {
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn priority(&self) -> &rtic::export::Priority {
|
|
||||||
self.priority
|
|
||||||
}
|
|
||||||
|
|
||||||
#instant_method
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Context::Init = ctxt {
|
if let Context::Init = ctxt {
|
||||||
let init = &app.inits.first().unwrap();
|
let init = &app.inits.first().unwrap();
|
||||||
let late_resources = util::late_resources_ident(&init.name);
|
let late_resources = util::late_resources_ident(&init.name);
|
||||||
|
@ -316,11 +183,114 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// not sure if this is the right way, maybe its backwards,
|
||||||
|
// that spawn_module should put in in root
|
||||||
|
|
||||||
|
if let Context::SoftwareTask(..) = ctxt {
|
||||||
|
let spawnee = &app.software_tasks[name];
|
||||||
|
let priority = spawnee.args.priority;
|
||||||
|
let t = util::spawn_t_ident(priority);
|
||||||
|
let cfgs = &spawnee.cfgs;
|
||||||
|
let (args, tupled, _untupled, ty) = util::regroup_inputs(&spawnee.inputs);
|
||||||
|
let args = &args;
|
||||||
|
let tupled = &tupled;
|
||||||
|
let fq = util::fq_ident(name);
|
||||||
|
let rq = util::rq_ident(priority);
|
||||||
|
let inputs = util::inputs_ident(name);
|
||||||
|
|
||||||
|
let app_name = &app.name;
|
||||||
|
let app_path = quote! {crate::#app_name};
|
||||||
|
|
||||||
|
let device = extra.device;
|
||||||
|
let enum_ = util::interrupt_ident();
|
||||||
|
let interrupt = &analysis.interrupts.get(&priority);
|
||||||
|
|
||||||
|
// Spawn caller
|
||||||
|
items.push(quote!(
|
||||||
|
#(#cfgs)*
|
||||||
|
pub fn spawn(#(#args,)*) -> Result<(), #ty> {
|
||||||
|
// #let_instant // do we need it?
|
||||||
|
use rtic::Mutex as _;
|
||||||
|
|
||||||
|
let input = #tupled;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
|
||||||
|
#app_path::#inputs
|
||||||
|
.get_unchecked_mut(usize::from(index))
|
||||||
|
.as_mut_ptr()
|
||||||
|
.write(input);
|
||||||
|
|
||||||
|
rtic::export::interrupt::free(|_| {
|
||||||
|
#app_path::#rq.enqueue_unchecked((#app_path::#t::#name, index));
|
||||||
|
});
|
||||||
|
|
||||||
|
rtic::pend(#device::#enum_::#interrupt);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Schedule caller
|
||||||
|
if let Some(m) = extra.monotonic {
|
||||||
|
let instants = util::instants_ident(name);
|
||||||
|
|
||||||
|
let tq = util::tq_ident();
|
||||||
|
let t = util::schedule_t_ident();
|
||||||
|
|
||||||
|
items.push(quote!(
|
||||||
|
#(#cfgs)*
|
||||||
|
pub fn schedule(
|
||||||
|
instant: <#m as rtic::Monotonic>::Instant
|
||||||
|
#(,#args)*
|
||||||
|
) -> Result<(), #ty> {
|
||||||
|
unsafe {
|
||||||
|
use rtic::Mutex as _;
|
||||||
|
|
||||||
|
let input = #tupled;
|
||||||
|
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
|
||||||
|
#app_path::#inputs
|
||||||
|
.get_unchecked_mut(usize::from(index))
|
||||||
|
.as_mut_ptr()
|
||||||
|
.write(input);
|
||||||
|
|
||||||
|
#app_path::#instants
|
||||||
|
.get_unchecked_mut(usize::from(index))
|
||||||
|
.as_mut_ptr()
|
||||||
|
.write(instant);
|
||||||
|
|
||||||
|
let nr = rtic::export::NotReady {
|
||||||
|
instant,
|
||||||
|
index,
|
||||||
|
task: #app_path::#t::#name,
|
||||||
|
};
|
||||||
|
|
||||||
|
rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked(nr));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !items.is_empty() {
|
if !items.is_empty() {
|
||||||
|
let user_imports = &app.user_imports;
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
pub mod #name {
|
pub mod #name {
|
||||||
|
#(
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
#user_imports
|
||||||
|
)*
|
||||||
#(#items)*
|
#(#items)*
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,12 +12,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
stmts.push(quote!(rtic::export::interrupt::disable();));
|
stmts.push(quote!(rtic::export::interrupt::disable();));
|
||||||
|
|
||||||
// Populate the FreeQueue
|
// Populate the FreeQueue
|
||||||
for fq in &analysis.free_queues {
|
for (name, task) in &app.software_tasks {
|
||||||
// Get the task name
|
|
||||||
let name = fq.0;
|
|
||||||
let task = &app.software_tasks[name];
|
|
||||||
let cap = task.args.capacity;
|
let cap = task.args.capacity;
|
||||||
|
|
||||||
let fq_ident = util::fq_ident(name);
|
let fq_ident = util::fq_ident(name);
|
||||||
|
|
||||||
stmts.push(quote!(
|
stmts.push(quote!(
|
||||||
|
@ -81,8 +77,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the SysTick if there exist a TimerQueue
|
// Initialize the SysTick if there exist a TimerQueue
|
||||||
if let Some(tq) = analysis.timer_queues.first() {
|
if extra.monotonic.is_some() {
|
||||||
let priority = tq.priority;
|
let priority = analysis.channels.keys().max().unwrap();
|
||||||
|
|
||||||
// Compile time assert that this priority is supported by the device
|
// Compile time assert that this priority is supported by the device
|
||||||
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
use rtic_syntax::ast::App;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
check::Extra,
|
|
||||||
codegen::{schedule_body, util},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates all `${ctxt}::Schedule` methods
|
|
||||||
pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> {
|
|
||||||
let mut items = vec![];
|
|
||||||
|
|
||||||
let mut seen = HashSet::<_>::new();
|
|
||||||
for (scheduler, schedulees) in app.schedule_callers() {
|
|
||||||
let m = extra.monotonic();
|
|
||||||
let instant = quote!(<#m as rtic::Monotonic>::Instant);
|
|
||||||
|
|
||||||
let mut methods = vec![];
|
|
||||||
|
|
||||||
for name in schedulees {
|
|
||||||
let schedulee = &app.software_tasks[name];
|
|
||||||
let cfgs = &schedulee.cfgs;
|
|
||||||
let (args, _, untupled, ty) = util::regroup_inputs(&schedulee.inputs);
|
|
||||||
let args = &args;
|
|
||||||
|
|
||||||
if scheduler.is_init() {
|
|
||||||
// `init` uses a special `schedule` implementation; it doesn't use the
|
|
||||||
// `schedule_${name}` functions which are shared by other contexts
|
|
||||||
|
|
||||||
let body = schedule_body::codegen(scheduler, &name, app);
|
|
||||||
|
|
||||||
methods.push(quote!(
|
|
||||||
#(#cfgs)*
|
|
||||||
pub fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> {
|
|
||||||
#body
|
|
||||||
}
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let schedule = util::schedule_ident(name);
|
|
||||||
|
|
||||||
if !seen.contains(name) {
|
|
||||||
// Generate a `schedule_${name}_S${sender}` function
|
|
||||||
seen.insert(name);
|
|
||||||
|
|
||||||
let body = schedule_body::codegen(scheduler, &name, app);
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
#(#cfgs)*
|
|
||||||
pub unsafe fn #schedule(
|
|
||||||
priority: &rtic::export::Priority,
|
|
||||||
instant: #instant
|
|
||||||
#(,#args)*
|
|
||||||
) -> Result<(), #ty> {
|
|
||||||
#body
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
methods.push(quote!(
|
|
||||||
#(#cfgs)*
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> {
|
|
||||||
unsafe {
|
|
||||||
#schedule(self.priority(), instant #(,#untupled)*)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lt = if scheduler.is_init() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(quote!('a))
|
|
||||||
};
|
|
||||||
|
|
||||||
let scheduler = scheduler.ident(app);
|
|
||||||
debug_assert!(!methods.is_empty());
|
|
||||||
items.push(quote!(
|
|
||||||
impl<#lt> #scheduler::Schedule<#lt> {
|
|
||||||
#(#methods)*
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
items
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
use rtic_syntax::{ast::App, Context};
|
|
||||||
use syn::Ident;
|
|
||||||
|
|
||||||
use crate::codegen::util;
|
|
||||||
|
|
||||||
pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 {
|
|
||||||
let schedulee = &app.software_tasks[name];
|
|
||||||
|
|
||||||
let fq = util::fq_ident(name);
|
|
||||||
let tq = util::tq_ident();
|
|
||||||
let (dequeue, enqueue) = if scheduler.is_init() {
|
|
||||||
(quote!(#fq.dequeue()), quote!(#tq.enqueue_unchecked(nr);))
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
quote!((#fq { priority }).lock(|fq| fq.split().1.dequeue())),
|
|
||||||
quote!((#tq { priority }).lock(|tq| tq.enqueue_unchecked(nr));),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let write_instant = if app.uses_schedule() {
|
|
||||||
let instants = util::instants_ident(name);
|
|
||||||
|
|
||||||
Some(quote!(
|
|
||||||
#instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant);
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let (_, tupled, _, _) = util::regroup_inputs(&schedulee.inputs);
|
|
||||||
let inputs = util::inputs_ident(name);
|
|
||||||
let t = util::schedule_t_ident();
|
|
||||||
quote!(
|
|
||||||
unsafe {
|
|
||||||
use rtic::Mutex as _;
|
|
||||||
|
|
||||||
let input = #tupled;
|
|
||||||
if let Some(index) = #dequeue {
|
|
||||||
#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(input);
|
|
||||||
|
|
||||||
#write_instant
|
|
||||||
|
|
||||||
let nr = rtic::export::NotReady {
|
|
||||||
instant,
|
|
||||||
index,
|
|
||||||
task: #t::#name,
|
|
||||||
};
|
|
||||||
|
|
||||||
#enqueue
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -39,71 +39,49 @@ pub fn codegen(
|
||||||
let cap_ty = util::capacity_typenum(cap, true);
|
let cap_ty = util::capacity_typenum(cap, true);
|
||||||
|
|
||||||
// Create free queues and inputs / instants buffers
|
// Create free queues and inputs / instants buffers
|
||||||
if let Some(&ceiling) = analysis.free_queues.get(name) {
|
let fq = util::fq_ident(name);
|
||||||
let fq = util::fq_ident(name);
|
|
||||||
|
|
||||||
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
|
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
|
||||||
(
|
(
|
||||||
quote!(rtic::export::SCFQ<#cap_ty>),
|
quote!(rtic::export::SCFQ<#cap_ty>),
|
||||||
quote!(rtic::export::Queue(unsafe {
|
quote!(rtic::export::Queue(unsafe {
|
||||||
rtic::export::iQueue::u8_sc()
|
rtic::export::iQueue::u8_sc()
|
||||||
})),
|
})),
|
||||||
Box::new(|| util::link_section_uninit(true)),
|
Box::new(|| util::link_section_uninit(true)),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
/// Queue version of a free-list that keeps track of empty slots in
|
/// Queue version of a free-list that keeps track of empty slots in
|
||||||
/// the following buffers
|
/// the following buffers
|
||||||
static mut #fq: #fq_ty = #fq_expr;
|
pub static mut #fq: #fq_ty = #fq_expr;
|
||||||
));
|
));
|
||||||
|
|
||||||
// Generate a resource proxy if needed
|
let ref elems = (0..cap)
|
||||||
if let Some(ceiling) = ceiling {
|
.map(|_| quote!(core::mem::MaybeUninit::uninit()))
|
||||||
mod_app.push(quote!(
|
.collect::<Vec<_>>();
|
||||||
struct #fq<'a> {
|
|
||||||
priority: &'a rtic::export::Priority,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
mod_app.push(util::impl_mutex(
|
if let Some(m) = extra.monotonic {
|
||||||
extra,
|
let instants = util::instants_ident(name);
|
||||||
&[],
|
|
||||||
false,
|
|
||||||
&fq,
|
|
||||||
fq_ty,
|
|
||||||
ceiling,
|
|
||||||
quote!(&mut #fq),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let ref elems = (0..cap)
|
|
||||||
.map(|_| quote!(core::mem::MaybeUninit::uninit()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if app.uses_schedule() {
|
|
||||||
let m = extra.monotonic();
|
|
||||||
let instants = util::instants_ident(name);
|
|
||||||
|
|
||||||
let uninit = mk_uninit();
|
|
||||||
mod_app.push(quote!(
|
|
||||||
#uninit
|
|
||||||
/// Buffer that holds the instants associated to the inputs of a task
|
|
||||||
static mut #instants:
|
|
||||||
[core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] =
|
|
||||||
[#(#elems,)*];
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let uninit = mk_uninit();
|
let uninit = mk_uninit();
|
||||||
let inputs = util::inputs_ident(name);
|
|
||||||
mod_app.push(quote!(
|
mod_app.push(quote!(
|
||||||
#uninit
|
#uninit
|
||||||
/// Buffer that holds the inputs of a task
|
/// Buffer that holds the instants associated to the inputs of a task
|
||||||
static mut #inputs: [core::mem::MaybeUninit<#input_ty>; #cap_lit] =
|
pub static mut #instants:
|
||||||
|
[core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] =
|
||||||
[#(#elems,)*];
|
[#(#elems,)*];
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let uninit = mk_uninit();
|
||||||
|
let inputs_ident = util::inputs_ident(name);
|
||||||
|
mod_app.push(quote!(
|
||||||
|
#uninit
|
||||||
|
/// Buffer that holds the inputs of a task
|
||||||
|
pub static mut #inputs_ident: [core::mem::MaybeUninit<#input_ty>; #cap_lit] =
|
||||||
|
[#(#elems,)*];
|
||||||
|
));
|
||||||
|
|
||||||
// `${task}Resources`
|
// `${task}Resources`
|
||||||
let mut needs_lt = false;
|
let mut needs_lt = false;
|
||||||
if !task.args.resources.is_empty() {
|
if !task.args.resources.is_empty() {
|
||||||
|
@ -161,6 +139,7 @@ pub fn codegen(
|
||||||
Context::SoftwareTask(name),
|
Context::SoftwareTask(name),
|
||||||
needs_lt,
|
needs_lt,
|
||||||
app,
|
app,
|
||||||
|
analysis,
|
||||||
extra,
|
extra,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
use rtic_syntax::ast::App;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
analyze::Analysis,
|
|
||||||
check::Extra,
|
|
||||||
codegen::{spawn_body, util},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Generates all `${ctxt}::Spawn` methods
|
|
||||||
pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
|
|
||||||
let mut items = vec![];
|
|
||||||
|
|
||||||
let mut seen = HashSet::<_>::new();
|
|
||||||
for (spawner, spawnees) in app.spawn_callers() {
|
|
||||||
let mut methods = vec![];
|
|
||||||
|
|
||||||
for name in spawnees {
|
|
||||||
let spawnee = &app.software_tasks[name];
|
|
||||||
let cfgs = &spawnee.cfgs;
|
|
||||||
let (args, _, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
|
|
||||||
let args = &args;
|
|
||||||
|
|
||||||
if spawner.is_init() {
|
|
||||||
// `init` uses a special spawn implementation; it doesn't use the `spawn_${name}`
|
|
||||||
// functions which are shared by other contexts
|
|
||||||
|
|
||||||
let body = spawn_body::codegen(spawner, &name, app, analysis, extra);
|
|
||||||
|
|
||||||
let let_instant = if app.uses_schedule() {
|
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
Some(quote!(let instant = unsafe { <#m as rtic::Monotonic>::zero() };))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
methods.push(quote!(
|
|
||||||
#(#cfgs)*
|
|
||||||
pub fn #name(&self #(,#args)*) -> Result<(), #ty> {
|
|
||||||
#let_instant
|
|
||||||
#body
|
|
||||||
}
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let spawn = util::spawn_ident(name);
|
|
||||||
|
|
||||||
if !seen.contains(name) {
|
|
||||||
// Generate a `spawn_${name}_S${sender}` function
|
|
||||||
seen.insert(name);
|
|
||||||
|
|
||||||
let instant = if app.uses_schedule() {
|
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
Some(quote!(, instant: <#m as rtic::Monotonic>::Instant))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let body = spawn_body::codegen(spawner, &name, app, analysis, extra);
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
#(#cfgs)*
|
|
||||||
unsafe fn #spawn(
|
|
||||||
priority: &rtic::export::Priority
|
|
||||||
#instant
|
|
||||||
#(,#args)*
|
|
||||||
) -> Result<(), #ty> {
|
|
||||||
#body
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (let_instant, instant) = if app.uses_schedule() {
|
|
||||||
let m = extra.monotonic();
|
|
||||||
|
|
||||||
(
|
|
||||||
Some(if spawner.is_idle() {
|
|
||||||
quote!(let instant = <#m as rtic::Monotonic>::now();)
|
|
||||||
} else {
|
|
||||||
quote!(let instant = self.instant();)
|
|
||||||
}),
|
|
||||||
Some(quote!(, instant)),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
methods.push(quote!(
|
|
||||||
#(#cfgs)*
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn #name(&self #(,#args)*) -> Result<(), #ty> {
|
|
||||||
unsafe {
|
|
||||||
#let_instant
|
|
||||||
#spawn(self.priority() #instant #(,#untupled)*)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lt = if spawner.is_init() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(quote!('a))
|
|
||||||
};
|
|
||||||
|
|
||||||
let spawner = spawner.ident(app);
|
|
||||||
debug_assert!(!methods.is_empty());
|
|
||||||
items.push(quote!(
|
|
||||||
impl<#lt> #spawner::Spawn<#lt> {
|
|
||||||
#(#methods)*
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
items
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
use rtic_syntax::{ast::App, Context};
|
|
||||||
use syn::Ident;
|
|
||||||
|
|
||||||
use crate::{analyze::Analysis, check::Extra, codegen::util};
|
|
||||||
|
|
||||||
pub fn codegen(
|
|
||||||
spawner: Context,
|
|
||||||
name: &Ident,
|
|
||||||
app: &App,
|
|
||||||
analysis: &Analysis,
|
|
||||||
_extra: &Extra,
|
|
||||||
) -> TokenStream2 {
|
|
||||||
let spawnee = &app.software_tasks[name];
|
|
||||||
let priority = spawnee.args.priority;
|
|
||||||
|
|
||||||
let write_instant = if app.uses_schedule() {
|
|
||||||
let instants = util::instants_ident(name);
|
|
||||||
|
|
||||||
Some(quote!(
|
|
||||||
#instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant);
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let t = util::spawn_t_ident(priority);
|
|
||||||
let fq = util::fq_ident(name);
|
|
||||||
let rq = util::rq_ident(priority);
|
|
||||||
let (dequeue, enqueue) = if spawner.is_init() {
|
|
||||||
(
|
|
||||||
quote!(#fq.dequeue()),
|
|
||||||
quote!(#rq.enqueue_unchecked((#t::#name, index));),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
quote!((#fq { priority }.lock(|fq| fq.split().1.dequeue()))),
|
|
||||||
quote!((#rq { priority }.lock(|rq| {
|
|
||||||
rq.split().0.enqueue_unchecked((#t::#name, index))
|
|
||||||
}));),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let enum_ = util::interrupt_ident();
|
|
||||||
let interrupt = &analysis.interrupts.get(&priority);
|
|
||||||
let pend = {
|
|
||||||
quote!(
|
|
||||||
rtic::pend(you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#enum_::#interrupt);
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (_, tupled, _, _) = util::regroup_inputs(&spawnee.inputs);
|
|
||||||
let inputs = util::inputs_ident(name);
|
|
||||||
quote!(
|
|
||||||
unsafe {
|
|
||||||
use rtic::Mutex as _;
|
|
||||||
|
|
||||||
let input = #tupled;
|
|
||||||
if let Some(index) = #dequeue {
|
|
||||||
#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(input);
|
|
||||||
|
|
||||||
#write_instant
|
|
||||||
|
|
||||||
#enqueue
|
|
||||||
|
|
||||||
#pend
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -8,16 +8,16 @@ use crate::{analyze::Analysis, check::Extra, codegen::util};
|
||||||
pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
|
pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
if let Some(timer_queue) = &analysis.timer_queues.first() {
|
if let Some(m) = extra.monotonic {
|
||||||
let t = util::schedule_t_ident();
|
let t = util::schedule_t_ident();
|
||||||
|
|
||||||
// Enumeration of `schedule`-able tasks
|
// Enumeration of `schedule`-able tasks
|
||||||
{
|
{
|
||||||
let variants = timer_queue
|
let variants = app
|
||||||
.tasks
|
.software_tasks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| {
|
.map(|(name, task)| {
|
||||||
let cfgs = &app.software_tasks[name].cfgs;
|
let cfgs = &task.cfgs;
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
|
@ -31,7 +31,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum #t {
|
pub enum #t {
|
||||||
#(#variants,)*
|
#(#variants,)*
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
@ -42,42 +42,30 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
// Static variable and resource proxy
|
// Static variable and resource proxy
|
||||||
{
|
{
|
||||||
let doc = format!("Timer queue");
|
let doc = format!("Timer queue");
|
||||||
let m = extra.monotonic();
|
let cap = app
|
||||||
let n = util::capacity_typenum(timer_queue.capacity, false);
|
.software_tasks
|
||||||
|
.iter()
|
||||||
|
.map(|(_name, task)| task.args.capacity)
|
||||||
|
.sum();
|
||||||
|
let n = util::capacity_typenum(cap, false);
|
||||||
let tq_ty = quote!(rtic::export::TimerQueue<#m, #t, #n>);
|
let tq_ty = quote!(rtic::export::TimerQueue<#m, #t, #n>);
|
||||||
|
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
static mut #tq: #tq_ty = rtic::export::TimerQueue(
|
pub static mut #tq: #tq_ty = rtic::export::TimerQueue(
|
||||||
rtic::export::BinaryHeap(
|
rtic::export::BinaryHeap(
|
||||||
rtic::export::iBinaryHeap::new()
|
rtic::export::iBinaryHeap::new()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
struct #tq<'a> {
|
|
||||||
priority: &'a rtic::export::Priority,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
items.push(util::impl_mutex(
|
|
||||||
extra,
|
|
||||||
&[],
|
|
||||||
false,
|
|
||||||
&tq,
|
|
||||||
tq_ty,
|
|
||||||
timer_queue.ceiling,
|
|
||||||
quote!(&mut #tq),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timer queue handler
|
// Timer queue handler
|
||||||
{
|
{
|
||||||
let arms = timer_queue
|
let arms = app
|
||||||
.tasks
|
.software_tasks
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| {
|
.map(|(name, task)| {
|
||||||
let task = &app.software_tasks[name];
|
|
||||||
|
|
||||||
let cfgs = &task.cfgs;
|
let cfgs = &task.cfgs;
|
||||||
let priority = task.args.priority;
|
let priority = task.args.priority;
|
||||||
let rq = util::rq_ident(priority);
|
let rq = util::rq_ident(priority);
|
||||||
|
@ -94,9 +82,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
quote!(
|
quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#t::#name => {
|
#t::#name => {
|
||||||
(#rq { priority: &rtic::export::Priority::new(PRIORITY) }).lock(|rq| {
|
rtic::export::interrupt::free(|_| #rq.split().0.enqueue_unchecked((#rqt::#name, index)));
|
||||||
rq.split().0.enqueue_unchecked((#rqt::#name, index))
|
|
||||||
});
|
|
||||||
|
|
||||||
#pend
|
#pend
|
||||||
}
|
}
|
||||||
|
@ -104,30 +90,18 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let priority = timer_queue.priority;
|
|
||||||
let sys_tick = util::suffixed("SysTick");
|
let sys_tick = util::suffixed("SysTick");
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe fn #sys_tick() {
|
unsafe fn #sys_tick() {
|
||||||
use rtic::Mutex as _;
|
use rtic::Mutex as _;
|
||||||
|
|
||||||
/// The priority of this handler
|
while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue())
|
||||||
const PRIORITY: u8 = #priority;
|
{
|
||||||
|
match task {
|
||||||
rtic::export::run(PRIORITY, || {
|
#(#arms)*
|
||||||
while let Some((task, index)) = (#tq {
|
|
||||||
// NOTE dynamic priority is always the static priority at this point
|
|
||||||
priority: &rtic::export::Priority::new(PRIORITY),
|
|
||||||
})
|
|
||||||
// NOTE `inline(always)` produces faster and smaller code
|
|
||||||
.lock(#[inline(always)]
|
|
||||||
|tq| tq.dequeue())
|
|
||||||
{
|
|
||||||
match task {
|
|
||||||
#(#arms)*
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,23 +207,9 @@ pub fn rq_ident(priority: u8) -> Ident {
|
||||||
Ident::new(&format!("P{}_RQ", priority), Span::call_site())
|
Ident::new(&format!("P{}_RQ", priority), Span::call_site())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for a "schedule" function
|
|
||||||
///
|
|
||||||
/// The methods of the `Schedule` structs invoke these functions.
|
|
||||||
pub fn schedule_ident(name: &Ident) -> Ident {
|
|
||||||
Ident::new(&format!("schedule_{}", name.to_string()), Span::call_site())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an identifier for the `enum` of `schedule`-able tasks
|
/// Generates an identifier for the `enum` of `schedule`-able tasks
|
||||||
pub fn schedule_t_ident() -> Ident {
|
pub fn schedule_t_ident() -> Ident {
|
||||||
Ident::new(&format!("T"), Span::call_site())
|
Ident::new(&format!("SCHED_T"), Span::call_site())
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an identifier for a "spawn" function
|
|
||||||
///
|
|
||||||
/// The methods of the `Spawn` structs invoke these functions.
|
|
||||||
pub fn spawn_ident(name: &Ident) -> Ident {
|
|
||||||
Ident::new(&format!("spawn_{}", name.to_string()), Span::call_site())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for the `enum` of `spawn`-able tasks
|
/// Generates an identifier for the `enum` of `spawn`-able tasks
|
||||||
|
|
|
@ -207,7 +207,6 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
settings.optimize_priorities = true;
|
settings.optimize_priorities = true;
|
||||||
settings.parse_binds = true;
|
settings.parse_binds = true;
|
||||||
settings.parse_extern_interrupt = true;
|
settings.parse_extern_interrupt = true;
|
||||||
settings.parse_schedule = true;
|
|
||||||
|
|
||||||
let (app, analysis) = match rtic_syntax::parse(args, input, settings) {
|
let (app, analysis) = match rtic_syntax::parse(args, input, settings) {
|
||||||
Err(e) => return e.to_compile_error().into(),
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
#[rtic::app(device = lm3s6965)]
|
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
mod app {
|
mod app {
|
||||||
#[task(binds = SysTick)]
|
#[task(binds = SysTick)]
|
||||||
fn sys_tick(_: sys_tick::Context) {}
|
fn sys_tick(_: sys_tick::Context) {}
|
||||||
|
|
||||||
#[task(schedule = [foo])]
|
|
||||||
fn foo(_: foo::Context) {}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue