cortex_m_rtic_macros/codegen/
shared_resources.rs

1use crate::{analyze::Analysis, check::Extra, codegen::util};
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use rtic_syntax::{analyze::Ownership, ast::App};
5use std::collections::HashMap;
6
7/// Generates `static` variables and shared resource proxies
8pub fn codegen(
9    app: &App,
10    analysis: &Analysis,
11    extra: &Extra,
12) -> (
13    // mod_app -- the `static` variables behind the proxies
14    Vec<TokenStream2>,
15    // mod_resources -- the `resources` module
16    TokenStream2,
17) {
18    let mut mod_app = vec![];
19    let mut mod_resources = vec![];
20
21    for (name, res) in &app.shared_resources {
22        let cfgs = &res.cfgs;
23        let ty = &res.ty;
24        let mangled_name = &util::static_shared_resource_ident(name);
25
26        let attrs = &res.attrs;
27
28        // late resources in `util::link_section_uninit`
29        // unless user specifies custom link section
30        let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) {
31            None
32        } else {
33            Some(util::link_section_uninit())
34        };
35
36        // For future use
37        // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
38        mod_app.push(quote!(
39            #[allow(non_camel_case_types)]
40            #[allow(non_upper_case_globals)]
41            // #[doc = #doc]
42            #[doc(hidden)]
43            #(#attrs)*
44            #(#cfgs)*
45            #section
46            static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit());
47        ));
48
49        // For future use
50        // let doc = format!(" RTIC internal: {}:{}", file!(), line!());
51
52        let shared_name = util::need_to_lock_ident(name);
53
54        if !res.properties.lock_free {
55            mod_resources.push(quote!(
56                // #[doc = #doc]
57                #[doc(hidden)]
58                #[allow(non_camel_case_types)]
59                #(#cfgs)*
60                pub struct #shared_name<'a> {
61                    priority: &'a Priority,
62                }
63
64                #(#cfgs)*
65                impl<'a> #shared_name<'a> {
66                    #[inline(always)]
67                    pub unsafe fn new(priority: &'a Priority) -> Self {
68                        #shared_name { priority }
69                    }
70
71                    #[inline(always)]
72                    pub unsafe fn priority(&self) -> &Priority {
73                        self.priority
74                    }
75                }
76            ));
77
78            let ptr = quote!(
79                #(#cfgs)*
80                #mangled_name.get_mut() as *mut _
81            );
82
83            let ceiling = match analysis.ownerships.get(name) {
84                Some(Ownership::Owned { priority } | Ownership::CoOwned { priority }) => *priority,
85                Some(Ownership::Contended { ceiling }) => *ceiling,
86                None => 0,
87            };
88
89            // For future use
90            // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!());
91
92            mod_app.push(util::impl_mutex(
93                extra,
94                cfgs,
95                true,
96                &shared_name,
97                &quote!(#ty),
98                ceiling,
99                &ptr,
100            ));
101        }
102    }
103
104    let mod_resources = if mod_resources.is_empty() {
105        quote!()
106    } else {
107        quote!(mod shared_resources {
108            use rtic::export::Priority;
109
110            #(#mod_resources)*
111        })
112    };
113
114    // Computing mapping of used interrupts to masks
115    let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
116
117    let mut prio_to_masks = HashMap::new();
118    let device = &extra.device;
119    let mut uses_exceptions_with_resources = false;
120
121    let mut mask_ids = Vec::new();
122
123    for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| {
124        if !util::is_exception(&task.args.binds) {
125            Some((&task.args.priority, &task.args.binds))
126        } else {
127            // If any resource to the exception uses non-lock-free or non-local resources this is
128            // not allwed on thumbv6.
129            uses_exceptions_with_resources = uses_exceptions_with_resources
130                || task
131                    .args
132                    .shared_resources
133                    .iter()
134                    .map(|(ident, access)| {
135                        if access.is_exclusive() {
136                            if let Some(r) = app.shared_resources.get(ident) {
137                                !r.properties.lock_free
138                            } else {
139                                false
140                            }
141                        } else {
142                            false
143                        }
144                    })
145                    .any(|v| v);
146
147            None
148        }
149    })) {
150        #[allow(clippy::or_fun_call)]
151        let v = prio_to_masks.entry(priority - 1).or_insert(Vec::new());
152        v.push(quote!(#device::Interrupt::#name as u32));
153        mask_ids.push(quote!(#device::Interrupt::#name as u32));
154    }
155
156    // Call rtic::export::create_mask([Mask; N]), where the array is the list of shifts
157
158    let mut mask_arr = Vec::new();
159    // NOTE: 0..3 assumes max 4 priority levels according to M0, M23 spec
160    for i in 0..3 {
161        let v = if let Some(v) = prio_to_masks.get(&i) {
162            v.clone()
163        } else {
164            Vec::new()
165        };
166
167        mask_arr.push(quote!(
168            rtic::export::create_mask([#(#v),*])
169        ));
170    }
171
172    // Generate a constant for the number of chunks needed by Mask.
173    let chunks_name = util::priority_mask_chunks_ident();
174    mod_app.push(quote!(
175        #[doc(hidden)]
176        #[allow(non_upper_case_globals)]
177        const #chunks_name: usize = rtic::export::compute_mask_chunks([#(#mask_ids),*]);
178    ));
179
180    let masks_name = util::priority_masks_ident();
181    mod_app.push(quote!(
182        #[doc(hidden)]
183        #[allow(non_upper_case_globals)]
184        const #masks_name: [rtic::export::Mask<#chunks_name>; 3] = [#(#mask_arr),*];
185    ));
186
187    if uses_exceptions_with_resources {
188        mod_app.push(quote!(
189            #[doc(hidden)]
190            #[allow(non_upper_case_globals)]
191            const __rtic_internal_V6_ERROR: () = rtic::export::no_basepri_panic();
192        ));
193    }
194
195    (mod_app, mod_resources)
196}