mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 12:50:53 +00:00

* Add insert_doc_snippet proc macro * Replace placeholders * Allow defining (conditional) replacements * Use unstable features for module-level docs * Document the new macro * Rename macro * Lint * Remove nonsense code block check, change syntax * Remove redundant doc_switch macro
325 lines
10 KiB
Rust
325 lines
10 KiB
Rust
//! ## Overview
|
|
//!
|
|
//! Procedural macros for use with the `esp-hal` family of HAL packages. In
|
|
//! general, you should not need to depend on this package directly, as the
|
|
//! relevant procmacros are re-exported by the various HAL packages.
|
|
//!
|
|
//! Provides macros for:
|
|
//!
|
|
//! - Placing statics and functions into RAM
|
|
//! - Marking interrupt handlers
|
|
//! - Blocking and Async `#[main]` macros
|
|
//!
|
|
//! These macros offer developers a convenient way to control memory placement
|
|
//! and define interrupt handlers in their embedded applications, allowing for
|
|
//! optimized memory usage and precise handling of hardware interrupts.
|
|
//!
|
|
//! Key Components:
|
|
//! - [`handler`](macro@handler) - Attribute macro for marking interrupt handlers. Interrupt
|
|
//! handlers are used to handle specific hardware interrupts generated by peripherals.
|
|
//!
|
|
//! - [`ram`](macro@ram) - Attribute macro for placing statics and functions into specific memory
|
|
//! sections, such as SRAM or RTC RAM (slow or fast) with different initialization options. See
|
|
//! its documentation for details.
|
|
//!
|
|
//! - [`embassy::main`](macro@embassy_main) - Creates a new `executor` instance and declares an
|
|
//! application entry point spawning the corresponding function body as an async task.
|
|
//!
|
|
//! ## Examples
|
|
//!
|
|
//! #### `main` macro
|
|
//!
|
|
//! Requires the `embassy` feature to be enabled.
|
|
//!
|
|
//! ```rust, no_run
|
|
//! #[main]
|
|
//! async fn main(spawner: Spawner) {
|
|
//! // Your application's entry point
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! ## Feature Flags
|
|
#![doc = document_features::document_features!()]
|
|
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
|
|
|
use proc_macro::TokenStream;
|
|
|
|
mod alert;
|
|
mod blocking;
|
|
mod builder;
|
|
mod doc_replace;
|
|
#[cfg(feature = "embassy")]
|
|
mod embassy;
|
|
mod interrupt;
|
|
#[cfg(any(
|
|
feature = "is-lp-core",
|
|
feature = "is-ulp-core",
|
|
feature = "has-lp-core",
|
|
feature = "has-ulp-core"
|
|
))]
|
|
mod lp_core;
|
|
mod ram;
|
|
|
|
/// Sets which segment of RAM to use for a function or static and how it should
|
|
/// be initialized.
|
|
///
|
|
/// Requires the `ram` feature.
|
|
///
|
|
/// # Options
|
|
///
|
|
/// - `rtc_fast`: Use RTC fast RAM.
|
|
/// - `rtc_slow`: Use RTC slow RAM. **Note**: not available on all targets.
|
|
/// - `persistent`: Persist the contents of the `static` across resets. See [the section
|
|
/// below](#persistent) for details.
|
|
/// - `zeroed`: Initialize the memory of the `static` to zero. The initializer expression will be
|
|
/// discarded. Types used must implement [`bytemuck::Zeroable`].
|
|
///
|
|
/// Using both `rtc_fast` and `rtc_slow` or `persistent` and `zeroed` together
|
|
/// is an error.
|
|
///
|
|
/// ## `persistent`
|
|
///
|
|
/// Initialize the memory to zero after the initial boot. Thereafter,
|
|
/// initialization is skipped to allow communication across `software_reset()`,
|
|
/// deep sleep, watchdog timeouts, etc.
|
|
///
|
|
/// Types used must implement [`bytemuck::AnyBitPattern`].
|
|
///
|
|
/// ### Warnings
|
|
///
|
|
/// - A system-level or lesser reset occurring before the ram has been zeroed *could* skip
|
|
/// initialization and start the application with the static filled with random bytes.
|
|
/// - There is no way to keep some kinds of resets from happening while updating a persistent
|
|
/// static—not even a critical section.
|
|
///
|
|
/// If these are issues for your application, consider adding a checksum
|
|
/// alongside the data.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust, no_run
|
|
/// #[ram(rtc_fast)]
|
|
/// static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb];
|
|
///
|
|
/// #[ram(rtc_fast, persistent)]
|
|
/// static mut SOME_PERSISTENT_DATA: [u8; 2] = [0; 2];
|
|
///
|
|
/// #[ram(rtc_fast, zeroed)]
|
|
/// static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8];
|
|
/// ```
|
|
///
|
|
/// See the `ram` example in the esp-hal repository for a full usage example.
|
|
///
|
|
/// [`bytemuck::AnyBitPattern`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.AnyBitPattern.html
|
|
/// [`bytemuck::Zeroable`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.Zeroable.html
|
|
#[proc_macro_attribute]
|
|
pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
ram::ram(args, input)
|
|
}
|
|
|
|
/// Replaces placeholders in rustdoc doc comments.
|
|
///
|
|
/// The purpose of this macro is to enable us to extract boilerplate, while at
|
|
/// the same time let rustfmt format code blocks. This macro rewrites the whole
|
|
/// documentation of the annotated item.
|
|
///
|
|
/// Replacements can be placed in the documentation as `# {placeholder}`. Each
|
|
/// replacement must be its own line, it's not possible to place a placeholder in the middle of a
|
|
/// line. The `before_snippet` and `after_snippet` placeholders are expanded to the
|
|
/// `esp_hal::before_snippet!()` and `esp_hal::after_snippet!()` macros, and are expected to be
|
|
/// used in example code blocks.
|
|
///
|
|
/// You can also define custom replacements in the attribute. A replacement can be
|
|
/// an unconditional literal (i.e. a string that is always substituted into the doc comment),
|
|
/// or a conditional.
|
|
///
|
|
/// ## Examples
|
|
///
|
|
/// ```rust, no_run
|
|
/// #[doc_replace(
|
|
/// "literal_placeholder" => "literal value",
|
|
/// "conditional_placeholder" => {
|
|
/// cfg(condition1) => "value 1",
|
|
/// cfg(condition2) => "value 2",
|
|
/// _ => "neither value 1 nor value 2",
|
|
/// }
|
|
/// )]
|
|
/// /// Here comes the documentation.
|
|
/// ///
|
|
/// /// The replacements are interpreted outside of code blocks, too:
|
|
/// /// # {literal_placeholder}
|
|
/// ///
|
|
/// /// ```rust, no run
|
|
/// /// // here is some code
|
|
/// /// # {literal_placeholder}
|
|
/// /// // here is some more code
|
|
/// /// # {conditional_placeholder}
|
|
/// /// ```
|
|
/// fn my_function() {}
|
|
/// ```
|
|
#[proc_macro_attribute]
|
|
pub fn doc_replace(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
doc_replace::replace(args, input)
|
|
}
|
|
|
|
/// Mark a function as an interrupt handler.
|
|
///
|
|
/// Optionally a priority can be specified, e.g. `#[handler(priority =
|
|
/// esp_hal::interrupt::Priority::Priority2)]`.
|
|
///
|
|
/// If no priority is given, `Priority::min()` is assumed
|
|
#[proc_macro_attribute]
|
|
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
interrupt::handler(args, input)
|
|
}
|
|
|
|
/// Load code to be run on the LP/ULP core.
|
|
///
|
|
/// ## Example
|
|
/// ```rust, no_run
|
|
/// let lp_core_code = load_lp_code!("path.elf");
|
|
/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
|
|
/// ````
|
|
#[cfg(any(feature = "has-lp-core", feature = "has-ulp-core"))]
|
|
#[proc_macro]
|
|
pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
|
lp_core::load_lp_code(input)
|
|
}
|
|
|
|
/// Marks the entry function of a LP core / ULP program.
|
|
#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
|
|
#[proc_macro_attribute]
|
|
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
lp_core::entry(args, input)
|
|
}
|
|
|
|
/// Creates a new `executor` instance and declares an application entry point
|
|
/// spawning the corresponding function body as an async task.
|
|
///
|
|
/// The following restrictions apply:
|
|
///
|
|
/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it
|
|
/// can use to spawn additional tasks.
|
|
/// * The function must be declared `async`.
|
|
/// * The function must not use generics.
|
|
/// * Only a single `main` task may be declared.
|
|
///
|
|
/// ## Examples
|
|
/// Spawning a task:
|
|
///
|
|
/// ``` rust
|
|
/// #[main]
|
|
/// async fn main(_s: embassy_executor::Spawner) {
|
|
/// // Function body
|
|
/// }
|
|
/// ```
|
|
#[cfg(feature = "embassy")]
|
|
#[proc_macro_attribute]
|
|
pub fn embassy_main(args: TokenStream, item: TokenStream) -> TokenStream {
|
|
embassy::main(args, item)
|
|
}
|
|
|
|
/// Attribute to declare the entry point of the program
|
|
///
|
|
/// The specified function will be called by the reset handler *after* RAM has
|
|
/// been initialized. If present, the FPU will also be enabled before the
|
|
/// function is called.
|
|
///
|
|
/// The type of the specified function must be `[unsafe] fn() -> !` (never
|
|
/// ending function)
|
|
///
|
|
/// # Properties
|
|
///
|
|
/// The entry point will be called by the reset handler. The program can't
|
|
/// reference to the entry point, much less invoke it.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// - Simple entry point
|
|
///
|
|
/// ``` no_run
|
|
/// #[main]
|
|
/// fn main() -> ! {
|
|
/// loop { /* .. */ }
|
|
/// }
|
|
/// ```
|
|
#[proc_macro_attribute]
|
|
pub fn blocking_main(args: TokenStream, input: TokenStream) -> TokenStream {
|
|
blocking::main(args, input)
|
|
}
|
|
|
|
/// Automatically implement the [Builder Lite] pattern for a struct.
|
|
///
|
|
/// This will create an `impl` which contains methods for each field of a
|
|
/// struct, allowing users to easily set the values. The generated methods will
|
|
/// be the field name prefixed with `with_`, and calls to these methods can be
|
|
/// chained as needed.
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// ```rust, no_run
|
|
/// #[derive(Default)]
|
|
/// enum MyEnum {
|
|
/// #[default]
|
|
/// A,
|
|
/// B,
|
|
/// }
|
|
///
|
|
/// #[derive(Default, BuilderLite)]
|
|
/// #[non_exhaustive]
|
|
/// struct MyStruct {
|
|
/// enum_field: MyEnum,
|
|
/// bool_field: bool,
|
|
/// option_field: Option<i32>,
|
|
/// }
|
|
///
|
|
/// MyStruct::default()
|
|
/// .with_enum_field(MyEnum::B)
|
|
/// .with_bool_field(true)
|
|
/// .with_option_field(-5);
|
|
/// ```
|
|
///
|
|
/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
|
|
#[proc_macro_derive(BuilderLite, attributes(builder_lite))]
|
|
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
|
|
builder::builder_lite_derive(item)
|
|
}
|
|
|
|
/// Print a build error and terminate the process.
|
|
///
|
|
/// It should be noted that the error will be printed BEFORE the main function
|
|
/// is called, and as such this should NOT be thought analogous to `println!` or
|
|
/// similar utilities.
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// ```rust
|
|
/// esp_hal_procmacros::error! {"
|
|
/// ERROR: something really bad has happened!
|
|
/// "}
|
|
/// // Process exits with exit code 1
|
|
/// ```
|
|
#[proc_macro]
|
|
pub fn error(input: TokenStream) -> TokenStream {
|
|
alert::do_alert(termcolor::Color::Red, input);
|
|
panic!("Build failed");
|
|
}
|
|
|
|
/// Print a build warning.
|
|
///
|
|
/// It should be noted that the warning will be printed BEFORE the main function
|
|
/// is called, and as such this should NOT be thought analogous to `println!` or
|
|
/// similar utilities.
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// ```rust
|
|
/// esp_hal_procmacros::warning! {"
|
|
/// WARNING: something unpleasant has happened!
|
|
/// "};
|
|
/// ```
|
|
#[proc_macro]
|
|
pub fn warning(input: TokenStream) -> TokenStream {
|
|
alert::do_alert(termcolor::Color::Yellow, input)
|
|
}
|