esp_hal_procmacros/lib.rs
1//! ## Overview
2//!
3//! Procedural macros for use with the `esp-hal` family of HAL packages. In
4//! general, you should not need to depend on this package directly, as the
5//! relevant procmacros are re-exported by the various HAL packages.
6//!
7//! Provides macros for:
8//!
9//! - Placing statics and functions into RAM
10//! - Marking interrupt handlers
11//! - Blocking and Async `#[main]` macros
12//!
13//! These macros offer developers a convenient way to control memory placement
14//! and define interrupt handlers in their embedded applications, allowing for
15//! optimized memory usage and precise handling of hardware interrupts.
16//!
17//! Key Components:
18//! - [`handler`](macro@handler) - Attribute macro for marking interrupt handlers. Interrupt
19//! handlers are used to handle specific hardware interrupts generated by peripherals.
20//!
21//! - [`ram`](macro@ram) - Attribute macro for placing statics and functions into specific memory
22//! sections, such as SRAM or RTC RAM (slow or fast) with different initialization options. See
23//! its documentation for details.
24//!
25//! - [`embassy::main`](macro@embassy_main) - Creates a new `executor` instance and declares an
26//! application entry point spawning the corresponding function body as an async task.
27//!
28//! ## Examples
29//!
30//! #### `main` macro
31//!
32//! Requires the `embassy` feature to be enabled.
33//!
34//! ```rust, no_run
35//! #[main]
36//! async fn main(spawner: Spawner) {
37//! // Your application's entry point
38//! }
39//! ```
40//!
41//! ## Feature Flags
42#![doc = document_features::document_features!()]
43#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
44
45use proc_macro::TokenStream;
46
47mod alert;
48mod blocking;
49mod builder;
50mod doc_replace;
51#[cfg(feature = "embassy")]
52mod embassy;
53mod interrupt;
54#[cfg(any(
55 feature = "is-lp-core",
56 feature = "is-ulp-core",
57 feature = "has-lp-core",
58 feature = "has-ulp-core"
59))]
60mod lp_core;
61mod ram;
62
63/// Sets which segment of RAM to use for a function or static and how it should
64/// be initialized.
65///
66/// Requires the `ram` feature.
67///
68/// # Options
69///
70/// - `rtc_fast`: Use RTC fast RAM.
71/// - `rtc_slow`: Use RTC slow RAM. **Note**: not available on all targets.
72/// - `persistent`: Persist the contents of the `static` across resets. See [the section
73/// below](#persistent) for details.
74/// - `zeroed`: Initialize the memory of the `static` to zero. The initializer expression will be
75/// discarded. Types used must implement [`bytemuck::Zeroable`].
76///
77/// Using both `rtc_fast` and `rtc_slow` or `persistent` and `zeroed` together
78/// is an error.
79///
80/// ## `persistent`
81///
82/// Initialize the memory to zero after the initial boot. Thereafter,
83/// initialization is skipped to allow communication across `software_reset()`,
84/// deep sleep, watchdog timeouts, etc.
85///
86/// Types used must implement [`bytemuck::AnyBitPattern`].
87///
88/// ### Warnings
89///
90/// - A system-level or lesser reset occurring before the ram has been zeroed *could* skip
91/// initialization and start the application with the static filled with random bytes.
92/// - There is no way to keep some kinds of resets from happening while updating a persistent
93/// static—not even a critical section.
94///
95/// If these are issues for your application, consider adding a checksum
96/// alongside the data.
97///
98/// # Examples
99///
100/// ```rust, no_run
101/// #[ram(rtc_fast)]
102/// static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb];
103///
104/// #[ram(rtc_fast, persistent)]
105/// static mut SOME_PERSISTENT_DATA: [u8; 2] = [0; 2];
106///
107/// #[ram(rtc_fast, zeroed)]
108/// static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8];
109/// ```
110///
111/// See the `ram` example in the esp-hal repository for a full usage example.
112///
113/// [`bytemuck::AnyBitPattern`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.AnyBitPattern.html
114/// [`bytemuck::Zeroable`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.Zeroable.html
115#[proc_macro_attribute]
116pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
117 ram::ram(args, input)
118}
119
120/// Replaces placeholders in rustdoc doc comments.
121///
122/// The purpose of this macro is to enable us to extract boilerplate, while at
123/// the same time let rustfmt format code blocks. This macro rewrites the whole
124/// documentation of the annotated item.
125///
126/// Replacements can be placed in the documentation as `# {placeholder}`. Each
127/// replacement must be its own line, it's not possible to place a placeholder in the middle of a
128/// line. The `before_snippet` and `after_snippet` placeholders are expanded to the
129/// `esp_hal::before_snippet!()` and `esp_hal::after_snippet!()` macros, and are expected to be
130/// used in example code blocks.
131///
132/// You can also define custom replacements in the attribute. A replacement can be
133/// an unconditional literal (i.e. a string that is always substituted into the doc comment),
134/// or a conditional.
135///
136/// ## Examples
137///
138/// ```rust, no_run
139/// #[doc_replace(
140/// "literal_placeholder" => "literal value",
141/// "conditional_placeholder" => {
142/// cfg(condition1) => "value 1",
143/// cfg(condition2) => "value 2",
144/// _ => "neither value 1 nor value 2",
145/// }
146/// )]
147/// /// Here comes the documentation.
148/// ///
149/// /// The replacements are interpreted outside of code blocks, too:
150/// /// # {literal_placeholder}
151/// ///
152/// /// ```rust, no run
153/// /// // here is some code
154/// /// # {literal_placeholder}
155/// /// // here is some more code
156/// /// # {conditional_placeholder}
157/// /// ```
158/// fn my_function() {}
159/// ```
160#[proc_macro_attribute]
161pub fn doc_replace(args: TokenStream, input: TokenStream) -> TokenStream {
162 doc_replace::replace(args, input)
163}
164
165/// Mark a function as an interrupt handler.
166///
167/// Optionally a priority can be specified, e.g. `#[handler(priority =
168/// esp_hal::interrupt::Priority::Priority2)]`.
169///
170/// If no priority is given, `Priority::min()` is assumed
171#[proc_macro_attribute]
172pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
173 interrupt::handler(args, input)
174}
175
176/// Load code to be run on the LP/ULP core.
177///
178/// ## Example
179/// ```rust, no_run
180/// let lp_core_code = load_lp_code!("path.elf");
181/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
182/// ````
183#[cfg(any(feature = "has-lp-core", feature = "has-ulp-core"))]
184#[proc_macro]
185pub fn load_lp_code(input: TokenStream) -> TokenStream {
186 lp_core::load_lp_code(input)
187}
188
189/// Marks the entry function of a LP core / ULP program.
190#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
191#[proc_macro_attribute]
192pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
193 lp_core::entry(args, input)
194}
195
196/// Creates a new `executor` instance and declares an application entry point
197/// spawning the corresponding function body as an async task.
198///
199/// The following restrictions apply:
200///
201/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it
202/// can use to spawn additional tasks.
203/// * The function must be declared `async`.
204/// * The function must not use generics.
205/// * Only a single `main` task may be declared.
206///
207/// ## Examples
208/// Spawning a task:
209///
210/// ``` rust
211/// #[main]
212/// async fn main(_s: embassy_executor::Spawner) {
213/// // Function body
214/// }
215/// ```
216#[cfg(feature = "embassy")]
217#[proc_macro_attribute]
218pub fn embassy_main(args: TokenStream, item: TokenStream) -> TokenStream {
219 embassy::main(args, item)
220}
221
222/// Attribute to declare the entry point of the program
223///
224/// The specified function will be called by the reset handler *after* RAM has
225/// been initialized. If present, the FPU will also be enabled before the
226/// function is called.
227///
228/// The type of the specified function must be `[unsafe] fn() -> !` (never
229/// ending function)
230///
231/// # Properties
232///
233/// The entry point will be called by the reset handler. The program can't
234/// reference to the entry point, much less invoke it.
235///
236/// # Examples
237///
238/// - Simple entry point
239///
240/// ``` no_run
241/// #[main]
242/// fn main() -> ! {
243/// loop { /* .. */ }
244/// }
245/// ```
246#[proc_macro_attribute]
247pub fn blocking_main(args: TokenStream, input: TokenStream) -> TokenStream {
248 blocking::main(args, input)
249}
250
251/// Automatically implement the [Builder Lite] pattern for a struct.
252///
253/// This will create an `impl` which contains methods for each field of a
254/// struct, allowing users to easily set the values. The generated methods will
255/// be the field name prefixed with `with_`, and calls to these methods can be
256/// chained as needed.
257///
258/// ## Example
259///
260/// ```rust, no_run
261/// #[derive(Default)]
262/// enum MyEnum {
263/// #[default]
264/// A,
265/// B,
266/// }
267///
268/// #[derive(Default, BuilderLite)]
269/// #[non_exhaustive]
270/// struct MyStruct {
271/// enum_field: MyEnum,
272/// bool_field: bool,
273/// option_field: Option<i32>,
274/// }
275///
276/// MyStruct::default()
277/// .with_enum_field(MyEnum::B)
278/// .with_bool_field(true)
279/// .with_option_field(-5);
280/// ```
281///
282/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
283#[proc_macro_derive(BuilderLite, attributes(builder_lite))]
284pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
285 builder::builder_lite_derive(item)
286}
287
288/// Print a build error and terminate the process.
289///
290/// It should be noted that the error will be printed BEFORE the main function
291/// is called, and as such this should NOT be thought analogous to `println!` or
292/// similar utilities.
293///
294/// ## Example
295///
296/// ```rust
297/// esp_hal_procmacros::error! {"
298/// ERROR: something really bad has happened!
299/// "}
300/// // Process exits with exit code 1
301/// ```
302#[proc_macro]
303pub fn error(input: TokenStream) -> TokenStream {
304 alert::do_alert(termcolor::Color::Red, input);
305 panic!("Build failed");
306}
307
308/// Print a build warning.
309///
310/// It should be noted that the warning will be printed BEFORE the main function
311/// is called, and as such this should NOT be thought analogous to `println!` or
312/// similar utilities.
313///
314/// ## Example
315///
316/// ```rust
317/// esp_hal_procmacros::warning! {"
318/// WARNING: something unpleasant has happened!
319/// "};
320/// ```
321#[proc_macro]
322pub fn warning(input: TokenStream) -> TokenStream {
323 alert::do_alert(termcolor::Color::Yellow, input)
324}