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
19//!    handlers. Interrupt handlers are used to handle specific hardware
20//!    interrupts generated by peripherals.
21//!
22//!  - [`ram`](macro@ram) - Attribute macro for placing statics and functions
23//!    into specific memory sections, such as SRAM or RTC RAM (slow or fast)
24//!    with different initialization options. See its documentation for details.
25//!
26//!  - [`embassy::main`](macro@embassy_main) - Creates a new `executor` instance
27//!    and declares an application entry point spawning the corresponding
28//!    function body as an async task.
29//!
30//! ## Examples
31//!
32//! #### `main` macro
33//!
34//! Requires the `embassy` feature to be enabled.
35//!
36//! ```rust, no_run
37//! #[main]
38//! async fn main(spawner: Spawner) {
39//!     // Your application's entry point
40//! }
41//! ```
42//!
43//! ## Feature Flags
44#![doc = document_features::document_features!()]
45#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
46
47use proc_macro::TokenStream;
48
49mod blocking;
50mod builder;
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
73///   section below](#persistent) for details.
74/// - `zeroed`: Initialize the memory of the `static` to zero. The initializer
75///   expression will be discarded. Types used must implement
76///   [`bytemuck::Zeroable`].
77///
78/// Using both `rtc_fast` and `rtc_slow` or `persistent` and `zeroed` together
79/// is an error.
80///
81/// ## `persistent`
82///
83/// Initialize the memory to zero after the initial boot. Thereafter,
84/// initialization is skipped to allow communication across `software_reset()`,
85/// deep sleep, watchdog timeouts, etc.
86///
87/// Types used must implement [`bytemuck::AnyBitPattern`].
88///
89/// ### Warnings
90///
91/// - A system-level or lesser reset occurring before the ram has been zeroed
92///   *could* skip initialization and start the application with the static
93///   filled with random bytes.
94/// - There is no way to keep some kinds of resets from happening while updating
95///   a persistent static—not even a critical section.
96///
97/// If these are issues for your application, consider adding a checksum
98/// alongside the data.
99///
100/// # Examples
101///
102/// ```rust, no_run
103/// #[ram(rtc_fast)]
104/// static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb];
105///
106/// #[ram(rtc_fast, persistent)]
107/// static mut SOME_PERSISTENT_DATA: [u8; 2] = [0; 2];
108///
109/// #[ram(rtc_fast, zeroed)]
110/// static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8];
111/// ```
112///
113/// See the `ram` example in the esp-hal repository for a full usage example.
114///
115/// [`bytemuck::AnyBitPattern`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.AnyBitPattern.html
116/// [`bytemuck::Zeroable`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.Zeroable.html
117#[proc_macro_attribute]
118#[proc_macro_error2::proc_macro_error]
119pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
120    ram::ram(args, input)
121}
122
123/// Mark a function as an interrupt handler.
124///
125/// Optionally a priority can be specified, e.g. `#[handler(priority =
126/// esp_hal::interrupt::Priority::Priority2)]`.
127///
128/// If no priority is given, `Priority::min()` is assumed
129#[proc_macro_attribute]
130#[proc_macro_error2::proc_macro_error]
131pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
132    interrupt::handler(args, input)
133}
134
135/// Load code to be run on the LP/ULP core.
136///
137/// ## Example
138/// ```rust, no_run
139/// let lp_core_code = load_lp_code!("path.elf");
140/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
141/// ````
142#[cfg(any(feature = "has-lp-core", feature = "has-ulp-core"))]
143#[proc_macro]
144pub fn load_lp_code(input: TokenStream) -> TokenStream {
145    lp_core::load_lp_code(input)
146}
147
148/// Marks the entry function of a LP core / ULP program.
149#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
150#[proc_macro_attribute]
151#[proc_macro_error2::proc_macro_error]
152pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
153    lp_core::entry(args, input)
154}
155
156/// Creates a new `executor` instance and declares an application entry point
157/// spawning the corresponding function body as an async task.
158///
159/// The following restrictions apply:
160///
161/// * The function must accept exactly 1 parameter, an
162///   `embassy_executor::Spawner` handle that it can use to spawn additional
163///   tasks.
164/// * The function must be declared `async`.
165/// * The function must not use generics.
166/// * Only a single `main` task may be declared.
167///
168/// ## Examples
169/// Spawning a task:
170///
171/// ``` rust
172/// #[main]
173/// async fn main(_s: embassy_executor::Spawner) {
174///     // Function body
175/// }
176/// ```
177#[cfg(feature = "embassy")]
178#[proc_macro_attribute]
179pub fn embassy_main(args: TokenStream, item: TokenStream) -> TokenStream {
180    embassy::main(args, item)
181}
182
183/// Attribute to declare the entry point of the program
184///
185/// The specified function will be called by the reset handler *after* RAM has
186/// been initialized. If present, the FPU will also be enabled before the
187/// function is called.
188///
189/// The type of the specified function must be `[unsafe] fn() -> !` (never
190/// ending function)
191///
192/// # Properties
193///
194/// The entry point will be called by the reset handler. The program can't
195/// reference to the entry point, much less invoke it.
196///
197/// # Examples
198///
199/// - Simple entry point
200///
201/// ``` no_run
202/// #[main]
203/// fn main() -> ! {
204///     loop { /* .. */ }
205/// }
206/// ```
207#[proc_macro_attribute]
208pub fn blocking_main(args: TokenStream, input: TokenStream) -> TokenStream {
209    blocking::main(args, input)
210}
211
212/// Automatically implement the [Builder Lite] pattern for a struct.
213///
214/// This will create an `impl` which contains methods for each field of a
215/// struct, allowing users to easily set the values. The generated methods will
216/// be the field name prefixed with `with_`, and calls to these methods can be
217/// chained as needed.
218///
219/// ## Example
220///
221/// ```rust, no_run
222/// #[derive(Default)]
223/// enum MyEnum {
224///     #[default]
225///     A,
226///     B,
227/// }
228///
229/// #[derive(Default, BuilderLite)]
230/// #[non_exhaustive]
231/// struct MyStruct {
232///     enum_field: MyEnum,
233///     bool_field: bool,
234///     option_field: Option<i32>,
235/// }
236///
237/// MyStruct::default()
238///     .with_enum_field(MyEnum::B)
239///     .with_bool_field(true)
240///     .with_option_field(-5);
241/// ```
242///
243/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
244#[proc_macro_derive(BuilderLite, attributes(builder_lite))]
245pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
246    builder::builder_lite_derive(item)
247}