355: Multi-core removal r=korken89 a=AfoHT

Dependent on https://github.com/rtic-rs/rtic-syntax/pull/27

With the same reasoning as ^^

For now the testing is done against my rtic-syntax/multiremove-branch, but before we merge it should corrected.

Co-authored-by: Henrik Tjäder <henrik@tjaders.com>
This commit is contained in:
bors[bot] 2020-09-04 07:50:13 +00:00 committed by GitHub
commit 7506bd8ae0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 436 additions and 1650 deletions

View file

@ -156,13 +156,6 @@ jobs:
command: check command: check
args: --examples --target=${{ matrix.target }} --features __min_r1_43 args: --examples --target=${{ matrix.target }} --features __min_r1_43
- name: cargo check -p homogeneous
uses: actions-rs/cargo@v1
with:
use-cross: false
command: check
args: -p homogeneous --examples --target=${{ matrix.target }}
# Use precompiled binutils # Use precompiled binutils
- name: cargo install cargo-binutils - name: cargo install cargo-binutils
uses: actions-rs/install@v0.1 uses: actions-rs/install@v0.1
@ -481,57 +474,6 @@ jobs:
command: test command: test
args: --test single args: --test single
# Verify all multicore examples
checkmulticore:
name: checkmulticore
runs-on: ubuntu-20.04
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
toolchain:
- nightly
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Rust ${{ matrix.toolchain }} with x86_64-unknown-linux-gnu
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: x86_64-unknown-linux-gnu
override: true
- name: Install Rust ${{ matrix.toolchain }} with thumbv7m-none-eabi
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: thumbv7m-none-eabi
override: true
- name: Install Rust ${{ matrix.toolchain }} with thumbv6m-none-eabi
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: thumbv6m-none-eabi
override: true
- uses: actions-rs/cargo@v1
with:
command: install
args: microamp-tools --version 0.1.0-alpha.3
- name: Check multi-core examples
run: |
cd heterogeneous
exs=(
smallest
x-init-2
x-init
x-schedule
x-spawn
)
for ex in ${exs[@]}; do
cargo-microamp --example=$ex --target thumbv7m-none-eabi,thumbv6m-none-eabi --check
done
# Build documentation, check links # Build documentation, check links
docs: docs:
name: docs name: docs
@ -661,7 +603,6 @@ jobs:
- checkmacros - checkmacros
- testv7 - testv7
- testv6 - testv6
- checkmulticore
- docs - docs
- mdbook - mdbook
# Only run this when pushing to master branch # Only run this when pushing to master branch
@ -765,7 +706,6 @@ jobs:
- checkmacros - checkmacros
- testv7 - testv7
- testv6 - testv6
- checkmulticore
- docs - docs
- mdbook - mdbook
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@ -782,7 +722,6 @@ jobs:
- checkmacros - checkmacros
- testv7 - testv7
- testv6 - testv6
- checkmulticore
- docs - docs
- mdbook - mdbook
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View file

@ -77,8 +77,6 @@ version = "0.5.2"
trybuild = "1" trybuild = "1"
[features] [features]
heterogeneous = ["cortex-m-rtic-macros/heterogeneous", "microamp"]
homogeneous = ["cortex-m-rtic-macros/homogeneous"]
# used for testing this crate; do not use in applications # used for testing this crate; do not use in applications
__v7 =[] __v7 =[]
__min_r1_43 =[] __min_r1_43 =[]
@ -89,8 +87,6 @@ lto = true
[workspace] [workspace]
members = [ members = [
"heterogeneous",
"homogeneous",
"macros", "macros",
] ]

View file

@ -1,18 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge@japaric.io>"]
edition = "2018"
name = "heterogeneous"
# this crate is only used for testing
publish = false
version = "0.0.0-alpha.0"
[dependencies]
bare-metal = "0.2.4"
[dependencies.cortex-m-rtic]
path = ".."
features = ["heterogeneous"]
[dev-dependencies]
panic-halt = "0.2.0"
microamp = "0.1.0-alpha.1"

View file

@ -1 +0,0 @@
This directory contains *heterogeneous* multi-core compile pass tests.

View file

@ -1,7 +0,0 @@
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = heterogeneous)]
const APP: () = {};

View file

@ -1,39 +0,0 @@
//! [compile-pass] Cross initialization of late resources
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = heterogeneous)]
const APP: () = {
struct Resources {
// owned by core #1 but initialized by core #0
x: u32,
// owned by core #0 but initialized by core #1
y: u32,
}
#[init(core = 0, late = [x])]
fn a(_: a::Context) -> a::LateResources {
a::LateResources { x: 0 }
}
#[idle(core = 0, resources = [y])]
fn b(_: b::Context) -> ! {
loop {}
}
#[init(core = 1)]
fn c(_: c::Context) -> c::LateResources {
c::LateResources { y: 0 }
}
#[idle(core = 1, resources = [x])]
fn d(_: d::Context) -> ! {
loop {}
}
};

View file

@ -1,26 +0,0 @@
//! [compile-pass] Split initialization of late resources
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = heterogeneous)]
const APP: () = {
struct Resources {
x: u32,
y: u32,
}
#[init(core = 0, late = [x])]
fn a(_: a::Context) -> a::LateResources {
a::LateResources { x: 0 }
}
#[init(core = 1)]
fn b(_: b::Context) -> b::LateResources {
b::LateResources { y: 0 }
}
};

View file

@ -1,36 +0,0 @@
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = heterogeneous, monotonic = heterogeneous::MT)]
const APP: () = {
#[init(core = 0, spawn = [ping])]
fn init(c: init::Context) {
c.spawn.ping().ok();
}
#[task(core = 0, schedule = [ping])]
fn pong(c: pong::Context) {
c.schedule.ping(c.scheduled + 1_000_000).ok();
}
#[task(core = 1, schedule = [pong])]
fn ping(c: ping::Context) {
c.schedule.pong(c.scheduled + 1_000_000).ok();
}
extern "C" {
#[core = 0]
fn I0();
#[core = 0]
fn I1();
#[core = 1]
fn I0();
#[core = 1]
fn I1();
}
};

View file

@ -1,20 +0,0 @@
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = heterogeneous)]
const APP: () = {
#[init(core = 0, spawn = [foo])]
fn init(c: init::Context) {
c.spawn.foo().ok();
}
#[task(core = 1)]
fn foo(_: foo::Context) {}
extern "C" {
#[core = 1]
fn I0();
}
};

View file

@ -1,99 +0,0 @@
//! Fake multi-core PAC
#![no_std]
use core::{
cmp::Ordering,
ops::{Add, Sub},
};
use bare_metal::Nr;
use rtic::{Fraction, Monotonic, MultiCore};
// both cores have the exact same interrupts
pub use Interrupt_0 as Interrupt_1;
// Fake priority bits
pub const NVIC_PRIO_BITS: u8 = 3;
pub fn xpend(_core: u8, _interrupt: impl Nr) {}
/// Fake monotonic timer
pub struct MT;
impl Monotonic for MT {
type Instant = Instant;
fn ratio() -> Fraction {
Fraction {
numerator: 1,
denominator: 1,
}
}
unsafe fn reset() {
(0xE0001004 as *mut u32).write_volatile(0)
}
fn now() -> Instant {
unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) }
}
fn zero() -> Instant {
Instant(0)
}
}
impl MultiCore for MT {}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Instant(i32);
impl Add<u32> for Instant {
type Output = Instant;
fn add(self, rhs: u32) -> Self {
Instant(self.0.wrapping_add(rhs as i32))
}
}
impl Sub for Instant {
type Output = u32;
fn sub(self, rhs: Self) -> u32 {
self.0.checked_sub(rhs.0).unwrap() as u32
}
}
impl Ord for Instant {
fn cmp(&self, rhs: &Self) -> Ordering {
self.0.wrapping_sub(rhs.0).cmp(&0)
}
}
impl PartialOrd for Instant {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
// Fake interrupts
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum Interrupt_0 {
I0 = 0,
I1 = 1,
I2 = 2,
I3 = 3,
I4 = 4,
I5 = 5,
I6 = 6,
I7 = 7,
}
unsafe impl Nr for Interrupt_0 {
fn nr(&self) -> u8 {
*self as u8
}
}

View file

@ -1,17 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge@japaric.io>"]
edition = "2018"
name = "homogeneous"
# this crate is only used for testing
publish = false
version = "0.0.0-alpha.0"
[dependencies]
bare-metal = "0.2.4"
[dependencies.cortex-m-rtic]
path = ".."
features = ["homogeneous"]
[dev-dependencies]
panic-halt = "0.2.0"

View file

@ -1 +0,0 @@
This directory contains *homogeneous* multi-core compile pass tests.

View file

@ -1,7 +0,0 @@
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = homogeneous)]
const APP: () = {};

View file

@ -1,39 +0,0 @@
//! [compile-pass] Cross initialization of late resources
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = homogeneous)]
const APP: () = {
struct Resources {
// owned by core #1 but initialized by core #0
x: u32,
// owned by core #0 but initialized by core #1
y: u32,
}
#[init(core = 0, late = [x])]
fn a(_: a::Context) -> a::LateResources {
a::LateResources { x: 0 }
}
#[idle(core = 0, resources = [y])]
fn b(_: b::Context) -> ! {
loop {}
}
#[init(core = 1)]
fn c(_: c::Context) -> c::LateResources {
c::LateResources { y: 0 }
}
#[idle(core = 1, resources = [x])]
fn d(_: d::Context) -> ! {
loop {}
}
};

View file

@ -1,26 +0,0 @@
//! [compile-pass] Split initialization of late resources
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = homogeneous)]
const APP: () = {
struct Resources {
x: u32,
y: u32,
}
#[init(core = 0, late = [x])]
fn a(_: a::Context) -> a::LateResources {
a::LateResources { x: 0 }
}
#[init(core = 1)]
fn b(_: b::Context) -> b::LateResources {
b::LateResources { y: 0 }
}
};

View file

@ -1,36 +0,0 @@
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = homogeneous, monotonic = homogeneous::MT)]
const APP: () = {
#[init(core = 0, spawn = [ping])]
fn init(c: init::Context) {
c.spawn.ping().ok();
}
#[task(core = 0, schedule = [ping])]
fn pong(c: pong::Context) {
c.schedule.ping(c.scheduled + 1_000_000).ok();
}
#[task(core = 1, schedule = [pong])]
fn ping(c: ping::Context) {
c.schedule.pong(c.scheduled + 1_000_000).ok();
}
extern "C" {
#[core = 0]
fn I0();
#[core = 0]
fn I1();
#[core = 1]
fn I0();
#[core = 1]
fn I1();
}
};

View file

@ -1,20 +0,0 @@
#![no_main]
#![no_std]
use panic_halt as _;
#[rtic::app(cores = 2, device = homogeneous)]
const APP: () = {
#[init(core = 0, spawn = [foo])]
fn init(c: init::Context) {
c.spawn.foo().ok();
}
#[task(core = 1)]
fn foo(_: foo::Context) {}
extern "C" {
#[core = 1]
fn I0();
}
};

View file

