mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-29 06:54:33 +01:00
Brutally yank out multicore
This commit is contained in:
parent
c5e6d1fa49
commit
76cf14c520
30 changed files with 704 additions and 739 deletions
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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/AfoHT/rtic-syntax", branch = "multiremove", version = "0.4.0" }
|
||||||
|
|
||||||
[features]
|
|
||||||
heterogeneous = []
|
|
||||||
homogeneous = []
|
|
||||||
|
|
|
@ -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,32 +25,24 @@ 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| {
|
.filter_map(|task| {
|
||||||
if task.args.core == core {
|
|
||||||
Some(task.args.priority)
|
Some(task.args.priority)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.chain(analysis.timer_queues.get(&core).map(|tq| tq.priority))
|
.chain(analysis.timer_queues.first().map(|tq| tq.priority))
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
if !priorities.is_empty() {
|
if !priorities.is_empty() {
|
||||||
interrupts.insert(
|
interrupts =
|
||||||
core,
|
|
||||||
priorities
|
priorities
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.rev()
|
.rev()
|
||||||
.zip(app.extern_interrupts[&core].keys().cloned())
|
.zip(app.extern_interrupts.keys().cloned())
|
||||||
.collect(),
|
.collect();
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
P::new(Analysis {
|
P::new(Analysis {
|
||||||
parent: analysis,
|
parent: analysis,
|
||||||
|
|
|
@ -4,13 +4,14 @@ use proc_macro2::Span;
|
||||||
use rtic_syntax::{
|
use rtic_syntax::{
|
||||||
analyze::Analysis,
|
analyze::Analysis,
|
||||||
ast::{App, CustomArg},
|
ast::{App, CustomArg},
|
||||||
|
|
||||||
};
|
};
|
||||||
use syn::{parse, Path};
|
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 +21,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") {
|
|
||||||
// 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
|
// 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",
|
||||||
|
@ -72,9 +52,7 @@ 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
|
.extern_interrupts.keys()
|
||||||
.iter()
|
|
||||||
.flat_map(|(_, interrupts)| interrupts.keys())
|
|
||||||
{
|
{
|
||||||
let name_s = name.to_string();
|
let name_s = name.to_string();
|
||||||
|
|
||||||
|
@ -93,50 +71,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
|
.extern_interrupts.len();
|
||||||
.get(&core)
|
if need > given {
|
||||||
.map(|ei| ei.len())
|
let s = {
|
||||||
.unwrap_or(0);
|
format!(
|
||||||
if need > given {
|
"not enough `extern` interrupts to dispatch \
|
||||||
let s = if app.args.cores == 1 {
|
all software tasks (need: {}; given: {})",
|
||||||
format!(
|
need, given
|
||||||
"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"
|
||||||
|
// 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,10 +131,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 { Some(0) } else { None }
|
peripherals = if *x { true } else { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
CustomArg::UInt(s) if app.args.cores != 1 => {
|
CustomArg::UInt(s) if app.args.cores != 1 => {
|
||||||
let x = s.parse::<u8>().ok();
|
let x = s.parse::<u8>().ok();
|
||||||
peripherals = if x.is_some() && x.unwrap() < app.args.cores {
|
peripherals = if x.is_some() && x.unwrap() < app.args.cores {
|
||||||
|
@ -184,15 +151,18 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(parse::Error::new(
|
return Err(parse::Error::new(
|
||||||
k.span(),
|
k.span(),
|
||||||
if app.args.cores == 1 {
|
//if app.args.cores == 1 {
|
||||||
"unexpected argument value; this should be a boolean"
|
"unexpected argument value; this should be a boolean",
|
||||||
|
/*
|
||||||
} else {
|
} else {
|
||||||
"unexpected argument value; this should be an integer"
|
"unexpected argument value; this should be an integer"
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -203,7 +173,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",
|
||||||
|
|
226
macros/src/codegen-verbose.rs
Normal file
226
macros/src/codegen-verbose.rs
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
use quote::quote;
|
||||||
|
use rtic_syntax::ast::App;
|
||||||
|
|
||||||
|
use crate::{analyze::Analysis, check::Extra};
|
||||||
|
|
||||||
|
mod assertions;
|
||||||
|
mod dispatchers;
|
||||||
|
mod hardware_tasks;
|
||||||
|
mod idle;
|
||||||
|
mod init;
|
||||||
|
mod locals;
|
||||||
|
mod module;
|
||||||
|
mod post_init;
|
||||||
|
mod pre_init;
|
||||||
|
mod resources;
|
||||||
|
mod resources_struct;
|
||||||
|
mod schedule;
|
||||||
|
mod schedule_body;
|
||||||
|
mod software_tasks;
|
||||||
|
mod spawn;
|
||||||
|
mod spawn_body;
|
||||||
|
mod timer_queue;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
// TODO document the syntax here or in `rtic-syntax`
|
||||||
|
pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
||||||
|
let mut const_app = vec![];
|
||||||
|
let mut mains = vec![];
|
||||||
|
let mut root = vec![];
|
||||||
|
let mut user = vec![];
|
||||||
|
let mut imports = vec![];
|
||||||
|
|
||||||
|
// generate a `main` function for each core
|
||||||
|
for core in 0..app.args.cores {
|
||||||
|
let assertion_stmts = assertions::codegen(core, analysis, extra);
|
||||||
|
|
||||||
|
let (const_app_pre_init, pre_init_stmts) = pre_init::codegen(core, &app, analysis, extra);
|
||||||
|
|
||||||
|
let (const_app_init, _root_init, user_init, user_init_imports, call_init) =
|
||||||
|
init::codegen(core, app, analysis, extra);
|
||||||
|
|
||||||
|
let (const_app_post_init, post_init_stmts) =
|
||||||
|
post_init::codegen(core, &app, analysis, extra);
|
||||||
|
|
||||||
|
let (const_app_idle, _root_idle, user_idle, user_idle_imports, call_idle) =
|
||||||
|
idle::codegen(core, app, analysis, extra);
|
||||||
|
|
||||||
|
user.push(quote!(
|
||||||
|
/// USER INIT
|
||||||
|
#user_init
|
||||||
|
|
||||||
|
/// USER IDLE
|
||||||
|
#user_idle
|
||||||
|
));
|
||||||
|
|
||||||
|
// Stow away the imports generated for each core
|
||||||
|
imports.push(quote!(
|
||||||
|
/// USER IMPORTS
|
||||||
|
#(#user_init_imports)*
|
||||||
|
|
||||||
|
/// USER IDLE
|
||||||
|
#(#user_idle_imports)*
|
||||||
|
));
|
||||||
|
|
||||||
|
root.push(quote!(
|
||||||
|
#(#_root_init)*
|
||||||
|
|
||||||
|
#(#_root_idle)*
|
||||||
|
));
|
||||||
|
|
||||||
|
const_app.push(quote!(
|
||||||
|
#(#const_app_pre_init)*
|
||||||
|
|
||||||
|
#const_app_init
|
||||||
|
|
||||||
|
#(#const_app_post_init)*
|
||||||
|
|
||||||
|
#const_app_idle
|
||||||
|
));
|
||||||
|
|
||||||
|
let cfg_core = util::cfg_core(core, app.args.cores);
|
||||||
|
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() -> ! {
|
||||||
|
#(#assertion_stmts)*
|
||||||
|
|
||||||
|
#(#pre_init_stmts)*
|
||||||
|
|
||||||
|
#call_init
|
||||||
|
|
||||||
|
#(#post_init_stmts)*
|
||||||
|
|
||||||
|
#call_idle
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (const_app_resources, mod_resources, mod_resources_imports) =
|
||||||
|
resources::codegen(app, analysis, extra);
|
||||||
|
|
||||||
|
let (
|
||||||
|
const_app_hardware_tasks,
|
||||||
|
root_hardware_tasks,
|
||||||
|
user_hardware_tasks,
|
||||||
|
user_hardware_tasks_imports,
|
||||||
|
) = hardware_tasks::codegen(app, analysis, extra);
|
||||||
|
|
||||||
|
let (
|
||||||
|
const_app_software_tasks,
|
||||||
|
root_software_tasks,
|
||||||
|
user_software_tasks,
|
||||||
|
user_software_tasks_imports,
|
||||||
|
) = software_tasks::codegen(app, analysis, extra);
|
||||||
|
|
||||||
|
let const_app_dispatchers = dispatchers::codegen(app, analysis, extra);
|
||||||
|
|
||||||
|
let const_app_spawn = spawn::codegen(app, analysis, extra);
|
||||||
|
|
||||||
|
let const_app_timer_queue = timer_queue::codegen(app, analysis, 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);
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
for s in root.clone() {
|
||||||
|
println!("{}", s.to_string());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
let user_imports = app.user_imports.clone();
|
||||||
|
let user_code = app.user_code.clone();
|
||||||
|
let name = &app.name;
|
||||||
|
let device = extra.device;
|
||||||
|
let endresult = quote!(
|
||||||
|
/// USER
|
||||||
|
#(#user)*
|
||||||
|
|
||||||
|
/// USER_HW_TASKS
|
||||||
|
#(#user_hardware_tasks)*
|
||||||
|
|
||||||
|
/// USER_SW_TASKS
|
||||||
|
#(#user_software_tasks)*
|
||||||
|
|
||||||
|
/// ROOT
|
||||||
|
//#(#root)*
|
||||||
|
|
||||||
|
/// MOD_RESOURCES
|
||||||
|
#mod_resources
|
||||||
|
|
||||||
|
/// root_hardware_tasks
|
||||||
|
#(#root_hardware_tasks)*
|
||||||
|
|
||||||
|
/// root_software_tasks
|
||||||
|
#(#root_software_tasks)*
|
||||||
|
|
||||||
|
/// Implementation details
|
||||||
|
mod #name {
|
||||||
|
/// Always include the device crate which contains the vector table
|
||||||
|
use #device as _;
|
||||||
|
#(#imports)*
|
||||||
|
/// User imports
|
||||||
|
#(#user_imports)*
|
||||||
|
|
||||||
|
/// User code from within the module
|
||||||
|
#(#user_code)*
|
||||||
|
|
||||||
|
/// User hardware tasks import
|
||||||
|
#(#user_hardware_tasks_imports)*
|
||||||
|
|
||||||
|
/// User software_tasks
|
||||||
|
#(#user_software_tasks_imports)*
|
||||||
|
|
||||||
|
/// Mod resources imports
|
||||||
|
#(#mod_resources_imports)*
|
||||||
|
|
||||||
|
#check_excess_cores
|
||||||
|
|
||||||
|
/// Const app
|
||||||
|
#(#const_app)*
|
||||||
|
|
||||||
|
/// Const app resources
|
||||||
|
#(#const_app_resources)*
|
||||||
|
|
||||||
|
/// Const app hw tasks
|
||||||
|
#(#const_app_hardware_tasks)*
|
||||||
|
|
||||||
|
/// Const app sw tasks
|
||||||
|
#(#const_app_software_tasks)*
|
||||||
|
|
||||||
|
/// Const app dispatchers
|
||||||
|
#(#const_app_dispatchers)*
|
||||||
|
|
||||||
|
/// Const app spawn
|
||||||
|
#(#const_app_spawn)*
|
||||||
|
/// Const app spawn end
|
||||||
|
|
||||||
|
#(#const_app_timer_queue)*
|
||||||
|
|
||||||
|
#(#const_app_schedule)*
|
||||||
|
|
||||||
|
/// Mains
|
||||||
|
#(#mains)*
|
||||||
|
}
|
||||||
|
);
|
||||||
|
for s in endresult.clone() {
|
||||||
|
eprintln!("{}", s.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
endresult
|
||||||
|
}
|
|
@ -30,65 +30,60 @@ 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(core, app, analysis, extra);
|
init::codegen(app, analysis, extra);
|
||||||
|
|
||||||
let (const_app_post_init, post_init_stmts) =
|
let (const_app_post_init, post_init_stmts) =
|
||||||
post_init::codegen(core, &app, analysis, extra);
|
post_init::codegen(&app, analysis);
|
||||||
|
|
||||||
let (const_app_idle, root_idle, user_idle, call_idle) =
|
let (const_app_idle, root_idle, user_idle, call_idle) =
|
||||||
idle::codegen(core, app, analysis, extra);
|
idle::codegen(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_post_init)*
|
||||||
|
|
||||||
#(#const_app_post_init)*
|
#const_app_idle
|
||||||
|
));
|
||||||
|
|
||||||
#const_app_idle
|
let main = util::suffixed("main");
|
||||||
));
|
let section = util::link_section("text");
|
||||||
|
mains.push(quote!(
|
||||||
|
#[no_mangle]
|
||||||
|
#section
|
||||||
|
unsafe extern "C" fn #main() -> ! {
|
||||||
|
let _TODO: () = ();
|
||||||
|
|
||||||
let cfg_core = util::cfg_core(core, app.args.cores);
|
#(#assertion_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)*
|
#(#pre_init_stmts)*
|
||||||
|
|
||||||
#(#pre_init_stmts)*
|
#call_init
|
||||||
|
|
||||||
#call_init
|
#(#post_init_stmts)*
|
||||||
|
|
||||||
#(#post_init_stmts)*
|
#call_idle
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
#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 +101,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!(
|
||||||
|
@ -141,8 +124,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
||||||
/// 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)*
|
||||||
|
|
|
@ -1,33 +1,35 @@
|
||||||
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
|
// we don't generate *all* assertions on all cores because the user could conditionally import a
|
||||||
// type only on some core (e.g. `#[cfg(core = "0")] use some::Type;`)
|
// type only on some core (e.g. `#[cfg(core = "0")] use some::Type;`)
|
||||||
|
|
||||||
if let Some(types) = analysis.send_types.get(&core) {
|
//if let Some(types) = analysis.send_types {
|
||||||
for ty in types {
|
for ty in &analysis.send_types {
|
||||||
stmts.push(quote!(rtic::export::assert_send::<#ty>();));
|
stmts.push(quote!(rtic::export::assert_send::<#ty>();));
|
||||||
}
|
}
|
||||||
}
|
//}
|
||||||
|
|
||||||
if let Some(types) = analysis.sync_types.get(&core) {
|
//if let Some(types) = analysis.sync_types {
|
||||||
for ty in types {
|
for ty in &analysis.sync_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
|
// 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
|
// `monotonic` timer can be used in multi-core context
|
||||||
|
/*
|
||||||
if analysis.timer_queues.len() > 1 && analysis.timer_queues.contains_key(&core) {
|
if analysis.timer_queues.len() > 1 && analysis.timer_queues.contains_key(&core) {
|
||||||
let monotonic = extra.monotonic();
|
let monotonic = extra.monotonic();
|
||||||
stmts.push(quote!(rtic::export::assert_multicore::<#monotonic>();));
|
stmts.push(quote!(rtic::export::assert_multicore::<#monotonic>();));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
stmts
|
stmts
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,181 +8,151 @@ 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, section) = {
|
||||||
);
|
(
|
||||||
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()
|
||||||
|
})),
|
||||||
|
util::link_section("bss"),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let doc = format!(
|
||||||
|
"Queue of tasks ready to be dispatched at priority level {}",
|
||||||
|
level
|
||||||
|
);
|
||||||
|
items.push(quote!(
|
||||||
|
#[doc = #doc]
|
||||||
|
#section
|
||||||
|
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 section = util::link_section("text");
|
||||||
|
let interrupt = util::suffixed(&interrupts[&level].to_string());
|
||||||
|
items.push(quote!(
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[doc = #doc]
|
||||||
|
#[no_mangle]
|
||||||
|
#section
|
||||||
|
unsafe fn #interrupt() {
|
||||||
|
/// The priority of this interrupt handler
|
||||||
|
const PRIORITY: u8 = #level;
|
||||||
|
|
||||||
|
rtic::export::run(PRIORITY, || {
|
||||||
|
#(#stmts)*
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
|
@ -29,10 +29,8 @@ 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 cfg_core = util::cfg_core(core, app.args.cores);
|
|
||||||
|
|
||||||
let (let_instant, instant) = if app.uses_schedule(core) {
|
let (let_instant, instant) = if app.uses_schedule() {
|
||||||
let m = extra.monotonic();
|
let m = extra.monotonic();
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -49,19 +47,14 @@ 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);
|
let section = util::link_section("text");
|
||||||
const_app.push(quote!(
|
const_app.push(quote!(
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#section
|
#section
|
||||||
#cfg_core
|
|
||||||
unsafe fn #symbol() {
|
unsafe fn #symbol() {
|
||||||
const PRIORITY: u8 = #priority;
|
const PRIORITY: u8 = #priority;
|
||||||
|
|
||||||
|
@ -104,7 +97,7 @@ pub fn codegen(
|
||||||
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, core, app);
|
locals::codegen(Context::HardwareTask(name), &task.locals, app);
|
||||||
|
|
||||||
root.push(struct_);
|
root.push(struct_);
|
||||||
locals_pat = Some(pat);
|
locals_pat = Some(pat);
|
||||||
|
@ -113,8 +106,7 @@ 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);
|
let section = util::link_section("text");
|
||||||
// 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)*
|
||||||
|
|
|
@ -10,7 +10,6 @@ use crate::{
|
||||||
|
|
||||||
/// 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,9 @@ pub fn codegen(
|
||||||
// call_idle
|
// call_idle
|
||||||
TokenStream2,
|
TokenStream2,
|
||||||
) {
|
) {
|
||||||
if let Some(idle) = app.idles.get(&core) {
|
//if let Some(idle) = app.idles.get(&core) {
|
||||||
|
if app.idles.len() > 0 {
|
||||||
|
let idle = &app.idles[0];
|
||||||
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 +37,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,25 +45,23 @@ 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 section = util::link_section("text");
|
||||||
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
|
#section
|
||||||
fn #name(#(#locals_pat,)* #context: #name::Context) -> ! {
|
fn #name(#(#locals_pat,)* #context: #name::Context) -> ! {
|
||||||
use rtic::Mutex as _;
|
use rtic::Mutex as _;
|
||||||
|
|
|
@ -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,9 @@ 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 let Some(init) = app.inits.get(&core) {
|
||||||
let cfg_core = util::cfg_core(core, app.args.cores);
|
if app.inits.len() > 0 {
|
||||||
|
let init = &app.inits[0];
|
||||||
let mut needs_lt = false;
|
let mut needs_lt = false;
|
||||||
let name = &init.name;
|
let name = &init.name;
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ 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()
|
.iter()
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
|
@ -51,16 +51,14 @@ pub fn codegen(
|
||||||
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 +74,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,11 +84,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 section = util::link_section("text");
|
||||||
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
|
#section
|
||||||
fn #name(#(#locals_pat,)* #context: #name::Context) #ret {
|
fn #name(#(#locals_pat,)* #context: #name::Context) #ret {
|
||||||
|
@ -101,7 +98,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 +109,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 {
|
||||||
|
|
|
@ -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,13 @@ 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") {
|
let section = if local.shared && cfg!(feature = "heterogeneous") {
|
||||||
Some(quote!(#[rtic::export::shared]))
|
Some(quote!(#[rtic::export::shared]))
|
||||||
} else {
|
} else {
|
||||||
util::link_section("data", core)
|
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 +56,7 @@ pub fn codegen(
|
||||||
));
|
));
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#section
|
//#section
|
||||||
static mut #name: #ty = #expr
|
static mut #name: #ty = #expr
|
||||||
));
|
));
|
||||||
values.push(quote!(
|
values.push(quote!(
|
||||||
|
|
|
@ -11,12 +11,12 @@ 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 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 +37,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 +51,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 +69,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 +205,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 +252,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[0];
|
||||||
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 +265,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 +312,12 @@ 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);
|
//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
|
//#cfg_core
|
||||||
pub mod #name {
|
pub mod #name {
|
||||||
#(#items)*
|
#(#items)*
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,24 @@ 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(
|
||||||
core: u8,
|
|
||||||
app: &App,
|
app: &App,
|
||||||
analysis: &Analysis,
|
analysis: &Analysis,
|
||||||
extra: &Extra,
|
|
||||||
) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
|
) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
|
||||||
let mut const_app = vec![];
|
//#TODO remove
|
||||||
|
let 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 let Some(late_resources) = analysis.late_resources {
|
||||||
for name in late_resources {
|
//for name in late_resources {
|
||||||
|
if analysis.late_resources.len() > 0 {
|
||||||
|
// #TODO, check soundness of this, why the wrapping
|
||||||
|
// BTreeSet wrapped in a vector
|
||||||
|
for name in &analysis.late_resources[0] {
|
||||||
// if it's live
|
// 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() {
|
||||||
|
@ -29,7 +32,9 @@ pub fn codegen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
if analysis.timer_queues.is_empty() {
|
if analysis.timer_queues.is_empty() {
|
||||||
|
/*
|
||||||
// cross-initialization barriers -- notify *other* cores that their resources have been
|
// cross-initialization barriers -- notify *other* cores that their resources have been
|
||||||
// initialized
|
// initialized
|
||||||
for (user, initializers) in &analysis.initialization_barriers {
|
for (user, initializers) in &analysis.initialization_barriers {
|
||||||
|
@ -55,7 +60,9 @@ pub fn codegen(
|
||||||
#ib.release();
|
#ib.release();
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
// then wait until the other cores have initialized *our* resources
|
// then wait until the other cores have initialized *our* resources
|
||||||
if analysis.initialization_barriers.contains_key(&core) {
|
if analysis.initialization_barriers.contains_key(&core) {
|
||||||
let ib = util::init_barrier(core);
|
let ib = util::init_barrier(core);
|
||||||
|
@ -75,6 +82,7 @@ pub fn codegen(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
// if the `schedule` API is used then we'll synchronize all cores to leave the
|
// 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
|
// `init`-ialization phase at the same time. In this case the rendezvous barrier makes the
|
||||||
|
@ -154,6 +162,7 @@ pub fn codegen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// enable the interrupts -- this completes the `init`-ialization phase
|
// enable the interrupts -- this completes the `init`-ialization phase
|
||||||
stmts.push(quote!(rtic::export::interrupt::enable();));
|
stmts.push(quote!(rtic::export::interrupt::enable();));
|
||||||
|
|
|
@ -6,60 +6,44 @@ 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(
|
||||||
core: u8,
|
|
||||||
app: &App,
|
app: &App,
|
||||||
analysis: &Analysis,
|
analysis: &Analysis,
|
||||||
extra: &Extra,
|
extra: &Extra,
|
||||||
) -> (
|
) ->
|
||||||
// `const_app_pre_init` -- `static` variables for barriers
|
|
||||||
Vec<TokenStream2>,
|
|
||||||
// `pre_init_stmts`
|
// `pre_init_stmts`
|
||||||
Vec<TokenStream2>,
|
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))
|
||||||
|
@ -73,7 +57,7 @@ pub fn codegen(
|
||||||
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,
|
||||||
|
@ -88,6 +72,7 @@ pub fn codegen(
|
||||||
|
|
||||||
// cross-spawn barriers: now that priorities have been set and the interrupts have been unmasked
|
// cross-spawn barriers: now that priorities have been set and the interrupts have been unmasked
|
||||||
// we are ready to receive messages from *other* cores
|
// we are ready to receive messages from *other* cores
|
||||||
|
/*
|
||||||
if analysis.spawn_barriers.contains_key(&core) {
|
if analysis.spawn_barriers.contains_key(&core) {
|
||||||
let sb = util::spawn_barrier(core);
|
let sb = util::spawn_barrier(core);
|
||||||
let shared = if cfg!(feature = "heterogeneous") {
|
let shared = if cfg!(feature = "heterogeneous") {
|
||||||
|
@ -108,6 +93,7 @@ pub fn codegen(
|
||||||
#sb.release();
|
#sb.release();
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// set exception priorities
|
// 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| {
|
||||||
|
@ -126,8 +112,8 @@ 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
|
||||||
|
@ -146,11 +132,12 @@ 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
|
// cross-spawn barriers: wait until other cores are ready to receive messages
|
||||||
for (&receiver, senders) in &analysis.spawn_barriers {
|
for (&receiver, senders) in &analysis.spawn_barriers {
|
||||||
// only block here if `init` can send messages to `receiver`
|
// only block here if `init` can send messages to `receiver`
|
||||||
|
@ -162,6 +149,7 @@ pub fn codegen(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
(const_app, stmts)
|
stmts
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
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::{Location, Ownership},
|
analyze::Ownership,
|
||||||
ast::App,
|
ast::App,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,38 +21,29 @@ 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 loc_attr = None;
|
||||||
Location::Owned {
|
let section = if expr.is_none() {
|
||||||
core,
|
util::link_section_uninit(true)
|
||||||
cross_initialized: false,
|
|
||||||
} => (
|
|
||||||
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 {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
};
|
||||||
|
/*
|
||||||
|
let (loc_attr, section) = match loc {
|
||||||
|
Location::Owned => (
|
||||||
|
None,
|
||||||
if expr.is_none() {
|
if expr.is_none() {
|
||||||
util::link_section_uninit(None)
|
util::link_section_uninit(true)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
let (ty, expr) = if let Some(expr) = expr {
|
let (ty, expr) = if let Some(expr) = expr {
|
||||||
(quote!(#ty), quote!(#expr))
|
(quote!(#ty), quote!(#expr))
|
||||||
|
@ -68,25 +59,22 @@ pub fn codegen(
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#loc_attr
|
//#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 +103,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),
|
||||||
|
|
|
@ -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[0].args.resources,
|
||||||
Context::Idle(core) => &app.idles[&core].args.resources,
|
Context::Idle => &app.idles[0].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,
|
||||||
};
|
};
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -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,7 +32,7 @@ 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);
|
let section = util::link_section("text");
|
||||||
methods.push(quote!(
|
methods.push(quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#section
|
#section
|
||||||
|
@ -44,7 +41,7 @@ pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> {
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
} 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
|
||||||
|
@ -52,9 +49,8 @@ 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);
|
let section = util::link_section("text");
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#cfg_sender
|
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#section
|
#section
|
||||||
unsafe fn #schedule(
|
unsafe fn #schedule(
|
||||||
|
@ -88,7 +84,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)*
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 _;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -38,102 +36,73 @@ pub fn codegen(
|
||||||
let cap_ty = util::capacity_typenum(cap, true);
|
let cap_ty = util::capacity_typenum(cap, true);
|
||||||
|
|
||||||
// create free queues and inputs / instants buffers
|
// create free queues and inputs / instants buffers
|
||||||
if let Some(free_queues) = analysis.free_queues.get(name) {
|
//if let Some(free_queues) = analysis.free_queues.get(name) {
|
||||||
for (&sender, &ceiling) in free_queues {
|
//for (&sender, &ceiling) in free_queues {
|
||||||
let cfg_sender = util::cfg_core(sender, app.args.cores);
|
if let Some(&ceiling) = analysis.free_queues.get(name) {
|
||||||
let fq = util::fq_ident(name, sender);
|
let fq = util::fq_ident(name);
|
||||||
|
|
||||||
let (loc, fq_ty, fq_expr, bss, mk_uninit): (
|
let (fq_ty, fq_expr, bss, 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 {
|
util::link_section("bss"),
|
||||||
(
|
Box::new(|| util::link_section_uninit(true)),
|
||||||
cfg_sender.clone(),
|
)
|
||||||
quote!(rtic::export::SCFQ<#cap_ty>),
|
};
|
||||||
quote!(rtic::export::Queue(unsafe {
|
const_app.push(quote!(
|
||||||
rtic::export::iQueue::u8_sc()
|
/// Queue version of a free-list that keeps track of empty slots in
|
||||||
})),
|
/// the following buffers
|
||||||
util::link_section("bss", sender),
|
#bss
|
||||||
Box::new(|| util::link_section_uninit(Some(sender))),
|
static mut #fq: #fq_ty = #fq_expr;
|
||||||
)
|
));
|
||||||
} 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 +124,13 @@ 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");
|
||||||
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,7 +140,6 @@ pub fn codegen(
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#cfg_receiver
|
|
||||||
#section
|
#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 _;
|
||||||
|
|
|
@ -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,13 @@ 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 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 +31,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,7 +39,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let section = util::link_section("text", sender);
|
let section = util::link_section("text");
|
||||||
methods.push(quote!(
|
methods.push(quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#section
|
#section
|
||||||
|
@ -52,13 +49,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
} 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,9 +65,9 @@ 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);
|
let section = util::link_section("text");
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#cfg_sender
|
//#cfg_sender
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#section
|
#section
|
||||||
unsafe fn #spawn(
|
unsafe fn #spawn(
|
||||||
|
@ -83,7 +80,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 +117,7 @@ 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
|
//#cfg_sender
|
||||||
impl<#lt> #spawner::Spawn<#lt> {
|
impl<#lt> #spawner::Spawn<#lt> {
|
||||||
#(#methods)*
|
#(#methods)*
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,15 @@ 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!(
|
||||||
quote!(
|
|
||||||
#device::xpend(#receiver, #device::#enum_::#interrupt);
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
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 _;
|
||||||
|
|
|
@ -8,9 +8,9 @@ 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 cfg_sender = util::cfg_core(sender, app.args.cores);
|
||||||
let t = util::schedule_t_ident(sender);
|
let t = util::schedule_t_ident();
|
||||||
|
|
||||||
// Enumeration of `schedule`-able tasks
|
// Enumeration of `schedule`-able tasks
|
||||||
{
|
{
|
||||||
|
@ -27,9 +27,9 @@ 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
|
//#cfg_sender
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -39,18 +39,18 @@ 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);
|
let section = util::link_section("bss");
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#cfg_sender
|
//#cfg_sender
|
||||||
#[doc = #doc]
|
#[doc = #doc]
|
||||||
#section
|
#section
|
||||||
static mut #tq: #tq_ty = rtic::export::TimerQueue(
|
static mut #tq: #tq_ty = rtic::export::TimerQueue(
|
||||||
|
@ -59,7 +59,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
#cfg_sender
|
//#cfg_sender
|
||||||
struct #tq<'a> {
|
struct #tq<'a> {
|
||||||
priority: &'a rtic::export::Priority,
|
priority: &'a rtic::export::Priority,
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ 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(),
|
//cfg_sender.as_ref(),
|
||||||
false,
|
false,
|
||||||
&tq,
|
&tq,
|
||||||
tq_ty,
|
tq_ty,
|
||||||
|
@ -88,17 +88,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,11 +113,11 @@ 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);
|
let section = util::link_section("text");
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#cfg_sender
|
//#cfg_sender
|
||||||
#section
|
#section
|
||||||
unsafe fn #sys_tick() {
|
unsafe fn #sys_tick() {
|
||||||
use rtic::Mutex as _;
|
use rtic::Mutex as _;
|
||||||
|
@ -137,7 +132,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 +143,5 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items
|
items
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +25,7 @@ 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
|
/// Generates a `#[cfg(core = "0")]` attribute if we are in multi-core mode
|
||||||
pub fn cfg_core(core: Core, cores: u8) -> Option<TokenStream2> {
|
pub fn cfg_core(core: Core, cores: u8) -> Option<TokenStream2> {
|
||||||
if cores == 1 {
|
if cores == 1 {
|
||||||
|
@ -36,14 +37,15 @@ pub fn cfg_core(core: Core, cores: u8) -> Option<TokenStream2> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/// Identifier for the free queue
|
/// Identifier for the free queue
|
||||||
///
|
///
|
||||||
/// There may be more than one free queue per task because we need one for each sender core so we
|
/// There may be more than one free queue per task because we need one for each sender core so we
|
||||||
/// include the sender (e.g. `S0`) in the name
|
/// include the sender (e.g. `S0`) in the name
|
||||||
pub fn fq_ident(task: &Ident, sender: Core) -> Ident {
|
pub fn fq_ident(task: &Ident) -> Ident {
|
||||||
Ident::new(
|
Ident::new(
|
||||||
&format!("{}_S{}_FQ", task.to_string(), sender),
|
&format!("{}_FQ", task.to_string()),
|
||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -52,7 +54,7 @@ pub fn fq_ident(task: &Ident, sender: Core) -> Ident {
|
||||||
pub fn impl_mutex(
|
pub fn impl_mutex(
|
||||||
extra: &Extra,
|
extra: &Extra,
|
||||||
cfgs: &[Attribute],
|
cfgs: &[Attribute],
|
||||||
cfg_core: Option<&TokenStream2>,
|
//cfg_core: Option<&TokenStream2>,
|
||||||
resources_prefix: bool,
|
resources_prefix: bool,
|
||||||
name: &Ident,
|
name: &Ident,
|
||||||
ty: TokenStream2,
|
ty: TokenStream2,
|
||||||
|
@ -68,7 +70,7 @@ pub fn impl_mutex(
|
||||||
let device = extra.device;
|
let device = extra.device;
|
||||||
quote!(
|
quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#cfg_core
|
//#cfg_core
|
||||||
impl<'a> rtic::Mutex for #path<'a> {
|
impl<'a> rtic::Mutex for #path<'a> {
|
||||||
type T = #ty;
|
type T = #ty;
|
||||||
|
|
||||||
|
@ -91,28 +93,26 @@ pub fn impl_mutex(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/// Generates an identifier for a cross-initialization barrier
|
/// Generates an identifier for a cross-initialization barrier
|
||||||
pub fn init_barrier(initializer: Core) -> Ident {
|
pub fn init_barrier(initializer: Core) -> Ident {
|
||||||
Ident::new(&format!("IB{}", initializer), Span::call_site())
|
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 +141,24 @@ 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> {
|
pub fn link_section(_section: &str) -> Option<TokenStream2> {
|
||||||
|
/*
|
||||||
if cfg!(feature = "homogeneous") {
|
if cfg!(feature = "homogeneous") {
|
||||||
let section = format!(".{}_{}.rtic{}", section, core, link_section_index());
|
let section = format!(".{}_{}.rtic{}", section, core, link_section_index());
|
||||||
Some(quote!(#[link_section = #section]))
|
Some(quote!(#[link_section = #section]))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
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 +168,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[0].name.to_string(),
|
||||||
Context::Idle(core) => app.idles[&core].name.to_string(),
|
Context::Idle => app.idles[0].name.to_string(),
|
||||||
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
|
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -185,10 +178,12 @@ 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
|
/// Generates an identifier for a rendezvous barrier
|
||||||
pub fn rendezvous_ident(core: Core) -> Ident {
|
pub fn rendezvous_ident() -> Ident {
|
||||||
Ident::new(&format!("RV{}", core), Span::call_site())
|
Ident::new(&format!("RV"), Span::call_site())
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Regroups the inputs of a task
|
// Regroups the inputs of a task
|
||||||
//
|
//
|
||||||
|
@ -242,8 +237,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[0].name.to_string(),
|
||||||
Context::Idle(core) => app.idles[&core].name.to_string(),
|
Context::Idle => app.idles[0].name.to_string(),
|
||||||
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
|
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -257,9 +252,9 @@ pub fn resources_ident(ctxt: Context, app: &App) -> Ident {
|
||||||
/// Each core may have several task dispatchers, one for each priority level. Each task dispatcher
|
/// Each core may have several task dispatchers, one for each priority level. Each task dispatcher
|
||||||
/// in turn may use more than one ready queue because the queues are SPSC queues so one is needed
|
/// in turn may use more than one ready queue because the queues are SPSC queues so one is needed
|
||||||
/// per sender core.
|
/// per sender core.
|
||||||
pub fn rq_ident(receiver: Core, priority: u8, sender: Core) -> Ident {
|
pub fn rq_ident(priority: u8) -> Ident {
|
||||||
Ident::new(
|
Ident::new(
|
||||||
&format!("R{}_P{}_S{}_RQ", receiver, priority, sender),
|
&format!("P{}_RQ", priority),
|
||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -268,30 +263,32 @@ pub fn rq_ident(receiver: Core, priority: u8, sender: Core) -> Ident {
|
||||||
///
|
///
|
||||||
/// 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. As one task may be `schedule`-ed
|
||||||
/// by different cores we need one "schedule" function per possible task-sender pair
|
/// by different cores we need one "schedule" function per possible task-sender pair
|
||||||
pub fn schedule_ident(name: &Ident, sender: Core) -> Ident {
|
pub fn schedule_ident(name: &Ident) -> Ident {
|
||||||
Ident::new(
|
Ident::new(
|
||||||
&format!("schedule_{}_S{}", name.to_string(), sender),
|
&format!("schedule_{}", name.to_string()),
|
||||||
Span::call_site(),
|
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
|
/// Generates an identifier for a cross-spawn barrier
|
||||||
pub fn spawn_barrier(receiver: Core) -> Ident {
|
pub fn spawn_barrier() -> Ident {
|
||||||
Ident::new(&format!("SB{}", receiver), Span::call_site())
|
Ident::new(&format!("SB"), 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. As one task may be `spawn`-ed by
|
||||||
/// different cores we need one "spawn" function per possible task-sender pair
|
/// different cores we need one "spawn" function per possible task-sender pair
|
||||||
pub fn spawn_ident(name: &Ident, sender: Core) -> Ident {
|
pub fn spawn_ident(name: &Ident) -> Ident {
|
||||||
Ident::new(
|
Ident::new(
|
||||||
&format!("spawn_{}_S{}", name.to_string(), sender),
|
&format!("spawn_{}", name.to_string()),
|
||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -300,26 +297,21 @@ pub fn spawn_ident(name: &Ident, sender: Core) -> Ident {
|
||||||
///
|
///
|
||||||
/// 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!("R{}_P{}_S{}_T", receiver, priority, sender),
|
&format!("P{}_T", priority),
|
||||||
Span::call_site(),
|
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's one timer queue per core
|
||||||
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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
|
@ -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");
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
@ -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>(
|
||||||
|
|
|
@ -161,9 +161,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
|
||||||
|
|
Loading…
Reference in a new issue