rtic/macros/src/codegen/util.rs

284 lines
8 KiB
Rust
Raw Normal View History

2019-06-29 09:11:42 +02:00
use core::sync::atomic::{AtomicUsize, Ordering};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
2020-08-27 13:21:56 +02:00
use rtic_syntax::{ast::App, Context};
2019-08-20 15:11:24 +02:00
use syn::{Attribute, Ident, LitInt, PatType};
use crate::check::Extra;
const RTIC_INTERNAL: &str = "__rtic_internal";
/// Turns `capacity` into an unsuffixed integer literal
2021-04-03 19:30:34 +02:00
pub fn capacity_literal(capacity: usize) -> LitInt {
2019-08-20 15:11:24 +02:00
LitInt::new(&capacity.to_string(), Span::call_site())
}
/// Identifier for the free queue
2020-08-27 13:21:56 +02:00
pub fn fq_ident(task: &Ident) -> Ident {
2022-02-09 18:56:35 +01:00
mark_internal_name(&format!("{}_FQ", task))
}
/// Generates a `Mutex` implementation
pub fn impl_mutex(
extra: &Extra,
cfgs: &[Attribute],
resources_prefix: bool,
name: &Ident,
2022-02-18 19:38:48 +01:00
ty: &TokenStream2,
ceiling: u8,
2022-02-18 19:38:48 +01:00
ptr: &TokenStream2,
) -> TokenStream2 {
let (path, priority) = if resources_prefix {
2021-07-06 22:47:48 +02:00
(quote!(shared_resources::#name), quote!(self.priority()))
} else {
(quote!(#name), quote!(self.priority))
};
2020-10-23 10:35:56 +02:00
let device = &extra.device;
2022-04-20 10:46:03 +02:00
let masks_name = priority_masks_ident();
quote!(
#(#cfgs)*
2020-06-11 19:18:29 +02:00
impl<'a> rtic::Mutex for #path<'a> {
type T = #ty;
#[inline(always)]
fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
/// Priority ceiling
const CEILING: u8 = #ceiling;
unsafe {
2020-06-11 19:18:29 +02:00
rtic::export::lock(
#ptr,
#priority,
CEILING,
#device::NVIC_PRIO_BITS,
2022-04-20 10:46:03 +02:00
&#masks_name,
f,
)
}
}
}
)
}
/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API)
2020-08-27 13:21:56 +02:00
pub fn inputs_ident(task: &Ident) -> Ident {
mark_internal_name(&format!("{}_INPUTS", task))
}
/// Generates an identifier for the `INSTANTS` buffer (`schedule` API)
pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident {
mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic))
}
2020-08-27 13:21:56 +02:00
pub fn interrupt_ident() -> Ident {
2019-06-18 10:31:31 +02:00
let span = Span::call_site();
Ident::new("interrupt", span)
2019-06-18 10:31:31 +02:00
}
pub fn timer_queue_marker_ident() -> Ident {
2021-12-25 13:17:16 +01:00
mark_internal_name("TIMER_QUEUE_MARKER")
}
2019-06-20 06:19:59 +02:00
/// Whether `name` is an exception with configurable priority
pub fn is_exception(name: &Ident) -> bool {
let s = name.to_string();
2020-10-13 16:16:33 +02:00
matches!(
&*s,
"MemoryManagement"
| "BusFault"
| "UsageFault"
| "SecureFault"
| "SVCall"
| "DebugMonitor"
| "PendSV"
| "SysTick"
)
2019-06-20 06:19:59 +02:00
}
/// Mark a name as internal
pub fn mark_internal_name(name: &str) -> Ident {
Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site())
}
2021-05-06 19:40:37 +02:00
/// Generate an internal identifier for monotonics
pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident {
2022-02-09 18:56:35 +01:00
mark_internal_name(&format!("{}_{}_{}", task, monotonic, ident_name,))
2021-05-06 19:40:37 +02:00
}
/// Generate an internal identifier for tasks
pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
2022-02-09 18:56:35 +01:00
mark_internal_name(&format!("{}_{}", task, ident_name))
2021-05-06 19:40:37 +02:00
}
2019-06-29 09:11:42 +02:00
fn link_section_index() -> usize {
static INDEX: AtomicUsize = AtomicUsize::new(0);
INDEX.fetch_add(1, Ordering::Relaxed)
}
2022-02-18 19:38:48 +01:00
/// Add `link_section` attribute
pub fn link_section_uninit() -> TokenStream2 {
let section = format!(".uninit.rtic{}", link_section_index());
2019-06-29 09:11:42 +02:00
2022-02-18 19:38:48 +01:00
quote!(#[link_section = #section])
2019-06-29 09:11:42 +02:00
}
// Regroups the inputs of a task
//
// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`]
pub fn regroup_inputs(
2019-08-20 15:11:24 +02:00
inputs: &[PatType],
) -> (
// args e.g. &[`_0`], &[`_0: i32`, `_1: i64`]
Vec<TokenStream2>,
// tupled e.g. `_0`, `(_0, _1)`
TokenStream2,
// untupled e.g. &[`_0`], &[`_0`, `_1`]
Vec<TokenStream2>,
// ty e.g. `Foo`, `(i32, i64)`
TokenStream2,
) {
if inputs.len() == 1 {
let ty = &inputs[0].ty;
(
vec![quote!(_0: #ty)],
quote!(_0),
vec![quote!(_0)],
quote!(#ty),
)
} else {
let mut args = vec![];
let mut pats = vec![];
let mut tys = vec![];
for (i, input) in inputs.iter().enumerate() {
let i = Ident::new(&format!("_{}", i), Span::call_site());
let ty = &input.ty;
args.push(quote!(#i: #ty));
pats.push(quote!(#i));
tys.push(quote!(#ty));
}
let tupled = {
let pats = pats.clone();
quote!((#(#pats,)*))
};
let ty = quote!((#(#tys,)*));
(args, tupled, pats, ty)
}
}
2021-07-07 22:50:59 +02:00
/// Get the ident for the name of the task
pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
let s = match ctxt {
Context::Init => app.init.name.to_string(),
Context::Idle => app.idle.as_ref().unwrap().name.to_string(),
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
};
Ident::new(&s, Span::call_site())
}
2021-07-05 21:40:01 +02:00
/// Generates a pre-reexport identifier for the "shared resources" struct
pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident {
let mut s = match ctxt {
2021-07-05 21:40:01 +02:00
Context::Init => app.init.name.to_string(),
2021-07-06 22:47:48 +02:00
Context::Idle => app.idle.as_ref().unwrap().name.to_string(),
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
};
2021-07-05 21:40:01 +02:00
s.push_str("SharedResources");
mark_internal_name(&s)
2021-07-05 21:40:01 +02:00
}
/// Generates a pre-reexport identifier for the "local resources" struct
pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
let mut s = match ctxt {
Context::Init => app.init.name.to_string(),
2021-07-06 22:47:48 +02:00
Context::Idle => app.idle.as_ref().unwrap().name.to_string(),
2021-07-05 21:40:01 +02:00
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
};
s.push_str("LocalResources");
mark_internal_name(&s)
}
/// Generates an identifier for a ready queue
///
2020-09-01 19:04:55 +02:00
/// There may be several task dispatchers, one for each priority level.
/// The ready queues are SPSC queues
2020-08-27 13:21:56 +02:00
pub fn rq_ident(priority: u8) -> Ident {
mark_internal_name(&format!("P{}_RQ", priority))
}
/// Generates an identifier for the `enum` of `schedule`-able tasks
2020-08-27 13:21:56 +02:00
pub fn schedule_t_ident() -> Ident {
2021-12-25 13:17:16 +01:00
Ident::new("SCHED_T", Span::call_site())
}
/// Generates an identifier for the `enum` of `spawn`-able tasks
///
/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue
/// for each of these `T` enums
2020-08-27 13:21:56 +02:00
pub fn spawn_t_ident(priority: u8) -> Ident {
2020-09-01 16:39:05 +02:00
Ident::new(&format!("P{}_T", priority), Span::call_site())
}
2020-12-13 14:52:16 +01:00
/// Suffixed identifier
2020-08-27 13:21:56 +02:00
pub fn suffixed(name: &str) -> Ident {
2019-06-18 10:31:31 +02:00
let span = Span::call_site();
2020-08-27 13:21:56 +02:00
Ident::new(name, span)
2019-06-18 10:31:31 +02:00
}
/// Generates an identifier for a timer queue
2020-12-08 20:49:13 +01:00
pub fn tq_ident(name: &str) -> Ident {
mark_internal_name(&format!("TQ_{}", name))
}
2020-12-13 14:52:16 +01:00
2021-02-18 19:30:59 +01:00
/// Generates an identifier for monotonic timer storage
pub fn monotonic_ident(name: &str) -> Ident {
mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name))
2021-02-18 19:30:59 +01:00
}
2021-07-06 22:47:48 +02:00
pub fn static_shared_resource_ident(name: &Ident) -> Ident {
2022-02-09 18:56:35 +01:00
mark_internal_name(&format!("shared_resource_{}", name))
2021-07-06 22:47:48 +02:00
}
Remove use of basepri register on thumbv8m.base The basepri register appears to be aviable on thumbv8m.main but not thumbv8m.base. At the very least, attempting to compile against a Cortex-M23 based Microchip ATSAML10E16A generates an error: ``` error[E0432]: unresolved import `cortex_m::register::basepri` --> /Users/dwatson/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rtic-1.1.3/src/export.rs:25:5 | 25 | use cortex_m::register::basepri; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `basepri` in `register` ``` This is an attempt to account for the fact that thumbv8m.base (M23) MCUs don't have the BASEPRI register but have more than 32 interrupts. This moves away from the architecture specific config flags and switches to a more functional flag. Make the mask size depend on the max interrupt id Rather than assuming a fixed interrupt count of 32 this code uses an array of u32 bitmasks to calculate the priority mask. The size of this array is calculated at compile time based on the size of the largest interrupt id being used in the target code. For thumbv6m this should be equivalent to the previous version that used a single u32 mask. For thumbv8m.base it will be larger depending on the interrupts used. Don't write 0s to the ISER and ICER registers Writing 0s to these registers is a no-op. Since these masks should be calculated at compile time, this conditional should result in writes being optimized out of the code. Prevent panic on non-arm targets Panicking on unknown targets was breaking things like the doc build on linux. This change should only panic when building on unknown arm targets.
2022-07-03 18:24:11 +02:00
/// Generates an Ident for the number of 32 bit chunks used for Mask storage.
pub fn priority_mask_chunks_ident() -> Ident {
mark_internal_name("MASK_CHUNKS")
}
2022-04-20 10:46:03 +02:00
pub fn priority_masks_ident() -> Ident {
mark_internal_name("MASKS")
}
2021-07-06 22:47:48 +02:00
pub fn static_local_resource_ident(name: &Ident) -> Ident {
2022-02-09 18:56:35 +01:00
mark_internal_name(&format!("local_resource_{}", name))
2021-07-07 22:50:59 +02:00
}
pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident {
2022-02-09 18:56:35 +01:00
mark_internal_name(&format!("local_{}_{}", task_name, name))
2021-07-06 22:47:48 +02:00
}
pub fn need_to_lock_ident(name: &Ident) -> Ident {
2022-02-09 18:56:35 +01:00
Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span())
}
2020-12-13 14:52:16 +01:00
/// The name to get better RT flag errors
pub fn rt_err_ident() -> Ident {
Ident::new(
2021-12-25 13:17:16 +01:00
"you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml",
2020-12-13 14:52:16 +01:00
Span::call_site(),
)
}