@ -1,99 +0,0 @@
//! Fake multi-core PAC
#![no_std]
use core::{
cmp::Ordering,
ops::{Add, Sub},
};
use bare_metal::Nr;
use rtic::{Fraction, Monotonic, MultiCore};
// both cores have the exact same interrupts
pub use Interrupt_0 as Interrupt_1;
// Fake priority bits
pub const NVIC_PRIO_BITS: u8 = 3;
pub fn xpend(_core: u8, _interrupt: impl Nr) {}
/// Fake monotonic timer
pub struct MT;
impl Monotonic for MT {
type Instant = Instant;
fn ratio() -> Fraction {
Fraction {
numerator: 1,
denominator: 1,
}
}
unsafe fn reset() {
(0xE0001004 as *mut u32).write_volatile(0)
}
fn now() -> Instant {
unsafe { Instant((0xE0001004 as *const u32).read_volatile() as i32) }
}
fn zero() -> Instant {
Instant(0)
}
}
impl MultiCore for MT {}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Instant(i32);
impl Add<u32> for Instant {
type Output = Instant;
fn add(self, rhs: u32) -> Self {
Instant(self.0.wrapping_add(rhs as i32))
}
}
impl Sub for Instant {
type Output = u32;
fn sub(self, rhs: Self) -> u32 {
self.0.checked_sub(rhs.0).unwrap() as u32
}
}
impl Ord for Instant {
fn cmp(&self, rhs: &Self) -> Ordering {
self.0.wrapping_sub(rhs.0).cmp(&0)
}
}
impl PartialOrd for Instant {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
// Fake interrupts
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum Interrupt_0 {
I0 = 0,
I1 = 1,
I2 = 2,
I3 = 3,
I4 = 4,
I5 = 5,
I6 = 6,
I7 = 7,
}
unsafe impl Nr for Interrupt_0 {
fn nr(&self) -> u8 {
*self as u8
}
}

View file

@ -21,8 +21,5 @@ proc-macro = true
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
syn = "1" syn = "1"
rtic-syntax = "0.4.0" rtic-syntax = { git = "https://github.com/rtic-rs/rtic-syntax", branch = "master", version = "0.4.0" }
[features]
heterogeneous = []
homogeneous = []

View file

@ -4,14 +4,14 @@ use std::collections::{BTreeMap, BTreeSet};
use rtic_syntax::{ use rtic_syntax::{
analyze::{self, Priority}, analyze::{self, Priority},
ast::App, ast::App,
Core, P, P,
}; };
use syn::Ident; use syn::Ident;
/// Extend the upstream `Analysis` struct with our field /// Extend the upstream `Analysis` struct with our field
pub struct Analysis { pub struct Analysis {
parent: P<analyze::Analysis>, parent: P<analyze::Analysis>,
pub interrupts: BTreeMap<Core, BTreeMap<Priority, Ident>>, pub interrupts: BTreeMap<Priority, Ident>,
} }
impl ops::Deref for Analysis { impl ops::Deref for Analysis {
@ -25,31 +25,20 @@ impl ops::Deref for Analysis {
// Assign an `extern` interrupt to each priority level // Assign an `extern` interrupt to each priority level
pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> { pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> {
let mut interrupts = BTreeMap::new(); let mut interrupts = BTreeMap::new();
for core in 0..app.args.cores { let priorities = app
let priorities = app .software_tasks
.software_tasks .values()
.values() .filter_map(|task| Some(task.args.priority))
.filter_map(|task| { .chain(analysis.timer_queues.first().map(|tq| tq.priority))
if task.args.core == core { .collect::<BTreeSet<_>>();
Some(task.args.priority)
} else {
None
}
})
.chain(analysis.timer_queues.get(&core).map(|tq| tq.priority))
.collect::<BTreeSet<_>>();
if !priorities.is_empty() { if !priorities.is_empty() {
interrupts.insert( interrupts = priorities
core, .iter()
priorities .cloned()
.iter() .rev()
.cloned() .zip(app.extern_interrupts.keys().cloned())
.rev() .collect();
.zip(app.extern_interrupts[&core].keys().cloned())
.collect(),
);
}
} }
P::new(Analysis { P::new(Analysis {

View file

@ -10,7 +10,7 @@ use syn::{parse, Path};
pub struct Extra<'a> { pub struct Extra<'a> {
pub device: &'a Path, pub device: &'a Path,
pub monotonic: Option<&'a Path>, pub monotonic: Option<&'a Path>,
pub peripherals: Option<u8>, pub peripherals: bool,
} }
impl<'a> Extra<'a> { impl<'a> Extra<'a> {
@ -20,35 +20,14 @@ 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>> {
if cfg!(feature = "homogeneous") { // Check that all exceptions are valid; only exceptions with configurable priorities are
// this RTIC mode uses the same namespace for all cores so we need to check that the
// identifiers used for each core `#[init]` and `#[idle]` functions don't collide
let mut seen = HashSet::new();
for name in app
.inits
.values()
.map(|init| &init.name)
.chain(app.idles.values().map(|idle| &idle.name))
{
if seen.contains(name) {
return Err(parse::Error::new(
name.span(),
"this identifier is already being used by another core",
));
} else {
seen.insert(name);
}
}
}
// check that all exceptions are valid; only exceptions with configurable priorities are
// accepted // accepted
for (name, task) in &app.hardware_tasks { for (name, task) in &app.hardware_tasks {
let name_s = task.args.binds.to_string(); let name_s = task.args.binds.to_string();
match &*name_s { match &*name_s {
"SysTick" => { "SysTick" => {
if analysis.timer_queues.get(&task.args.core).is_some() { // If the timer queue is used, then SysTick is unavailable
if !analysis.timer_queues.is_empty() {
return Err(parse::Error::new( return Err(parse::Error::new(
name.span(), name.span(),
"this exception can't be used because it's being used by the runtime", "this exception can't be used because it's being used by the runtime",
@ -69,13 +48,9 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
} }
} }
// 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 for name in app.extern_interrupts.keys() {
.extern_interrupts
.iter()
.flat_map(|(_, interrupts)| interrupts.keys())
{
let name_s = name.to_string(); let name_s = name.to_string();
match &*name_s { match &*name_s {
@ -91,52 +66,38 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
} }
} }
// check that there are enough external interrupts to dispatch the software tasks and the timer // Check that there are enough external interrupts to dispatch the software tasks and the timer
// queue handler // queue handler
for core in 0..app.args.cores { let mut first = None;
let mut first = None; let priorities = app
let priorities = app .software_tasks
.software_tasks .iter()
.iter() .filter_map(|(name, task)| {
.filter_map(|(name, task)| { first = Some(name);
if task.args.core == core { Some(task.args.priority)
first = Some(name); })
Some(task.args.priority) .chain(analysis.timer_queues.first().map(|tq| tq.priority))
} else { .collect::<HashSet<_>>();
None
}
})
.chain(analysis.timer_queues.get(&core).map(|tq| tq.priority))
.collect::<HashSet<_>>();
let need = priorities.len(); let need = priorities.len();
let given = app let given = app.extern_interrupts.len();
.extern_interrupts if need > given {
.get(&core) let s = {
.map(|ei| ei.len()) format!(
.unwrap_or(0); "not enough `extern` interrupts to dispatch \
if need > given { all software tasks (need: {}; given: {})",
let s = if app.args.cores == 1 { need, given
format!( )
"not enough `extern` interrupts to dispatch \ };
all software tasks (need: {}; given: {})",
need, given
)
} else {
format!(
"not enough `extern` interrupts to dispatch \
all software tasks on this core (need: {}; given: {})",
need, given
)
};
return Err(parse::Error::new(first.unwrap().span(), &s)); // If not enough tasks and first still is None, may cause
} // "custom attribute panicked" due to unwrap on None
return Err(parse::Error::new(first.unwrap().span(), &s));
} }
let mut device = None; let mut device = None;
let mut monotonic = None; let mut monotonic = None;
let mut peripherals = None; let mut peripherals = false;
for (k, v) in &app.args.custom { for (k, v) in &app.args.custom {
let ks = k.to_string(); let ks = k.to_string();
@ -165,34 +126,11 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
}, },
"peripherals" => match v { "peripherals" => match v {
CustomArg::Bool(x) if app.args.cores == 1 => { CustomArg::Bool(x) => peripherals = if *x { true } else { false },
peripherals = if *x { Some(0) } else { None }
}
CustomArg::UInt(s) if app.args.cores != 1 => {
let x = s.parse::<u8>().ok();
peripherals = if x.is_some() && x.unwrap() < app.args.cores {
Some(x.unwrap())
} else {
return Err(parse::Error::new(
k.span(),
&format!(
"unexpected argument value; \
this should be an integer in the range 0..={}",
app.args.cores
),
));
}
}
_ => { _ => {
return Err(parse::Error::new( return Err(parse::Error::new(
k.span(), k.span(),
if app.args.cores == 1 { "unexpected argument value; this should be a boolean",
"unexpected argument value; this should be a boolean"
} else {
"unexpected argument value; this should be an integer"
},
)); ));
} }
}, },
@ -203,7 +141,7 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
} }
} }
if !analysis.timer_queues.is_empty() && monotonic.is_none() { if !&analysis.timer_queues.is_empty() && monotonic.is_none() {
return Err(parse::Error::new( return Err(parse::Error::new(
Span::call_site(), Span::call_site(),
"a `monotonic` timer must be specified to use the `schedule` API", "a `monotonic` timer must be specified to use the `schedule` API",

View file

@ -30,65 +30,52 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
let mut root = vec![]; let mut root = vec![];
let mut user = vec![]; let mut user = vec![];
// generate a `main` function for each core // Generate the `main` function
for core in 0..app.args.cores { let assertion_stmts = assertions::codegen(analysis);
let assertion_stmts = assertions::codegen(core, analysis, extra);
let (const_app_pre_init, pre_init_stmts) = pre_init::codegen(core, &app, analysis, extra); let pre_init_stmts = pre_init::codegen(&app, analysis, extra);
let (const_app_init, root_init, user_init, call_init) = let (const_app_init, root_init, user_init, call_init) = init::codegen(app, analysis, extra);
init::codegen(core, app, analysis, extra);
let (const_app_post_init, post_init_stmts) = let post_init_stmts = post_init::codegen(&app, analysis);
post_init::codegen(core, &app, analysis, extra);
let (const_app_idle, root_idle, user_idle, call_idle) = let (const_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis, extra);
idle::codegen(core, app, analysis, extra);
user.push(quote!( user.push(quote!(
#user_init #user_init
#user_idle #user_idle
)); ));
root.push(quote!( root.push(quote!(
#(#root_init)* #(#root_init)*
#(#root_idle)* #(#root_idle)*
)); ));
const_app.push(quote!( const_app.push(quote!(
#(#const_app_pre_init)* #const_app_init
#const_app_init #const_app_idle
));
#(#const_app_post_init)* let main = util::suffixed("main");
mains.push(quote!(
#[no_mangle]
unsafe extern "C" fn #main() -> ! {
let _TODO: () = ();
#const_app_idle #(#assertion_stmts)*
));
let cfg_core = util::cfg_core(core, app.args.cores); #(#pre_init_stmts)*
let main = util::suffixed("main", core);
let section = util::link_section("text", core);
mains.push(quote!(
#[no_mangle]
#section
#cfg_core
unsafe extern "C" fn #main() -> ! {
let _TODO: () = ();
#(#assertion_stmts)* #call_init
#(#pre_init_stmts)* #(#post_init_stmts)*
#call_init #call_idle
}
#(#post_init_stmts)* ));
#call_idle
}
));
}
let (const_app_resources, mod_resources) = resources::codegen(app, analysis, extra); let (const_app_resources, mod_resources) = resources::codegen(app, analysis, extra);
@ -106,18 +93,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
let const_app_schedule = schedule::codegen(app, extra); let const_app_schedule = schedule::codegen(app, extra);
let cores = app.args.cores.to_string();
let cfg_core = quote!(#[cfg(core = #cores)]);
let msg = format!(
"specified {} core{} but tried to compile for more than {0} core{1}",
app.args.cores,
if app.args.cores > 1 { "s" } else { "" }
);
let check_excess_cores = quote!(
#cfg_core
compile_error!(#msg);
);
let name = &app.name; let name = &app.name;
let device = extra.device; let device = extra.device;
quote!( quote!(
@ -136,13 +111,11 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
#(#root_software_tasks)* #(#root_software_tasks)*
/// Implementation details /// Implementation details
// the user can't access the items within this `const` item // The user can't access the items within this `const` item
const #name: () = { const #name: () = {
/// Always include the device crate which contains the vector table /// Always include the device crate which contains the vector table
use #device as _; use #device as _;
#check_excess_cores
#(#const_app)* #(#const_app)*
#(#const_app_resources)* #(#const_app_resources)*

View file

@ -1,32 +1,18 @@
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use crate::{analyze::Analysis, check::Extra}; use crate::analyze::Analysis;
/// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits
pub fn codegen(core: u8, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { pub fn codegen(analysis: &Analysis) -> Vec<TokenStream2> {
let mut stmts = vec![]; let mut stmts = vec![];
// we don't generate *all* assertions on all cores because the user could conditionally import a for ty in &analysis.send_types {
// type only on some core (e.g. `#[cfg(core = "0")] use some::Type;`) stmts.push(quote!(rtic::export::assert_send::<#ty>();));
if let Some(types) = analysis.send_types.get(&core) {
for ty in types {
stmts.push(quote!(rtic::export::assert_send::<#ty>();));
}
} }
if let Some(types) = analysis.sync_types.get(&core) { for ty in &analysis.sync_types {
for ty in types { stmts.push(quote!(rtic::export::assert_sync::<#ty>();));
stmts.push(quote!(rtic::export::assert_sync::<#ty>();));
}
}
// if the `schedule` API is used in more than one core then we need to check that the
// `monotonic` timer can be used in multi-core context
if analysis.timer_queues.len() > 1 && analysis.timer_queues.contains_key(&core) {
let monotonic = extra.monotonic();
stmts.push(quote!(rtic::export::assert_multicore::<#monotonic>();));
} }
stmts stmts

View file

@ -8,181 +8,147 @@ 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![];
for (&receiver, dispatchers) in &analysis.channels { let interrupts = &analysis.interrupts;
let interrupts = &analysis.interrupts[&receiver];
for (&level, channels) in dispatchers { for (&level, channel) in &analysis.channels {
let mut stmts = vec![]; let mut stmts = vec![];
for (&sender, channel) in channels { let variants = channel
let cfg_sender = util::cfg_core(sender, app.args.cores); .tasks
.iter()
.map(|name| {
let cfgs = &app.software_tasks[name].cfgs;
let variants = channel quote!(
.tasks #(#cfgs)*
.iter() #name
.map(|name| { )
let cfgs = &app.software_tasks[name].cfgs; })
.collect::<Vec<_>>();
quote!( let doc = format!(
#(#cfgs)* "Software tasks to be dispatched at priority level {}",
#name level,
) );
}) let t = util::spawn_t_ident(level);
.collect::<Vec<_>>(); items.push(quote!(
#[allow(non_camel_case_types)]
let doc = format!( #[derive(Clone, Copy)]
"Software tasks spawned from core #{} to be dispatched at priority level {} by core #{}", #[doc = #doc]
sender, level, receiver, enum #t {
); #(#variants,)*
let t = util::spawn_t_ident(receiver, level, sender);
items.push(quote!(
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[doc = #doc]
enum #t {
#(#variants,)*
}
));
let n = util::capacity_typenum(channel.capacity, true);
let rq = util::rq_ident(receiver, level, sender);
let (rq_attr, rq_ty, rq_expr, section) = if sender == receiver {
(
cfg_sender.clone(),
quote!(rtic::export::SCRQ<#t, #n>),
quote!(rtic::export::Queue(unsafe {
rtic::export::iQueue::u8_sc()
})),
util::link_section("bss", sender),
)
} else {
let shared = if cfg!(feature = "heterogeneous") {
Some(quote!(#[rtic::export::shared]))
} else {
None
};
(
shared,
quote!(rtic::export::MCRQ<#t, #n>),
quote!(rtic::export::Queue(rtic::export::iQueue::u8())),
None,
)
};
let doc = format!(
"Queue of tasks sent by core #{} ready to be dispatched by core #{} at priority level {}",
sender,
receiver,
level
);
items.push(quote!(
#[doc = #doc]
#rq_attr
#section
static mut #rq: #rq_ty = #rq_expr;
));
if let Some(ceiling) = channel.ceiling {
items.push(quote!(
#cfg_sender
struct #rq<'a> {
priority: &'a rtic::export::Priority,
}
));
items.push(util::impl_mutex(
extra,
&[],
cfg_sender.as_ref(),
false,
&rq,
rq_ty,
ceiling,
quote!(&mut #rq),
));
}
let arms = channel
.tasks
.iter()
.map(|name| {
let task = &app.software_tasks[name];
let cfgs = &task.cfgs;
let fq = util::fq_ident(name, sender);
let inputs = util::inputs_ident(name, sender);
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
let (let_instant, instant) = if app.uses_schedule(receiver) {
let instants = util::instants_ident(name, sender);
(
quote!(
let instant =
#instants.get_unchecked(usize::from(index)).as_ptr().read();
),
quote!(, instant),
)
} else {
(quote!(), quote!())
};
let locals_new = if task.locals.is_empty() {
quote!()
} else {
quote!(#name::Locals::new(),)
};
quote!(
#(#cfgs)*
#t::#name => {
let #tupled =
#inputs.get_unchecked(usize::from(index)).as_ptr().read();
#let_instant
#fq.split().0.enqueue_unchecked(index);
let priority = &rtic::export::Priority::new(PRIORITY);
crate::#name(
#locals_new
#name::Context::new(priority #instant)
#(,#pats)*
)
}
)
})
.collect::<Vec<_>>();
stmts.push(quote!(
while let Some((task, index)) = #rq.split().1.dequeue() {
match task {
#(#arms)*
}
}
));
} }
));
let doc = format!( let n = util::capacity_typenum(channel.capacity, true);
"Interrupt handler used by core #{} to dispatch tasks at priority {}", let rq = util::rq_ident(level);
receiver, level let (rq_ty, rq_expr) = {
); (
let cfg_receiver = util::cfg_core(receiver, app.args.cores); quote!(rtic::export::SCRQ<#t, #n>),
let section = util::link_section("text", receiver); quote!(rtic::export::Queue(unsafe {
let interrupt = util::suffixed(&interrupts[&level].to_string(), receiver); rtic::export::iQueue::u8_sc()
})),
)
};
let doc = format!(
"Queue of tasks ready to be dispatched at priority level {}",
level
);
items.push(quote!(
#[doc = #doc]
static mut #rq: #rq_ty = #rq_expr;
));
if let Some(ceiling) = channel.ceiling {
items.push(quote!( items.push(quote!(
#[allow(non_snake_case)] struct #rq<'a> {
#[doc = #doc] priority: &'a rtic::export::Priority,
#[no_mangle]
#cfg_receiver
#section
unsafe fn #interrupt() {
/// The priority of this interrupt handler
const PRIORITY: u8 = #level;
rtic::export::run(PRIORITY, || {
#(#stmts)*
});
} }
)); ));
items.push(util::impl_mutex(
extra,
&[],
false,
&rq,
rq_ty,
ceiling,
quote!(&mut #rq),
));
} }
let arms = channel
.tasks
.iter()
.map(|name| {
let task = &app.software_tasks[name];
let cfgs = &task.cfgs;
let fq = util::fq_ident(name);
let inputs = util::inputs_ident(name);
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
let (let_instant, instant) = if app.uses_schedule() {
let instants = util::instants_ident(name);
(
quote!(
let instant =
#instants.get_unchecked(usize::from(index)).as_ptr().read();
),
quote!(, instant),
)
} else {
(quote!(), quote!())
};
let locals_new = if task.locals.is_empty() {
quote!()
} else {
quote!(#name::Locals::new(),)
};
quote!(
#(#cfgs)*
#t::#name => {
let #tupled =
#inputs.get_unchecked(usize::from(index)).as_ptr().read();
#let_instant
#fq.split().0.enqueue_unchecked(index);
let priority = &rtic::export::Priority::new(PRIORITY);
crate::#name(
#locals_new
#name::Context::new(priority #instant)
#(,#pats)*
)
}
)
})
.collect::<Vec<_>>();
stmts.push(quote!(
while let Some((task, index)) = #rq.split().1.dequeue() {
match task {
#(#arms)*
}
}
));
let doc = format!("Interrupt handler to dispatch tasks at priority {}", level);
let interrupt = util::suffixed(&interrupts[&level].to_string());
items.push(quote!(
#[allow(non_snake_case)]
#[doc = #doc]
#[no_mangle]
unsafe fn #interrupt() {
/// The priority of this interrupt handler
const PRIORITY: u8 = #level;
rtic::export::run(PRIORITY, || {
#(#stmts)*
});
}
));
} }
items items

View file

@ -5,7 +5,7 @@ use rtic_syntax::{ast::App, Context};
use crate::{ use crate::{
analyze::Analysis, analyze::Analysis,
check::Extra, check::Extra,
codegen::{locals, module, resources_struct, util}, codegen::{locals, module, resources_struct},
}; };
/// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s) /// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s)
@ -29,10 +29,7 @@ pub fn codegen(
let mut user_tasks = vec![]; let mut user_tasks = vec![];
for (name, task) in &app.hardware_tasks { for (name, task) in &app.hardware_tasks {
let core = task.args.core; let (let_instant, instant) = if app.uses_schedule() {
let cfg_core = util::cfg_core(core, app.args.cores);
let (let_instant, instant) = if app.uses_schedule(core) {
let m = extra.monotonic(); let m = extra.monotonic();
( (
@ -49,19 +46,12 @@ pub fn codegen(
quote!(#name::Locals::new(),) quote!(#name::Locals::new(),)
}; };
let symbol = if cfg!(feature = "homogeneous") { let symbol = task.args.binds.clone();
util::suffixed(&task.args.binds.to_string(), core)
} else {
task.args.binds.clone()
};
let priority = task.args.priority; let priority = task.args.priority;
let section = util::link_section("text", core);
const_app.push(quote!( const_app.push(quote!(
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[no_mangle] #[no_mangle]
#section
#cfg_core
unsafe fn #symbol() { unsafe fn #symbol() {
const PRIORITY: u8 = #priority; const PRIORITY: u8 = #priority;
@ -103,8 +93,7 @@ pub fn codegen(
// `${task}Locals` // `${task}Locals`
let mut locals_pat = None; let mut locals_pat = None;
if !task.locals.is_empty() { if !task.locals.is_empty() {
let (struct_, pat) = let (struct_, pat) = locals::codegen(Context::HardwareTask(name), &task.locals, app);
locals::codegen(Context::HardwareTask(name), &task.locals, core, app);
root.push(struct_); root.push(struct_);
locals_pat = Some(pat); locals_pat = Some(pat);
@ -113,13 +102,10 @@ pub fn codegen(
let attrs = &task.attrs; let attrs = &task.attrs;
let context = &task.context; let context = &task.context;
let stmts = &task.stmts; let stmts = &task.stmts;
let section = util::link_section("text", core);
// XXX shouldn't this have a cfg_core?
let locals_pat = locals_pat.iter(); let locals_pat = locals_pat.iter();
user_tasks.push(quote!( user_tasks.push(quote!(
#(#attrs)* #(#attrs)*
#[allow(non_snake_case)] #[allow(non_snake_case)]
#section
fn #name(#(#locals_pat,)* #context: #name::Context) { fn #name(#(#locals_pat,)* #context: #name::Context) {
use rtic::Mutex as _; use rtic::Mutex as _;

View file

@ -5,12 +5,11 @@ use rtic_syntax::{ast::App, Context};
use crate::{ use crate::{
analyze::Analysis, analyze::Analysis,
check::Extra, check::Extra,
codegen::{locals, module, resources_struct, util}, codegen::{locals, module, resources_struct},
}; };
/// Generates support code for `#[idle]` functions /// Generates support code for `#[idle]` functions
pub fn codegen( pub fn codegen(
core: u8,
app: &App, app: &App,
analysis: &Analysis, analysis: &Analysis,
extra: &Extra, extra: &Extra,
@ -27,7 +26,8 @@ pub fn codegen(
// call_idle // call_idle
TokenStream2, TokenStream2,
) { ) {
if let Some(idle) = app.idles.get(&core) { if app.idles.len() > 0 {
let idle = &app.idles.first().unwrap();
let mut needs_lt = false; let mut needs_lt = false;
let mut const_app = None; let mut const_app = None;
let mut root_idle = vec![]; let mut root_idle = vec![];
@ -36,7 +36,7 @@ pub fn codegen(
if !idle.args.resources.is_empty() { if !idle.args.resources.is_empty() {
let (item, constructor) = let (item, constructor) =
resources_struct::codegen(Context::Idle(core), 0, &mut needs_lt, app, analysis); resources_struct::codegen(Context::Idle, 0, &mut needs_lt, app, analysis);
root_idle.push(item); root_idle.push(item);
const_app = Some(constructor); const_app = Some(constructor);
@ -44,26 +44,22 @@ pub fn codegen(
let name = &idle.name; let name = &idle.name;
if !idle.locals.is_empty() { if !idle.locals.is_empty() {
let (locals, pat) = locals::codegen(Context::Idle(core), &idle.locals, core, app); let (locals, pat) = locals::codegen(Context::Idle, &idle.locals, app);
locals_new = Some(quote!(#name::Locals::new())); locals_new = Some(quote!(#name::Locals::new()));
locals_pat = Some(pat); locals_pat = Some(pat);
root_idle.push(locals); root_idle.push(locals);
} }
root_idle.push(module::codegen(Context::Idle(core), needs_lt, app, extra)); root_idle.push(module::codegen(Context::Idle, needs_lt, app, extra));
let cfg_core = util::cfg_core(core, app.args.cores);
let attrs = &idle.attrs; let attrs = &idle.attrs;
let context = &idle.context; let context = &idle.context;
let stmts = &idle.stmts; let stmts = &idle.stmts;
let section = util::link_section("text", core);
let locals_pat = locals_pat.iter(); let locals_pat = locals_pat.iter();
let user_idle = Some(quote!( let user_idle = Some(quote!(
#(#attrs)* #(#attrs)*
#[allow(non_snake_case)] #[allow(non_snake_case)]
#cfg_core
#section
fn #name(#(#locals_pat,)* #context: #name::Context) -> ! { fn #name(#(#locals_pat,)* #context: #name::Context) -> ! {
use rtic::Mutex as _; use rtic::Mutex as _;

View file

@ -10,7 +10,6 @@ use crate::{
/// Generates support code for `#[init]` functions /// Generates support code for `#[init]` functions
pub fn codegen( pub fn codegen(
core: u8,
app: &App, app: &App,
analysis: &Analysis, analysis: &Analysis,
extra: &Extra, extra: &Extra,
@ -28,8 +27,8 @@ pub fn codegen(
// call_init -- the call to the user `#[init]` if there's one // call_init -- the call to the user `#[init]` if there's one
Option<TokenStream2>, Option<TokenStream2>,
) { ) {
if let Some(init) = app.inits.get(&core) { if app.inits.len() > 0 {
let cfg_core = util::cfg_core(core, app.args.cores); let init = &app.inits.first().unwrap();
let mut needs_lt = false; let mut needs_lt = false;
let name = &init.name; let name = &init.name;
@ -38,29 +37,25 @@ pub fn codegen(
let ret = { let ret = {
let late_fields = analysis let late_fields = analysis
.late_resources .late_resources
.get(&core) .iter()
.map(|resources| { .flat_map(|resources| {
resources resources.iter().map(|name| {
.iter() let ty = &app.late_resources[name].ty;
.map(|name| { let cfgs = &app.late_resources[name].cfgs;
let ty = &app.late_resources[name].ty;
let cfgs = &app.late_resources[name].cfgs;
quote!( quote!(
#(#cfgs)* #(#cfgs)*
pub #name: #ty pub #name: #ty
) )
}) })
.collect::<Vec<_>>()
}) })
.unwrap_or(vec![]); .collect::<Vec<_>>();
if !late_fields.is_empty() { if !late_fields.is_empty() {
let late_resources = util::late_resources_ident(&name); let late_resources = util::late_resources_ident(&name);
root_init.push(quote!( root_init.push(quote!(
/// Resources initialized at runtime /// Resources initialized at runtime
#cfg_core
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct #late_resources { pub struct #late_resources {
#(#late_fields),* #(#late_fields),*
@ -76,7 +71,7 @@ pub fn codegen(
let mut locals_pat = None; let mut locals_pat = None;
let mut locals_new = None; let mut locals_new = None;
if !init.locals.is_empty() { if !init.locals.is_empty() {
let (struct_, pat) = locals::codegen(Context::Init(core), &init.locals, core, app); let (struct_, pat) = locals::codegen(Context::Init, &init.locals, app);
locals_new = Some(quote!(#name::Locals::new())); locals_new = Some(quote!(#name::Locals::new()));
locals_pat = Some(pat); locals_pat = Some(pat);
@ -86,13 +81,10 @@ pub fn codegen(
let context = &init.context; let context = &init.context;
let attrs = &init.attrs; let attrs = &init.attrs;
let stmts = &init.stmts; let stmts = &init.stmts;
let section = util::link_section("text", core);
let locals_pat = locals_pat.iter(); let locals_pat = locals_pat.iter();
let user_init = Some(quote!( let user_init = Some(quote!(
#(#attrs)* #(#attrs)*
#cfg_core
#[allow(non_snake_case)] #[allow(non_snake_case)]
#section
fn #name(#(#locals_pat,)* #context: #name::Context) #ret { fn #name(#(#locals_pat,)* #context: #name::Context) #ret {
#(#stmts)* #(#stmts)*
} }
@ -101,7 +93,7 @@ pub fn codegen(
let mut const_app = None; let mut const_app = None;
if !init.args.resources.is_empty() { if !init.args.resources.is_empty() {
let (item, constructor) = let (item, constructor) =
resources_struct::codegen(Context::Init(core), 0, &mut needs_lt, app, analysis); resources_struct::codegen(Context::Init, 0, &mut needs_lt, app, analysis);
root_init.push(item); root_init.push(item);
const_app = Some(constructor); const_app = Some(constructor);
@ -112,7 +104,7 @@ 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(core), needs_lt, app, extra)); root_init.push(module::codegen(Context::Init, needs_lt, app, extra));
(const_app, root_init, user_init, call_init) (const_app, root_init, user_init, call_init)
} else { } else {

View file

@ -2,7 +2,7 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use rtic_syntax::{ use rtic_syntax::{
ast::{App, Local}, ast::{App, Local},
Context, Core, Map, Context, Map,
}; };
use crate::codegen::util; use crate::codegen::util;
@ -10,7 +10,6 @@ use crate::codegen::util;
pub fn codegen( pub fn codegen(
ctxt: Context, ctxt: Context,
locals: &Map<Local>, locals: &Map<Local>,
core: Core,
app: &App, app: &App,
) -> ( ) -> (
// locals // locals
@ -42,11 +41,6 @@ pub fn codegen(
let cfgs = &local.cfgs; let cfgs = &local.cfgs;
has_cfgs |= !cfgs.is_empty(); has_cfgs |= !cfgs.is_empty();
let section = if local.shared && cfg!(feature = "heterogeneous") {
Some(quote!(#[rtic::export::shared]))
} else {
util::link_section("data", core)
};
let expr = &local.expr; let expr = &local.expr;
let ty = &local.ty; let ty = &local.ty;
fields.push(quote!( fields.push(quote!(
@ -55,7 +49,6 @@ pub fn codegen(
)); ));
items.push(quote!( items.push(quote!(
#(#cfgs)* #(#cfgs)*
#section
static mut #name: #ty = #expr static mut #name: #ty = #expr
)); ));
values.push(quote!( values.push(quote!(

View file

@ -11,12 +11,11 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
let name = ctxt.ident(app); let name = ctxt.ident(app);
let core = ctxt.core(app);
let mut needs_instant = false; let mut needs_instant = false;
let mut lt = None; let mut lt = None;
match ctxt { match ctxt {
Context::Init(core) => { Context::Init => {
if app.uses_schedule(core) { if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
fields.push(quote!( fields.push(quote!(
@ -37,7 +36,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
)); ));
} }
if extra.peripherals == Some(core) { if extra.peripherals {
let device = extra.device; let device = extra.device;
fields.push(quote!( fields.push(quote!(
@ -51,10 +50,10 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
values.push(quote!(core)); values.push(quote!(core));
} }
Context::Idle(..) => {} Context::Idle => {}
Context::HardwareTask(..) => { Context::HardwareTask(..) => {
if app.uses_schedule(core) { if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
fields.push(quote!( fields.push(quote!(
@ -69,7 +68,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
} }
Context::SoftwareTask(..) => { Context::SoftwareTask(..) => {
if app.uses_schedule(core) { if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
fields.push(quote!( fields.push(quote!(
@ -205,7 +204,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
values.push(quote!(spawn: Spawn { priority })); values.push(quote!(spawn: Spawn { priority }));
} else { } else {
let instant_field = if app.uses_schedule(core) { let instant_field = if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
needs_instant = true; needs_instant = true;
@ -252,8 +251,8 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
} }
} }
if let Context::Init(core) = ctxt { if let Context::Init = ctxt {
let init = &app.inits[&core]; let init = &app.inits.first().unwrap();
if init.returns_late_resources { if init.returns_late_resources {
let late_resources = util::late_resources_ident(&init.name); let late_resources = util::late_resources_ident(&init.name);
@ -265,14 +264,14 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
} }
let doc = match ctxt { let doc = match ctxt {
Context::Idle(_) => "Idle loop", Context::Idle => "Idle loop",
Context::Init(_) => "Initialization function", Context::Init => "Initialization function",
Context::HardwareTask(_) => "Hardware task", Context::HardwareTask(_) => "Hardware task",
Context::SoftwareTask(_) => "Software task", Context::SoftwareTask(_) => "Software task",
}; };
let core = if ctxt.is_init() { let core = if ctxt.is_init() {
if app.uses_schedule(core) { if app.uses_schedule() {
Some(quote!(core: rtic::Peripherals,)) Some(quote!(core: rtic::Peripherals,))
} else { } else {
Some(quote!(core: rtic::export::Peripherals,)) Some(quote!(core: rtic::export::Peripherals,))
@ -312,12 +311,9 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
)); ));
if !items.is_empty() { if !items.is_empty() {
let cfg_core = util::cfg_core(ctxt.core(app), app.args.cores);
quote!( quote!(
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[doc = #doc] #[doc = #doc]
#cfg_core
pub mod #name { pub mod #name {
#(#items)* #(#items)*
} }

View file

@ -2,22 +2,17 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use rtic_syntax::ast::App; use rtic_syntax::ast::App;
use crate::{analyze::Analysis, check::Extra, codegen::util}; use crate::analyze::Analysis;
/// Generates code that runs after `#[init]` returns /// Generates code that runs after `#[init]` returns
pub fn codegen( pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
core: u8,
app: &App,
analysis: &Analysis,
extra: &Extra,
) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
let mut const_app = vec![];
let mut stmts = vec![]; let mut stmts = vec![];
// initialize late resources // Initialize late resources
if let Some(late_resources) = analysis.late_resources.get(&core) { if analysis.late_resources.len() > 0 {
for name in late_resources { // BTreeSet wrapped in a vector
// if it's live for name in analysis.late_resources.first().unwrap() {
// If it's live
let cfgs = app.late_resources[name].cfgs.clone(); let cfgs = app.late_resources[name].cfgs.clone();
if analysis.locations.get(name).is_some() { if analysis.locations.get(name).is_some() {
// Need to also include the cfgs // Need to also include the cfgs
@ -29,134 +24,8 @@ pub fn codegen(
} }
} }
if analysis.timer_queues.is_empty() { // Enable the interrupts -- this completes the `init`-ialization phase
// cross-initialization barriers -- notify *other* cores that their resources have been
// initialized
for (user, initializers) in &analysis.initialization_barriers {
if !initializers.contains(&core) {
continue;
}
let ib = util::init_barrier(*user);
let shared = if cfg!(feature = "heterogeneous") {
Some(quote!(
#[rtic::export::shared]
))
} else {
None
};
const_app.push(quote!(
#shared
static #ib: rtic::export::Barrier = rtic::export::Barrier::new();
));
stmts.push(quote!(
#ib.release();
));
}
// then wait until the other cores have initialized *our* resources
if analysis.initialization_barriers.contains_key(&core) {
let ib = util::init_barrier(core);
stmts.push(quote!(
#ib.wait();
));
}
// cross-spawn barriers: wait until other cores are ready to receive messages
for (&receiver, senders) in &analysis.spawn_barriers {
if senders.get(&core) == Some(&false) {
let sb = util::spawn_barrier(receiver);
stmts.push(quote!(
#sb.wait();
));
}
}
} else {
// if the `schedule` API is used then we'll synchronize all cores to leave the
// `init`-ialization phase at the same time. In this case the rendezvous barrier makes the
// cross-initialization and spawn barriers unnecessary
let m = extra.monotonic();
if analysis.timer_queues.len() == 1 {
// reset the monotonic timer / counter
stmts.push(quote!(
<#m as rtic::Monotonic>::reset();
));
} else {
// in the multi-core case we need a rendezvous (RV) barrier between *all* the cores that
// use the `schedule` API; otherwise one of the cores could observe the before-reset
// value of the monotonic counter
// (this may be easier to implement with `AtomicU8.fetch_sub` but that API is not
// available on ARMv6-M)
// this core will reset the monotonic counter
const FIRST: u8 = 0;
if core == FIRST {
for &i in analysis.timer_queues.keys() {
let rv = util::rendezvous_ident(i);
let shared = if cfg!(feature = "heterogeneous") {
Some(quote!(
#[rtic::export::shared]
))
} else {
None
};
const_app.push(quote!(
#shared
static #rv: rtic::export::Barrier = rtic::export::Barrier::new();
));
// wait until all the other cores have reached the RV point
if i != FIRST {
stmts.push(quote!(
#rv.wait();
));
}
}
let rv = util::rendezvous_ident(core);
stmts.push(quote!(
// the compiler fences are used to prevent `reset` from being re-ordering wrt to
// the atomic operations -- we don't know if `reset` contains load or store
// operations
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
// reset the counter
<#m as rtic::Monotonic>::reset();
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
// now unblock all the other cores
#rv.release();
));
} else {
let rv = util::rendezvous_ident(core);
// let the first core know that we have reached the RV point
stmts.push(quote!(
#rv.release();
));
let rv = util::rendezvous_ident(FIRST);
// wait until the first core has reset the monotonic timer
stmts.push(quote!(
#rv.wait();
));
}
}
}
// enable the interrupts -- this completes the `init`-ialization phase
stmts.push(quote!(rtic::export::interrupt::enable();)); stmts.push(quote!(rtic::export::interrupt::enable();));
(const_app, stmts) stmts
} }

View file

@ -5,75 +5,52 @@ use rtic_syntax::ast::App;
use crate::{analyze::Analysis, check::Extra, codegen::util}; use crate::{analyze::Analysis, check::Extra, codegen::util};
/// Generates code that runs before `#[init]` /// Generates code that runs before `#[init]`
pub fn codegen( pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
core: u8,
app: &App,
analysis: &Analysis,
extra: &Extra,
) -> (
// `const_app_pre_init` -- `static` variables for barriers
Vec<TokenStream2>,
// `pre_init_stmts`
Vec<TokenStream2>,
) {
let mut const_app = vec![];
let mut stmts = vec![]; let mut stmts = vec![];
// disable interrupts -- `init` must run with interrupts disabled // Disable interrupts -- `init` must run with interrupts disabled
stmts.push(quote!(rtic::export::interrupt::disable();)); stmts.push(quote!(rtic::export::interrupt::disable();));
// populate this core `FreeQueue`s // Populate the FreeQueue
for (name, senders) in &analysis.free_queues { for fq in &analysis.free_queues {
// Get the task name
let name = fq.0;
let task = &app.software_tasks[name]; let task = &app.software_tasks[name];
let cap = task.args.capacity; let cap = task.args.capacity;
for &sender in senders.keys() { let fq_ident = util::fq_ident(name);
if sender == core {
let fq = util::fq_ident(name, sender);
stmts.push(quote!(
(0..#cap).for_each(|i| #fq.enqueue_unchecked(i));
));
}
}
}
if app.args.cores == 1 {
stmts.push(quote!( stmts.push(quote!(
// To set the variable in cortex_m so the peripherals cannot be taken multiple times (0..#cap).for_each(|i| #fq_ident.enqueue_unchecked(i));
let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
));
} else {
stmts.push(quote!(
// NOTE(transmute) to avoid debug_assertion in multi-core mode
// (This code will go away when we drop multi-core mode)
let mut core: rtic::export::Peripherals = core::mem::transmute(());
)); ));
} }
stmts.push(quote!(
// To set the variable in cortex_m so the peripherals cannot be taken multiple times
let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
));
let device = extra.device; let device = extra.device;
let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
// unmask interrupts and set their priorities // Unmask interrupts and set their priorities
for (&priority, name) in analysis for (&priority, name) in analysis
.interrupts .interrupts
.get(&core)
.iter() .iter()
.flat_map(|interrupts| *interrupts)
.chain(app.hardware_tasks.values().flat_map(|task| { .chain(app.hardware_tasks.values().flat_map(|task| {
if !util::is_exception(&task.args.binds) { if !util::is_exception(&task.args.binds) {
Some((&task.args.priority, &task.args.binds)) Some((&task.args.priority, &task.args.binds))
} else { } else {
// we do exceptions in another pass // We do exceptions in another pass
None None
} }
})) }))
{ {
// 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)];));
// NOTE this also checks that the interrupt exists in the `Interrupt` enumeration // NOTE this also checks that the interrupt exists in the `Interrupt` enumeration
let interrupt = util::interrupt_ident(core, app.args.cores); let interrupt = util::interrupt_ident();
stmts.push(quote!( stmts.push(quote!(
core.NVIC.set_priority( core.NVIC.set_priority(
#device::#interrupt::#name, #device::#interrupt::#name,
@ -86,30 +63,7 @@ pub fn codegen(
stmts.push(quote!(rtic::export::NVIC::unmask(#device::#interrupt::#name);)); stmts.push(quote!(rtic::export::NVIC::unmask(#device::#interrupt::#name);));
} }
// cross-spawn barriers: now that priorities have been set and the interrupts have been unmasked // Set exception priorities
// we are ready to receive messages from *other* cores
if analysis.spawn_barriers.contains_key(&core) {
let sb = util::spawn_barrier(core);
let shared = if cfg!(feature = "heterogeneous") {
Some(quote!(
#[rtic::export::shared]
))
} else {
None
};
const_app.push(quote!(
#shared
static #sb: rtic::export::Barrier = rtic::export::Barrier::new();
));
// unblock cores that may send us a message
stmts.push(quote!(
#sb.release();
));
}
// set exception priorities
for (name, priority) in app.hardware_tasks.values().filter_map(|task| { for (name, priority) in app.hardware_tasks.values().filter_map(|task| {
if util::is_exception(&task.args.binds) { if util::is_exception(&task.args.binds) {
Some((&task.args.binds, task.args.priority)) Some((&task.args.binds, task.args.priority))
@ -117,7 +71,7 @@ pub fn codegen(
None None
} }
}) { }) {
// 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)];));
stmts.push(quote!(core.SCB.set_priority( stmts.push(quote!(core.SCB.set_priority(
@ -126,11 +80,11 @@ pub fn codegen(
);)); );));
} }
// initialize the SysTick // Initialize the SysTick if there exist a TimerQueue
if let Some(tq) = analysis.timer_queues.get(&core) { if let Some(tq) = analysis.timer_queues.first() {
let priority = tq.priority; let priority = tq.priority;
// 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)];));
stmts.push(quote!(core.SCB.set_priority( stmts.push(quote!(core.SCB.set_priority(
@ -145,23 +99,11 @@ pub fn codegen(
)); ));
} }
// if there's no user `#[idle]` then optimize returning from interrupt handlers // If there's no user `#[idle]` then optimize returning from interrupt handlers
if app.idles.get(&core).is_none() { if app.idles.is_empty() {
// Set SLEEPONEXIT bit to enter sleep mode when returning from ISR // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR
stmts.push(quote!(core.SCB.scr.modify(|r| r | 1 << 1);)); stmts.push(quote!(core.SCB.scr.modify(|r| r | 1 << 1);));
} }
// cross-spawn barriers: wait until other cores are ready to receive messages stmts
for (&receiver, senders) in &analysis.spawn_barriers {
// only block here if `init` can send messages to `receiver`
if senders.get(&core) == Some(&true) {
let sb = util::spawn_barrier(receiver);
stmts.push(quote!(
#sb.wait();
));
}
}
(const_app, stmts)
} }

View file

@ -1,9 +1,6 @@
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use rtic_syntax::{ use rtic_syntax::{analyze::Ownership, ast::App};
analyze::{Location, Ownership},
ast::App,
};
use crate::{analyze::Analysis, check::Extra, codegen::util}; use crate::{analyze::Analysis, check::Extra, codegen::util};
@ -21,37 +18,15 @@ pub fn codegen(
let mut const_app = vec![]; let mut const_app = vec![];
let mut mod_resources = vec![]; let mut mod_resources = vec![];
for (name, res, expr, loc) in app.resources(analysis) { for (name, res, expr, _) in app.resources(analysis) {
let cfgs = &res.cfgs; let cfgs = &res.cfgs;
let ty = &res.ty; let ty = &res.ty;
{ {
let (loc_attr, section) = match loc { let section = if expr.is_none() {
Location::Owned { util::link_section_uninit(true)
core, } else {
cross_initialized: false, None
} => (
util::cfg_core(*core, app.args.cores),
if expr.is_none() {
util::link_section_uninit(Some(*core))
} else {
util::link_section("data", *core)
},
),
// shared `static`s and cross-initialized resources need to be in `.shared` memory
_ => (
if cfg!(feature = "heterogeneous") {
Some(quote!(#[rtic::export::shared]))
} else {
None
},
if expr.is_none() {
util::link_section_uninit(None)
} else {
None
},
),
}; };
let (ty, expr) = if let Some(expr) = expr { let (ty, expr) = if let Some(expr) = expr {
@ -68,25 +43,20 @@ pub fn codegen(
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#(#attrs)* #(#attrs)*
#(#cfgs)* #(#cfgs)*
#loc_attr
#section #section
static mut #name: #ty = #expr; static mut #name: #ty = #expr;
)); ));
} }
if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) { if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) {
let cfg_core = util::cfg_core(loc.core().expect("UNREACHABLE"), app.args.cores);
mod_resources.push(quote!( mod_resources.push(quote!(
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#(#cfgs)* #(#cfgs)*
#cfg_core
pub struct #name<'a> { pub struct #name<'a> {
priority: &'a Priority, priority: &'a Priority,
} }
#(#cfgs)* #(#cfgs)*
#cfg_core
impl<'a> #name<'a> { impl<'a> #name<'a> {
#[inline(always)] #[inline(always)]
pub unsafe fn new(priority: &'a Priority) -> Self { pub unsafe fn new(priority: &'a Priority) -> Self {
@ -115,7 +85,6 @@ pub fn codegen(
const_app.push(util::impl_mutex( const_app.push(util::impl_mutex(
extra, extra,
cfgs, cfgs,
cfg_core.as_ref(),
true, true,
name, name,
quote!(#ty), quote!(#ty),

View file

@ -14,8 +14,8 @@ pub fn codegen(
let mut lt = None; let mut lt = None;
let resources = match ctxt { let resources = match ctxt {
Context::Init(core) => &app.inits[&core].args.resources, Context::Init => &app.inits.first().unwrap().args.resources,
Context::Idle(core) => &app.idles[&core].args.resources, Context::Idle => &app.idles.first().unwrap().args.resources,
Context::HardwareTask(name) => &app.hardware_tasks[name].args.resources, Context::HardwareTask(name) => &app.hardware_tasks[name].args.resources,
Context::SoftwareTask(name) => &app.software_tasks[name].args.resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.resources,
}; };
@ -39,7 +39,7 @@ pub fn codegen(
if ctxt.is_init() { if ctxt.is_init() {
if !analysis.ownerships.contains_key(name) { if !analysis.ownerships.contains_key(name) {
// owned by `init` // Owned by `init`
fields.push(quote!( fields.push(quote!(
#(#cfgs)* #(#cfgs)*
pub #name: &'static #mut_ #ty pub #name: &'static #mut_ #ty
@ -50,7 +50,7 @@ pub fn codegen(
#name: &#mut_ #name #name: &#mut_ #name
)); ));
} else { } else {
// owned by someone else // Owned by someone else
lt = Some(quote!('a)); lt = Some(quote!('a));
fields.push(quote!( fields.push(quote!(
@ -75,7 +75,7 @@ pub fn codegen(
pub #name: &'a #ty pub #name: &'a #ty
)); ));
} else { } else {
// resource proxy // Resource proxy
lt = Some(quote!('a)); lt = Some(quote!('a));
fields.push(quote!( fields.push(quote!(
@ -136,7 +136,7 @@ pub fn codegen(
if lt.is_some() { if lt.is_some() {
*needs_lt = true; *needs_lt = true;
// the struct could end up empty due to `cfg`s leading to an error due to `'a` being unused // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused
if has_cfgs { if has_cfgs {
fields.push(quote!( fields.push(quote!(
#[doc(hidden)] #[doc(hidden)]
@ -147,13 +147,9 @@ pub fn codegen(
} }
} }
let core = ctxt.core(app);
let cores = app.args.cores;
let cfg_core = util::cfg_core(core, cores);
let doc = format!("Resources `{}` has access to", ctxt.ident(app)); let doc = format!("Resources `{}` has access to", ctxt.ident(app));
let ident = util::resources_ident(ctxt, app); let ident = util::resources_ident(ctxt, app);
let item = quote!( let item = quote!(
#cfg_core
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[doc = #doc] #[doc = #doc]
pub struct #ident<#lt> { pub struct #ident<#lt> {
@ -167,7 +163,6 @@ pub fn codegen(
Some(quote!(priority: &#lt rtic::export::Priority)) Some(quote!(priority: &#lt rtic::export::Priority))
}; };
let constructor = quote!( let constructor = quote!(
#cfg_core
impl<#lt> #ident<#lt> { impl<#lt> #ident<#lt> {
#[inline(always)] #[inline(always)]
unsafe fn new(#arg) -> Self { unsafe fn new(#arg) -> Self {

View file

@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::HashSet;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
@ -13,14 +13,11 @@ use crate::{
pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> { pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> {
let mut items = vec![]; let mut items = vec![];
let mut seen = BTreeMap::<u8, HashSet<_>>::new(); let mut seen = HashSet::<_>::new();
for (scheduler, schedulees) in app.schedule_callers() { for (scheduler, schedulees) in app.schedule_callers() {
let m = extra.monotonic(); let m = extra.monotonic();
let instant = quote!(<#m as rtic::Monotonic>::Instant); let instant = quote!(<#m as rtic::Monotonic>::Instant);
let sender = scheduler.core(app);
let cfg_sender = util::cfg_core(sender, app.args.cores);
let seen = seen.entry(sender).or_default();
let mut methods = vec![]; let mut methods = vec![];
for name in schedulees { for name in schedulees {
@ -35,28 +32,23 @@ pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> {
let body = schedule_body::codegen(scheduler, &name, app); let body = schedule_body::codegen(scheduler, &name, app);
let section = util::link_section("text", sender);
methods.push(quote!( methods.push(quote!(
#(#cfgs)* #(#cfgs)*
#section
fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> { fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> {
#body #body
} }
)); ));
} else { } else {
let schedule = util::schedule_ident(name, sender); let schedule = util::schedule_ident(name);
if !seen.contains(name) { if !seen.contains(name) {
// generate a `schedule_${name}_S${sender}` function // Generate a `schedule_${name}_S${sender}` function
seen.insert(name); seen.insert(name);
let body = schedule_body::codegen(scheduler, &name, app); let body = schedule_body::codegen(scheduler, &name, app);
let section = util::link_section("text", sender);
items.push(quote!( items.push(quote!(
#cfg_sender
#(#cfgs)* #(#cfgs)*
#section
unsafe fn #schedule( unsafe fn #schedule(
priority: &rtic::export::Priority, priority: &rtic::export::Priority,
instant: #instant instant: #instant
@ -88,7 +80,6 @@ pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> {
let scheduler = scheduler.ident(app); let scheduler = scheduler.ident(app);
debug_assert!(!methods.is_empty()); debug_assert!(!methods.is_empty());
items.push(quote!( items.push(quote!(
#cfg_sender
impl<#lt> #scheduler::Schedule<#lt> { impl<#lt> #scheduler::Schedule<#lt> {
#(#methods)* #(#methods)*
} }

View file

@ -6,12 +6,10 @@ use syn::Ident;
use crate::codegen::util; use crate::codegen::util;
pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 { pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 {
let sender = scheduler.core(app);
let schedulee = &app.software_tasks[name]; let schedulee = &app.software_tasks[name];
let receiver = schedulee.args.core;
let fq = util::fq_ident(name, sender); let fq = util::fq_ident(name);
let tq = util::tq_ident(sender); let tq = util::tq_ident();
let (dequeue, enqueue) = if scheduler.is_init() { let (dequeue, enqueue) = if scheduler.is_init() {
(quote!(#fq.dequeue()), quote!(#tq.enqueue_unchecked(nr);)) (quote!(#fq.dequeue()), quote!(#tq.enqueue_unchecked(nr);))
} else { } else {
@ -21,8 +19,8 @@ pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 {
) )
}; };
let write_instant = if app.uses_schedule(receiver) { let write_instant = if app.uses_schedule() {
let instants = util::instants_ident(name, sender); let instants = util::instants_ident(name);
Some(quote!( Some(quote!(
#instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant); #instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant);
@ -32,8 +30,8 @@ pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 {
}; };
let (_, tupled, _, _) = util::regroup_inputs(&schedulee.inputs); let (_, tupled, _, _) = util::regroup_inputs(&schedulee.inputs);
let inputs = util::inputs_ident(name, sender); let inputs = util::inputs_ident(name);
let t = util::schedule_t_ident(sender); let t = util::schedule_t_ident();
quote!( quote!(
unsafe { unsafe {
use rtic::Mutex as _; use rtic::Mutex as _;

View file

@ -28,8 +28,6 @@ pub fn codegen(
let mut user_tasks = vec![]; let mut user_tasks = vec![];
for (name, task) in &app.software_tasks { for (name, task) in &app.software_tasks {
let receiver = task.args.core;
let inputs = &task.inputs; let inputs = &task.inputs;
let (_, _, _, input_ty) = util::regroup_inputs(inputs); let (_, _, _, input_ty) = util::regroup_inputs(inputs);
@ -37,103 +35,70 @@ pub fn codegen(
let cap_lit = util::capacity_literal(cap); let cap_lit = util::capacity_literal(cap);
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(free_queues) = analysis.free_queues.get(name) { if let Some(&ceiling) = analysis.free_queues.get(name) {
for (&sender, &ceiling) in free_queues { let fq = util::fq_ident(name);
let cfg_sender = util::cfg_core(sender, app.args.cores);
let fq = util::fq_ident(name, sender);
let (loc, fq_ty, fq_expr, bss, mk_uninit): ( let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
_, (
_, quote!(rtic::export::SCFQ<#cap_ty>),
_, quote!(rtic::export::Queue(unsafe {
_, rtic::export::iQueue::u8_sc()
Box<dyn Fn() -> Option<_>>, })),
) = if receiver == sender { Box::new(|| util::link_section_uninit(true)),
( )
cfg_sender.clone(), };
quote!(rtic::export::SCFQ<#cap_ty>), const_app.push(quote!(
quote!(rtic::export::Queue(unsafe { /// Queue version of a free-list that keeps track of empty slots in
rtic::export::iQueue::u8_sc() /// the following buffers
})), static mut #fq: #fq_ty = #fq_expr;
util::link_section("bss", sender), ));
Box::new(|| util::link_section_uninit(Some(sender))),
)
} else {
let shared = if cfg!(feature = "heterogeneous") {
Some(quote!(#[rtic::export::shared]))
} else {
None
};
(
shared,
quote!(rtic::export::MCFQ<#cap_ty>),
quote!(rtic::export::Queue(rtic::export::iQueue::u8())),
None,
Box::new(|| util::link_section_uninit(None)),
)
};
let loc = &loc;
// Generate a resource proxy if needed
if let Some(ceiling) = ceiling {
const_app.push(quote!( const_app.push(quote!(
/// Queue version of a free-list that keeps track of empty slots in struct #fq<'a> {
/// the following buffers priority: &'a rtic::export::Priority,
#loc }
#bss
static mut #fq: #fq_ty = #fq_expr;
)); ));
// Generate a resource proxy if needed const_app.push(util::impl_mutex(
if let Some(ceiling) = ceiling { extra,
const_app.push(quote!( &[],
#cfg_sender false,
struct #fq<'a> { &fq,
priority: &'a rtic::export::Priority, fq_ty,
} ceiling,
)); quote!(&mut #fq),
));
}
const_app.push(util::impl_mutex( let ref elems = (0..cap)
extra, .map(|_| quote!(core::mem::MaybeUninit::uninit()))
&[], .collect::<Vec<_>>();
cfg_sender.as_ref(),
false,
&fq,
fq_ty,
ceiling,
quote!(&mut #fq),
));
}
let ref elems = (0..cap) if app.uses_schedule() {
.map(|_| quote!(core::mem::MaybeUninit::uninit())) let m = extra.monotonic();
.collect::<Vec<_>>(); let instants = util::instants_ident(name);
if app.uses_schedule(receiver) {
let m = extra.monotonic();
let instants = util::instants_ident(name, sender);
let uninit = mk_uninit();
const_app.push(quote!(
#loc
#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, sender);
const_app.push(quote!( const_app.push(quote!(
#loc
#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] = static mut #instants:
[core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] =
[#(#elems,)*]; [#(#elems,)*];
)); ));
} }
let uninit = mk_uninit();
let inputs = util::inputs_ident(name);
const_app.push(quote!(
#uninit
/// Buffer that holds the inputs of a task
static mut #inputs: [core::mem::MaybeUninit<#input_ty>; #cap_lit] =
[#(#elems,)*];
));
} }
// `${task}Resources` // `${task}Resources`
@ -155,15 +120,12 @@ pub fn codegen(
// `${task}Locals` // `${task}Locals`
let mut locals_pat = None; let mut locals_pat = None;
if !task.locals.is_empty() { if !task.locals.is_empty() {
let (struct_, pat) = let (struct_, pat) = locals::codegen(Context::SoftwareTask(name), &task.locals, app);
locals::codegen(Context::SoftwareTask(name), &task.locals, receiver, app);
locals_pat = Some(pat); locals_pat = Some(pat);
root.push(struct_); root.push(struct_);
} }
let cfg_receiver = util::cfg_core(receiver, app.args.cores);
let section = util::link_section("text", receiver);
let context = &task.context; let context = &task.context;
let attrs = &task.attrs; let attrs = &task.attrs;
let cfgs = &task.cfgs; let cfgs = &task.cfgs;
@ -173,8 +135,6 @@ pub fn codegen(
#(#attrs)* #(#attrs)*
#(#cfgs)* #(#cfgs)*
#[allow(non_snake_case)] #[allow(non_snake_case)]
#cfg_receiver
#section
fn #name(#(#locals_pat,)* #context: #name::Context #(,#inputs)*) { fn #name(#(#locals_pat,)* #context: #name::Context #(,#inputs)*) {
use rtic::Mutex as _; use rtic::Mutex as _;

View file

@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::HashSet;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
@ -14,16 +14,12 @@ use crate::{
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![];
let mut seen = BTreeMap::<u8, HashSet<_>>::new(); let mut seen = HashSet::<_>::new();
for (spawner, spawnees) in app.spawn_callers() { for (spawner, spawnees) in app.spawn_callers() {
let sender = spawner.core(app);
let cfg_sender = util::cfg_core(sender, app.args.cores);
let seen = seen.entry(sender).or_default();
let mut methods = vec![]; let mut methods = vec![];
for name in spawnees { for name in spawnees {
let spawnee = &app.software_tasks[name]; let spawnee = &app.software_tasks[name];
let receiver = spawnee.args.core;
let cfgs = &spawnee.cfgs; let cfgs = &spawnee.cfgs;
let (args, _, untupled, ty) = util::regroup_inputs(&spawnee.inputs); let (args, _, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
let args = &args; let args = &args;
@ -34,7 +30,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let body = spawn_body::codegen(spawner, &name, app, analysis, extra); let body = spawn_body::codegen(spawner, &name, app, analysis, extra);
let let_instant = if app.uses_schedule(receiver) { let let_instant = if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
Some(quote!(let instant = unsafe { <#m as rtic::Monotonic>::zero() };)) Some(quote!(let instant = unsafe { <#m as rtic::Monotonic>::zero() };))
@ -42,23 +38,21 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
None None
}; };
let section = util::link_section("text", sender);
methods.push(quote!( methods.push(quote!(
#(#cfgs)* #(#cfgs)*
#section
fn #name(&self #(,#args)*) -> Result<(), #ty> { fn #name(&self #(,#args)*) -> Result<(), #ty> {
#let_instant #let_instant
#body #body
} }
)); ));
} else { } else {
let spawn = util::spawn_ident(name, sender); let spawn = util::spawn_ident(name);
if !seen.contains(name) { if !seen.contains(name) {
// generate a `spawn_${name}_S${sender}` function // Generate a `spawn_${name}_S${sender}` function
seen.insert(name); seen.insert(name);
let instant = if app.uses_schedule(receiver) { let instant = if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
Some(quote!(, instant: <#m as rtic::Monotonic>::Instant)) Some(quote!(, instant: <#m as rtic::Monotonic>::Instant))
@ -68,11 +62,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let body = spawn_body::codegen(spawner, &name, app, analysis, extra); let body = spawn_body::codegen(spawner, &name, app, analysis, extra);
let section = util::link_section("text", sender);
items.push(quote!( items.push(quote!(
#cfg_sender
#(#cfgs)* #(#cfgs)*
#section
unsafe fn #spawn( unsafe fn #spawn(
priority: &rtic::export::Priority priority: &rtic::export::Priority
#instant #instant
@ -83,7 +74,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
)); ));
} }
let (let_instant, instant) = if app.uses_schedule(receiver) { let (let_instant, instant) = if app.uses_schedule() {
let m = extra.monotonic(); let m = extra.monotonic();
( (
@ -120,7 +111,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let spawner = spawner.ident(app); let spawner = spawner.ident(app);
debug_assert!(!methods.is_empty()); debug_assert!(!methods.is_empty());
items.push(quote!( items.push(quote!(
#cfg_sender
impl<#lt> #spawner::Spawn<#lt> { impl<#lt> #spawner::Spawn<#lt> {
#(#methods)* #(#methods)*
} }

View file

@ -12,13 +12,11 @@ pub fn codegen(
analysis: &Analysis, analysis: &Analysis,
extra: &Extra, extra: &Extra,
) -> TokenStream2 { ) -> TokenStream2 {
let sender = spawner.core(app);
let spawnee = &app.software_tasks[name]; let spawnee = &app.software_tasks[name];
let priority = spawnee.args.priority; let priority = spawnee.args.priority;
let receiver = spawnee.args.core;
let write_instant = if app.uses_schedule(receiver) { let write_instant = if app.uses_schedule() {
let instants = util::instants_ident(name, sender); let instants = util::instants_ident(name);
Some(quote!( Some(quote!(
#instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant); #instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant);
@ -27,9 +25,9 @@ pub fn codegen(
None None
}; };
let t = util::spawn_t_ident(receiver, priority, sender); let t = util::spawn_t_ident(priority);
let fq = util::fq_ident(name, sender); let fq = util::fq_ident(name);
let rq = util::rq_ident(receiver, priority, sender); let rq = util::rq_ident(priority);
let (dequeue, enqueue) = if spawner.is_init() { let (dequeue, enqueue) = if spawner.is_init() {
( (
quote!(#fq.dequeue()), quote!(#fq.dequeue()),
@ -45,20 +43,16 @@ pub fn codegen(
}; };
let device = extra.device; let device = extra.device;
let enum_ = util::interrupt_ident(receiver, app.args.cores); let enum_ = util::interrupt_ident();
let interrupt = &analysis.interrupts[&receiver][&priority]; let interrupt = &analysis.interrupts.get(&priority);
let pend = if sender != receiver { let pend = {
quote!(
#device::xpend(#receiver, #device::#enum_::#interrupt);
)
} else {
quote!( quote!(
rtic::pend(#device::#enum_::#interrupt); rtic::pend(#device::#enum_::#interrupt);
) )
}; };
let (_, tupled, _, _) = util::regroup_inputs(&spawnee.inputs); let (_, tupled, _, _) = util::regroup_inputs(&spawnee.inputs);
let inputs = util::inputs_ident(name, sender); let inputs = util::inputs_ident(name);
quote!( quote!(
unsafe { unsafe {
use rtic::Mutex as _; use rtic::Mutex as _;

View file

@ -8,9 +8,8 @@ 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![];
for (&sender, timer_queue) in &analysis.timer_queues { if let Some(timer_queue) = &analysis.timer_queues.first() {
let cfg_sender = util::cfg_core(sender, app.args.cores); let t = util::schedule_t_ident();
let t = util::schedule_t_ident(sender);
// Enumeration of `schedule`-able tasks // Enumeration of `schedule`-able tasks
{ {
@ -27,9 +26,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let doc = format!("Tasks that can be scheduled from core #{}", sender); let doc = format!("Tasks that can be scheduled");
items.push(quote!( items.push(quote!(
#cfg_sender
#[doc = #doc] #[doc = #doc]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -39,27 +37,23 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
)); ));
} }
let tq = util::tq_ident(sender); let tq = util::tq_ident();
// Static variable and resource proxy // Static variable and resource proxy
{ {
let doc = format!("Core #{} timer queue", sender); let doc = format!("Timer queue");
let m = extra.monotonic(); let m = extra.monotonic();
let n = util::capacity_typenum(timer_queue.capacity, false); let n = util::capacity_typenum(timer_queue.capacity, false);
let tq_ty = quote!(rtic::export::TimerQueue<#m, #t, #n>); let tq_ty = quote!(rtic::export::TimerQueue<#m, #t, #n>);
let section = util::link_section("bss", sender);
items.push(quote!( items.push(quote!(
#cfg_sender
#[doc = #doc] #[doc = #doc]
#section
static mut #tq: #tq_ty = rtic::export::TimerQueue( static mut #tq: #tq_ty = rtic::export::TimerQueue(
rtic::export::BinaryHeap( rtic::export::BinaryHeap(
rtic::export::iBinaryHeap::new() rtic::export::iBinaryHeap::new()
) )
); );
#cfg_sender
struct #tq<'a> { struct #tq<'a> {
priority: &'a rtic::export::Priority, priority: &'a rtic::export::Priority,
} }
@ -68,7 +62,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
items.push(util::impl_mutex( items.push(util::impl_mutex(
extra, extra,
&[], &[],
cfg_sender.as_ref(),
false, false,
&tq, &tq,
tq_ty, tq_ty,
@ -88,17 +81,12 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let cfgs = &task.cfgs; let cfgs = &task.cfgs;
let priority = task.args.priority; let priority = task.args.priority;
let receiver = task.args.core; let rq = util::rq_ident(priority);
let rq = util::rq_ident(receiver, priority, sender); let rqt = util::spawn_t_ident(priority);
let rqt = util::spawn_t_ident(receiver, priority, sender); let enum_ = util::interrupt_ident();
let enum_ = util::interrupt_ident(receiver, app.args.cores); let interrupt = &analysis.interrupts.get(&priority);
let interrupt = &analysis.interrupts[&receiver][&priority];
let pend = if sender != receiver { let pend = {
quote!(
#device::xpend(#receiver, #device::#enum_::#interrupt);
)
} else {
quote!( quote!(
rtic::pend(#device::#enum_::#interrupt); rtic::pend(#device::#enum_::#interrupt);
) )
@ -118,12 +106,9 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let priority = timer_queue.priority; let priority = timer_queue.priority;
let sys_tick = util::suffixed("SysTick", sender); let sys_tick = util::suffixed("SysTick");
let section = util::link_section("text", sender);
items.push(quote!( items.push(quote!(
#[no_mangle] #[no_mangle]
#cfg_sender
#section
unsafe fn #sys_tick() { unsafe fn #sys_tick() {
use rtic::Mutex as _; use rtic::Mutex as _;
@ -137,7 +122,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
}) })
// NOTE `inline(always)` produces faster and smaller code // NOTE `inline(always)` produces faster and smaller code
.lock(#[inline(always)] .lock(#[inline(always)]
|tq| tq.dequeue()) |tq| tq.dequeue())
{ {
match task { match task {
#(#arms)* #(#arms)*
@ -148,6 +133,5 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
)); ));
} }
} }
items items
} }

View file

@ -2,7 +2,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote; use quote::quote;
use rtic_syntax::{ast::App, Context, Core}; use rtic_syntax::{ast::App, Context};
use syn::{Attribute, Ident, LitInt, PatType}; use syn::{Attribute, Ident, LitInt, PatType};
use crate::check::Extra; use crate::check::Extra;
@ -25,34 +25,15 @@ pub fn capacity_typenum(capacity: u8, round_up_to_power_of_two: bool) -> TokenSt
quote!(rtic::export::consts::#ident) quote!(rtic::export::consts::#ident)
} }
/// Generates a `#[cfg(core = "0")]` attribute if we are in multi-core mode
pub fn cfg_core(core: Core, cores: u8) -> Option<TokenStream2> {
if cores == 1 {
None
} else if cfg!(feature = "heterogeneous") {
let core = core.to_string();
Some(quote!(#[cfg(core = #core)]))
} else {
None
}
}
/// Identifier for the free queue /// Identifier for the free queue
/// pub fn fq_ident(task: &Ident) -> Ident {
/// There may be more than one free queue per task because we need one for each sender core so we Ident::new(&format!("{}_FQ", task.to_string()), Span::call_site())
/// include the sender (e.g. `S0`) in the name
pub fn fq_ident(task: &Ident, sender: Core) -> Ident {
Ident::new(
&format!("{}_S{}_FQ", task.to_string(), sender),
Span::call_site(),
)
} }
/// Generates a `Mutex` implementation /// Generates a `Mutex` implementation
pub fn impl_mutex( pub fn impl_mutex(
extra: &Extra, extra: &Extra,
cfgs: &[Attribute], cfgs: &[Attribute],
cfg_core: Option<&TokenStream2>,
resources_prefix: bool, resources_prefix: bool,
name: &Ident, name: &Ident,
ty: TokenStream2, ty: TokenStream2,
@ -68,7 +49,6 @@ pub fn impl_mutex(
let device = extra.device; let device = extra.device;
quote!( quote!(
#(#cfgs)* #(#cfgs)*
#cfg_core
impl<'a> rtic::Mutex for #path<'a> { impl<'a> rtic::Mutex for #path<'a> {
type T = #ty; type T = #ty;
@ -91,28 +71,19 @@ pub fn impl_mutex(
) )
} }
/// Generates an identifier for a cross-initialization barrier
pub fn init_barrier(initializer: Core) -> Ident {
Ident::new(&format!("IB{}", initializer), Span::call_site())
}
/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API) /// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API)
pub fn inputs_ident(task: &Ident, sender: Core) -> Ident { pub fn inputs_ident(task: &Ident) -> Ident {
Ident::new(&format!("{}_S{}_INPUTS", task, sender), Span::call_site()) Ident::new(&format!("{}_INPUTS", task), Span::call_site())
} }
/// Generates an identifier for the `INSTANTS` buffer (`schedule` API) /// Generates an identifier for the `INSTANTS` buffer (`schedule` API)
pub fn instants_ident(task: &Ident, sender: Core) -> Ident { pub fn instants_ident(task: &Ident) -> Ident {
Ident::new(&format!("{}_S{}_INSTANTS", task, sender), Span::call_site()) Ident::new(&format!("{}_INSTANTS", task), Span::call_site())
} }
pub fn interrupt_ident(core: Core, cores: u8) -> Ident { pub fn interrupt_ident() -> Ident {
let span = Span::call_site(); let span = Span::call_site();
if cores == 1 { Ident::new("Interrupt", span)
Ident::new("Interrupt", span)
} else {
Ident::new(&format!("Interrupt_{}", core), span)
}
} }
/// Whether `name` is an exception with configurable priority /// Whether `name` is an exception with configurable priority
@ -141,31 +112,12 @@ fn link_section_index() -> usize {
INDEX.fetch_add(1, Ordering::Relaxed) INDEX.fetch_add(1, Ordering::Relaxed)
} }
pub fn link_section(section: &str, core: Core) -> Option<TokenStream2> {
if cfg!(feature = "homogeneous") {
let section = format!(".{}_{}.rtic{}", section, core, link_section_index());
Some(quote!(#[link_section = #section]))
} else {
None
}
}
// NOTE `None` means in shared memory // NOTE `None` means in shared memory
pub fn link_section_uninit(core: Option<Core>) -> Option<TokenStream2> { pub fn link_section_uninit(empty_expr: bool) -> Option<TokenStream2> {
let section = if let Some(core) = core { let section = if empty_expr {
let index = link_section_index(); let index = link_section_index();
format!(".uninit.rtic{}", index)
if cfg!(feature = "homogeneous") {
format!(".uninit_{}.rtic{}", core, index)
} else {
format!(".uninit.rtic{}", index)
}
} else { } else {
if cfg!(feature = "heterogeneous") {
// `#[shared]` attribute sets the linker section
return None;
}
format!(".uninit.rtic{}", link_section_index()) format!(".uninit.rtic{}", link_section_index())
}; };
@ -175,8 +127,8 @@ pub fn link_section_uninit(core: Option<Core>) -> Option<TokenStream2> {
/// Generates a pre-reexport identifier for the "locals" struct /// Generates a pre-reexport identifier for the "locals" struct
pub fn locals_ident(ctxt: Context, app: &App) -> Ident { pub fn locals_ident(ctxt: Context, app: &App) -> Ident {
let mut s = match ctxt { let mut s = match ctxt {
Context::Init(core) => app.inits[&core].name.to_string(), Context::Init => app.inits.first().unwrap().name.to_string(),
Context::Idle(core) => app.idles[&core].name.to_string(), Context::Idle => app.idles.first().unwrap().name.to_string(),
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
}; };
@ -185,11 +137,6 @@ pub fn locals_ident(ctxt: Context, app: &App) -> Ident {
Ident::new(&s, Span::call_site()) Ident::new(&s, Span::call_site())
} }
/// Generates an identifier for a rendezvous barrier
pub fn rendezvous_ident(core: Core) -> Ident {
Ident::new(&format!("RV{}", core), Span::call_site())
}
// Regroups the inputs of a task // Regroups the inputs of a task
// //
// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] // `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`]
@ -242,8 +189,8 @@ pub fn regroup_inputs(
/// Generates a pre-reexport identifier for the "resources" struct /// Generates a pre-reexport identifier for the "resources" struct
pub fn resources_ident(ctxt: Context, app: &App) -> Ident { pub fn resources_ident(ctxt: Context, app: &App) -> Ident {
let mut s = match ctxt { let mut s = match ctxt {
Context::Init(core) => app.inits[&core].name.to_string(), Context::Init => app.inits.first().unwrap().name.to_string(),
Context::Idle(core) => app.idles[&core].name.to_string(), Context::Idle => app.idles.first().unwrap().name.to_string(),
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
}; };
@ -254,72 +201,47 @@ pub fn resources_ident(ctxt: Context, app: &App) -> Ident {
/// Generates an identifier for a ready queue /// Generates an identifier for a ready queue
/// ///
/// Each core may have several task dispatchers, one for each priority level. Each task dispatcher /// There may be several task dispatchers, one for each priority level.
/// in turn may use more than one ready queue because the queues are SPSC queues so one is needed /// The ready queues are SPSC queues
/// per sender core. pub fn rq_ident(priority: u8) -> Ident {
pub fn rq_ident(receiver: Core, priority: u8, sender: Core) -> Ident { Ident::new(&format!("P{}_RQ", priority), Span::call_site())
Ident::new(
&format!("R{}_P{}_S{}_RQ", receiver, priority, sender),
Span::call_site(),
)
} }
/// Generates an identifier for a "schedule" function /// Generates an identifier for a "schedule" function
/// ///
/// The methods of the `Schedule` structs invoke these functions. As one task may be `schedule`-ed /// The methods of the `Schedule` structs invoke these functions.
/// by different cores we need one "schedule" function per possible task-sender pair pub fn schedule_ident(name: &Ident) -> Ident {
pub fn schedule_ident(name: &Ident, sender: Core) -> Ident { Ident::new(&format!("schedule_{}", name.to_string()), Span::call_site())
Ident::new(
&format!("schedule_{}_S{}", name.to_string(), sender),
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(core: Core) -> Ident { pub fn schedule_t_ident() -> Ident {
Ident::new(&format!("T{}", core), Span::call_site()) Ident::new(&format!("T"), Span::call_site())
}
/// Generates an identifier for a cross-spawn barrier
pub fn spawn_barrier(receiver: Core) -> Ident {
Ident::new(&format!("SB{}", receiver), Span::call_site())
} }
/// Generates an identifier for a "spawn" function /// Generates an identifier for a "spawn" function
/// ///
/// The methods of the `Spawn` structs invoke these functions. As one task may be `spawn`-ed by /// The methods of the `Spawn` structs invoke these functions.
/// different cores we need one "spawn" function per possible task-sender pair pub fn spawn_ident(name: &Ident) -> Ident {
pub fn spawn_ident(name: &Ident, sender: Core) -> Ident { Ident::new(&format!("spawn_{}", name.to_string()), Span::call_site())
Ident::new(
&format!("spawn_{}_S{}", name.to_string(), sender),
Span::call_site(),
)
} }
/// Generates an identifier for the `enum` of `spawn`-able tasks /// Generates an identifier for the `enum` of `spawn`-able tasks
/// ///
/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue /// This identifier needs the same structure as the `RQ` identifier because there's one ready queue
/// for each of these `T` enums /// for each of these `T` enums
pub fn spawn_t_ident(receiver: Core, priority: u8, sender: Core) -> Ident { pub fn spawn_t_ident(priority: u8) -> Ident {
Ident::new( Ident::new(&format!("P{}_T", priority), Span::call_site())
&format!("R{}_P{}_S{}_T", receiver, priority, sender),
Span::call_site(),
)
} }
pub fn suffixed(name: &str, core: u8) -> Ident { pub fn suffixed(name: &str) -> Ident {
let span = Span::call_site(); let span = Span::call_site();
Ident::new(name, span)
if cfg!(feature = "homogeneous") {
Ident::new(&format!("{}_{}", name, core), span)
} else {
Ident::new(name, span)
}
} }
/// Generates an identifier for a timer queue /// Generates an identifier for a timer queue
/// ///
/// At most there's one timer queue per core /// At most there is one timer queue
pub fn tq_ident(core: Core) -> Ident { pub fn tq_ident() -> Ident {
Ident::new(&format!("TQ{}", core), Span::call_site()) Ident::new(&format!("TQ"), Span::call_site())
} }

View file

@ -201,12 +201,12 @@ mod tests;
/// ///
/// Attributes can be applied to the functions inside this block. These attributes will be forwarded /// Attributes can be applied to the functions inside this block. These attributes will be forwarded
/// to the interrupt handlers generated by the `app` attribute. /// to the interrupt handlers generated by the `app` attribute.
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
let mut settings = Settings::default(); let mut settings = Settings::default();
settings.optimize_priorities = true; settings.optimize_priorities = true;
settings.parse_binds = true; settings.parse_binds = true;
settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous");
settings.parse_extern_interrupt = true; settings.parse_extern_interrupt = true;
settings.parse_schedule = true; settings.parse_schedule = true;

View file

@ -1,5 +1,4 @@
// NOTE these tests are specific to the Cortex-M port; `rtic-syntax` has a more extensive test suite // NOTE these tests are specific to the Cortex-M port; `rtic-syntax` has a more extensive test suite
// that tests functionality common to all the RTIC ports // that tests functionality common to all the RTIC ports
mod multi;
mod single; mod single;

View file

@ -1,59 +0,0 @@
use quote::quote;
use rtic_syntax::Settings;
#[test]
fn analyze() {
let mut settings = Settings::default();
settings.parse_cores = true;
settings.parse_extern_interrupt = true;
let (app, analysis) = rtic_syntax::parse2(
quote!(device = pac, cores = 2),
quote!(
const APP: () = {
#[task(core = 0, priority = 1)]
fn a(_: a::Context) {}
#[task(core = 0, priority = 2)]
fn b(_: b::Context) {}
#[task(core = 1, priority = 1)]
fn c(_: c::Context) {}
#[task(core = 1, priority = 2)]
fn d(_: d::Context) {}
// first interrupt is assigned to the highest priority dispatcher
extern "C" {
#[core = 0]
fn B();
#[core = 0]
fn A();
#[core = 1]
fn A();
#[core = 1]
fn C();
}
};
),
settings,
)
.unwrap();
let analysis = crate::analyze::app(analysis, &app);
// first core
let interrupts0 = &analysis.interrupts[&0];
assert_eq!(interrupts0.len(), 2);
assert_eq!(interrupts0[&2].to_string(), "B");
assert_eq!(interrupts0[&1].to_string(), "A");
// second core
let interrupts1 = &analysis.interrupts[&1];
assert_eq!(interrupts1.len(), 2);
assert_eq!(interrupts1[&2].to_string(), "A");
assert_eq!(interrupts1[&1].to_string(), "C");
}

View file

@ -15,7 +15,7 @@ fn analyze() {
#[task(priority = 2)] #[task(priority = 2)]
fn b(_: b::Context) {} fn b(_: b::Context) {}
// first interrupt is assigned to the highest priority dispatcher // First interrupt is assigned to the highest priority dispatcher
extern "C" { extern "C" {
fn B(); fn B();
fn A(); fn A();
@ -27,7 +27,7 @@ fn analyze() {
.unwrap(); .unwrap();
let analysis = crate::analyze::app(analysis, &app); let analysis = crate::analyze::app(analysis, &app);
let interrupts = &analysis.interrupts[&0]; let interrupts = &analysis.interrupts;
assert_eq!(interrupts.len(), 2); assert_eq!(interrupts.len(), 2);
assert_eq!(interrupts[&2].to_string(), "B"); assert_eq!(interrupts[&2].to_string(), "B");
assert_eq!(interrupts[&1].to_string(), "A"); assert_eq!(interrupts[&1].to_string(), "A");

View file

@ -19,10 +19,6 @@ use crate::Fraction;
/// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively /// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively
/// makes it "wrap around" and creates an incorrect value. This is also true if the operation is /// makes it "wrap around" and creates an incorrect value. This is also true if the operation is
/// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks. /// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks.
///
/// In multi-core contexts: this value is tied to the CYCCNT of *one* core so sending it a different
/// core makes it lose its meaning -- each Cortex-M core has its own CYCCNT counter and these are
/// usually unsynchronized and may even be running at different frequencies.
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub struct Instant { pub struct Instant {
inner: i32, inner: i32,

View file

@ -12,14 +12,12 @@ pub use cortex_m::{
peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC}, peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC},
Peripherals, Peripherals,
}; };
use heapless::spsc::{MultiCore, SingleCore}; use heapless::spsc::SingleCore;
pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; pub use heapless::{consts, i::Queue as iQueue, spsc::Queue};
pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap}; pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap};
#[cfg(feature = "heterogeneous")] #[cfg(feature = "heterogeneous")]
pub use microamp::shared; pub use microamp::shared;
pub type MCFQ<N> = Queue<u8, N, u8, MultiCore>;
pub type MCRQ<T, N> = Queue<(T, u8), N, u8, MultiCore>;
pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>; pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>;
pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>; pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>;
@ -30,7 +28,7 @@ where
F: FnOnce(), F: FnOnce(),
{ {
if priority == 1 { if priority == 1 {
// if the priority of this interrupt is `1` then BASEPRI can only be `0` // If the priority of this interrupt is `1` then BASEPRI can only be `0`
f(); f();
unsafe { basepri::write(0) } unsafe { basepri::write(0) }
} else { } else {
@ -82,7 +80,7 @@ impl Priority {
} }
} }
// these two methods are used by `lock` (see below) but can't be used from the RTIC application // These two methods are used by `lock` (see below) but can't be used from the RTIC application
#[inline(always)] #[inline(always)]
fn set(&self, value: u8) { fn set(&self, value: u8) {
self.inner.set(value) self.inner.set(value)
@ -108,13 +106,6 @@ where
{ {
} }
#[inline(always)]
pub fn assert_multicore<T>()
where
T: super::MultiCore,
{
}
#[cfg(armv7m)] #[cfg(armv7m)]
#[inline(always)] #[inline(always)]
pub unsafe fn lock<T, R>( pub unsafe fn lock<T, R>(

View file

@ -28,13 +28,6 @@
//! release. //! release.
//! //!
//! [SemVer]: https://semver.org/spec/v2.0.0.html //! [SemVer]: https://semver.org/spec/v2.0.0.html
//!
//! # Cargo features
//!
//! - `heterogeneous`. This opt-in feature enables the *experimental* heterogeneous multi-core
//! support. This feature depends on unstable feature and requires the use of the nightly channel.
//!
//! - `homogeneous`. This opt-in feature enables the *experimental* homogeneous multi-core support.
#![deny(missing_docs)] #![deny(missing_docs)]
#![deny(rust_2018_compatibility)] #![deny(rust_2018_compatibility)]
@ -48,7 +41,6 @@ use cortex_m::{
interrupt::Nr, interrupt::Nr,
peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU}, peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU},
}; };
#[cfg(all(not(feature = "heterogeneous"), not(feature = "homogeneous")))]
use cortex_m_rt as _; // vector table use cortex_m_rt as _; // vector table
pub use cortex_m_rtic_macros::app; pub use cortex_m_rtic_macros::app;
pub use rtic_core::{Exclusive, Mutex}; pub use rtic_core::{Exclusive, Mutex};
@ -161,9 +153,6 @@ pub trait Monotonic {
fn zero() -> Self::Instant; fn zero() -> Self::Instant;
} }
/// A marker trait that indicates that it is correct to use this type in multi-core context
pub trait MultiCore {}
/// Sets the given `interrupt` as pending /// Sets the given `interrupt` as pending
/// ///
/// This is a convenience function around /// This is a convenience function around

View file

@ -40,7 +40,7 @@ where
mem::transmute::<_, SYST>(()).enable_interrupt(); mem::transmute::<_, SYST>(()).enable_interrupt();
} }
// set SysTick pending // Set SysTick pending
SCB::set_pendst(); SCB::set_pendst();
} }
@ -79,13 +79,13 @@ where
}; };
mem::transmute::<_, SYST>(()).set_reload(dur); mem::transmute::<_, SYST>(()).set_reload(dur);
// start counting down from the new reload // Start counting down from the new reload
mem::transmute::<_, SYST>(()).clear_current(); mem::transmute::<_, SYST>(()).clear_current();
None None
} }
} else { } else {
// the queue is empty // The queue is empty
mem::transmute::<_, SYST>(()).disable_interrupt(); mem::transmute::<_, SYST>(()).disable_interrupt();
None None