Rework how peripheral singletons are generated (#3747)

* Handle GPIOs as stable peripherals

* Define interrupts in create_peripheral

* Remove the rest of paste from the peripherals macro

* Move create_peripheral calls out of peripherals!

* Merge macros

* Merge peripherals files

* Delete the peripheral module

* Replace the peripherals macro with for_each generated code

* Do not generate code directly in GPIO metadata
This commit is contained in:
Dániel Buga 2025-07-04 14:15:12 +02:00 committed by GitHub
parent 68b992f6d6
commit 86e006b601
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 322 additions and 405 deletions

View File

@ -104,6 +104,7 @@ This will use software-interrupt 3 which isn't available for anything else to wa
///
/// This function never returns.
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
#[cfg_attr(not(low_power_wait), expect(dead_code, reason = "cpu index is unused"))]
struct NoHooks(usize);
impl Callbacks for NoHooks {

View File

@ -205,6 +205,7 @@ metadata!("build_info", CHIP_NAME, chip!());
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[cfg_attr(not(feature = "unstable"), doc(hidden))]
pub use esp_riscv_rt::{self, riscv};
pub(crate) use peripherals::pac;
#[cfg(xtensa)]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[cfg_attr(not(feature = "unstable"), doc(hidden))]
@ -218,8 +219,6 @@ pub use self::soc::efuse;
#[instability::unstable]
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub use self::soc::lp_core;
pub use self::soc::peripherals;
pub(crate) use self::soc::peripherals::pac;
#[instability::unstable]
#[cfg(feature = "psram")]
pub use self::soc::psram;
@ -234,6 +233,7 @@ pub mod clock;
pub mod gpio;
#[cfg(any(soc_has_i2c0, soc_has_i2c1))]
pub mod i2c;
pub mod peripherals;
#[cfg(all(feature = "unstable", any(soc_has_hmac, soc_has_sha)))]
mod reg_access;
#[cfg(any(soc_has_spi0, soc_has_spi1, soc_has_spi2, soc_has_spi3))]
@ -244,7 +244,6 @@ pub mod time;
pub mod uart;
mod macros;
mod peripheral;
pub use procmacros::blocking_main as main;
#[cfg(any(lp_core, ulp_riscv_core))]

View File

@ -1,237 +0,0 @@
//! # Exclusive peripheral access
/// Creates a new `Peripherals` struct and its associated methods.
///
/// The macro has a few fields doing different things, in the form of
/// `second <= third (fourth)`.
/// - The second field is the name of the peripheral, as it appears in the `Peripherals` struct.
/// - The third field is the name of the peripheral as it appears in the PAC. This may be `virtual`
/// if the peripheral is not present in the PAC.
/// - The fourth field is an optional list of interrupts that can be bound to the peripheral.
#[doc(hidden)]
#[macro_export]
macro_rules! peripherals {
(
peripherals: [
$(
$name:ident <= $from_pac:tt ($($interrupt_name:ident => $interrupt:ident),*),
)*
],
unstable_peripherals: [
$(
$unstable_name:ident <= $unstable_from_pac:tt ($($unstable_interrupt_name:ident => $unstable_interrupt:ident),*),
)*
],
pins: [
$( $gpio:ident, )*
]
) => {
macro_rules! generate_interrupt_fns {
($fn_name:ident, $int_name:ident) => {
paste::paste!{
/// Binds an interrupt handler to the corresponding interrupt for this peripheral.
#[instability::unstable]
pub fn [<bind_ $fn_name _interrupt >](&self, handler: unsafe extern "C" fn() -> ()) {
unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$int_name, handler); }
}
/// Disables the interrupt handler
#[instability::unstable]
pub fn [<disable_ $fn_name _interrupt >](&self) {
for core in $crate::system::Cpu::other() {
$crate::interrupt::disable(core, $crate::peripherals::Interrupt::$int_name);
}
}
/// Enables the interrupt handler on the given core
#[instability::unstable]
pub fn [<enable_ $fn_name _interrupt >](&self, priority: $crate::interrupt::Priority) {
unwrap!($crate::interrupt::enable($crate::peripherals::Interrupt::$int_name, priority));
}
}
};
}
$(
$crate::create_peripheral!($name <= $from_pac);
)*
$(
$crate::create_peripheral!(#[instability::unstable] $unstable_name <= $unstable_from_pac);
)*
$(
$crate::create_peripheral!($gpio <= virtual);
)*
/// The `Peripherals` struct provides access to all of the hardware peripherals on the chip.
#[allow(non_snake_case)]
pub struct Peripherals {
$(
#[doc = concat!("The ", stringify!($name), " peripheral.")]
pub $name: $name<'static>,
)*
$(
#[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
crate feature is enabled. This comes with no stability guarantees, and could be changed
or removed at any time."]
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
pub $unstable_name: $unstable_name<'static>,
#[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
crate feature is enabled. This comes with no stability guarantees, and could be changed
or removed at any time."]
#[cfg(not(feature = "unstable"))]
#[allow(unused)]
pub(crate) $unstable_name: $unstable_name<'static>,
)*
$(
#[doc = stringify!($gpio)]
pub $gpio: $gpio<'static>,
)*
}
impl Peripherals {
/// Returns all the peripherals *once*
#[inline]
pub(crate) fn take() -> Self {
#[unsafe(no_mangle)]
static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
critical_section::with(|_| unsafe {
if _ESP_HAL_DEVICE_PERIPHERALS {
panic!("init called more than once!")
}
_ESP_HAL_DEVICE_PERIPHERALS = true;
Self::steal()
})
}
/// Unsafely create an instance of this peripheral out of thin air.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
pub unsafe fn steal() -> Self {
unsafe {
Self {
$(
$name: $name::steal(),
)*
$(
$unstable_name: $unstable_name::steal(),
)*
$(
$gpio: $gpio::steal(),
)*
}
}
}
}
$(
impl $name<'_> {
$(
generate_interrupt_fns!($interrupt_name, $interrupt);
)*
}
)*
$(
impl $unstable_name<'_> {
$(
generate_interrupt_fns!($unstable_interrupt_name, $unstable_interrupt);
)*
}
)*
};
}
#[doc(hidden)]
#[macro_export]
/// Macro to create a peripheral structure.
macro_rules! create_peripheral {
($(#[$attr:meta])? $name:ident <= virtual) => {
$(#[$attr])?
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[doc = concat!(stringify!($name), " peripheral singleton")]
pub struct $name<'a> {
_marker: core::marker::PhantomData<&'a mut ()>,
}
impl $name<'_> {
/// Unsafely create an instance of this peripheral out of thin air.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
pub unsafe fn steal() -> Self {
Self {
_marker: core::marker::PhantomData,
}
}
/// Unsafely clone this peripheral reference.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
#[allow(dead_code)]
pub unsafe fn clone_unchecked(&self) -> Self {
unsafe { Self::steal() }
}
/// Creates a new peripheral reference with a shorter lifetime.
///
/// Use this method if you would like to keep working with the peripheral after
/// you dropped the driver that consumes this.
#[inline]
#[allow(dead_code)]
pub fn reborrow(&mut self) -> $name<'_> {
unsafe { self.clone_unchecked() }
}
}
impl $crate::private::Sealed for $name<'_> {}
};
($(#[$attr:meta])? $name:ident <= $base:ident) => {
$crate::create_peripheral!($(#[$attr])? $name <= virtual);
impl $name<'_> {
#[doc = r"Pointer to the register block"]
#[instability::unstable]
pub const PTR: *const <pac::$base as core::ops::Deref>::Target = pac::$base::PTR;
#[doc = r"Return the pointer to the register block"]
#[inline(always)]
#[instability::unstable]
pub const fn ptr() -> *const <pac::$base as core::ops::Deref>::Target {
pac::$base::PTR
}
#[doc = r"Return a reference to the register block"]
#[inline(always)]
#[instability::unstable]
pub const fn regs<'a>() -> &'a <pac::$base as core::ops::Deref>::Target {
unsafe { &*Self::PTR }
}
#[doc = r"Return a reference to the register block"]
#[inline(always)]
#[instability::unstable]
pub fn register_block(&self) -> &<pac::$base as core::ops::Deref>::Target {
unsafe { &*Self::PTR }
}
}
};
}

242
esp-hal/src/peripherals.rs Normal file
View File

@ -0,0 +1,242 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
pub(crate) use crate::soc::pac;
/// Macro to create a peripheral structure.
macro_rules! create_peripheral {
($(#[$attr:meta])? $name:ident <= virtual ($($interrupt:ident: { $bind:ident, $enable:ident, $disable:ident }),*)) => {
$(#[$attr])?
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[doc = concat!(stringify!($name), " peripheral singleton")]
pub struct $name<'a> {
_marker: core::marker::PhantomData<&'a mut ()>,
}
impl $name<'_> {
/// Unsafely create an instance of this peripheral out of thin air.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
pub unsafe fn steal() -> Self {
Self {
_marker: core::marker::PhantomData,
}
}
/// Unsafely clone this peripheral reference.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
#[allow(dead_code)]
pub unsafe fn clone_unchecked(&self) -> Self {
unsafe { Self::steal() }
}
/// Creates a new peripheral reference with a shorter lifetime.
///
/// Use this method if you would like to keep working with the peripheral after
/// you dropped the driver that consumes this.
#[inline]
#[allow(dead_code)]
pub fn reborrow(&mut self) -> $name<'_> {
unsafe { self.clone_unchecked() }
}
$(
/// Binds an interrupt handler to the corresponding interrupt for this peripheral.
#[instability::unstable]
pub fn $bind(&self, handler: unsafe extern "C" fn() -> ()) {
unsafe { $crate::interrupt::bind_interrupt($crate::peripherals::Interrupt::$interrupt, handler); }
}
/// Disables the interrupt handler
#[instability::unstable]
pub fn $disable(&self) {
for core in $crate::system::Cpu::other() {
$crate::interrupt::disable(core, $crate::peripherals::Interrupt::$interrupt);
}
}
/// Enables the interrupt handler on the given core
#[instability::unstable]
pub fn $enable(&self, priority: $crate::interrupt::Priority) {
unwrap!($crate::interrupt::enable($crate::peripherals::Interrupt::$interrupt, priority));
}
)*
}
impl $crate::private::Sealed for $name<'_> {}
};
($(#[$attr:meta])? $name:ident <= $base:ident $interrupts:tt) => {
create_peripheral!($(#[$attr])? $name <= virtual $interrupts);
impl $name<'_> {
#[doc = r"Pointer to the register block"]
#[instability::unstable]
pub const PTR: *const <pac::$base as core::ops::Deref>::Target = pac::$base::PTR;
#[doc = r"Return the pointer to the register block"]
#[inline(always)]
#[instability::unstable]
pub const fn ptr() -> *const <pac::$base as core::ops::Deref>::Target {
pac::$base::PTR
}
#[doc = r"Return a reference to the register block"]
#[inline(always)]
#[instability::unstable]
pub const fn regs<'a>() -> &'a <pac::$base as core::ops::Deref>::Target {
unsafe { &*Self::PTR }
}
#[doc = r"Return a reference to the register block"]
#[inline(always)]
#[instability::unstable]
pub fn register_block(&self) -> &<pac::$base as core::ops::Deref>::Target {
unsafe { &*Self::PTR }
}
}
};
}
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));
for_each_gpio! {
($n:literal, $pin_peri:ident $af_ins:tt $af_outs:tt ($($attr:ident)*)) => {
$(
crate::io_type!($attr, $pin_peri);
)*
};
(all $( ($n:literal, $pin_peri:ident $af_ins:tt $af_outs:tt $attrs:tt) ),*) => {
crate::gpio! {
$( ($n, $pin_peri $af_ins $af_outs) )*
}
};
}
define_io_mux_reg!();
for_each_peripheral! {
// Define stable peripheral singletons
($name:ident <= $from_pac:tt $interrupts:tt) => {
create_peripheral!($name <= $from_pac $interrupts);
};
// Define unstable peripheral singletons
($name:ident <= $from_pac:tt $interrupts:tt (unstable)) => {
create_peripheral!(#[instability::unstable] $name <= $from_pac $interrupts);
};
// Define the Peripherals struct
(all $( ($name:ident <= $from_pac:tt $interrupts:tt $(($unstable:ident))?) ),*) => {
// We need a way to ignore the "unstable" marker, but macros can't generate attributes or struct fields.
// The solution is printing an empty doc comment.
macro_rules! ignore { ($any:tt) => {""} }
/// The `Peripherals` struct provides access to all of the hardware peripherals on the chip.
#[allow(non_snake_case)]
pub struct Peripherals {
$(
// This is a bit hairy, but non-macro attributes are not allowed on struct fields. We work
// around this by excluding code with the `$()?` optional macro syntax and an "unstable" marker
// in the source data. The marker itself is passed through the `ignore` macro so that it doesn't
// appear in the generated code.
//
// The code can end up looking two ways:
//
// - Without `unstable` we just generate the field:
// ```
// #[attributes]
// pub PERI: PERI<'static>,
// ```
//
// - With `unstable` we're basically emulating what `instability::unstable` would do:
// ```
// #[attributes]
// #[cfg(feature = "unstable")]
// pub PERI: PERI<'static>,
//
// #[attributes]
// #[cfg(not(feature = "unstable"))]
// pub(crate) PERI: PERI<'static>,
// ```
#[doc = concat!("The ", stringify!($name), " peripheral.")]
$(
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
crate feature is enabled. This comes with no stability guarantees, and could be changed
or removed at any time."]
#[doc = ignore!($unstable)]
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
)?
pub $name: $name<'static>,
$(
#[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
crate feature is enabled. This comes with no stability guarantees, and could be changed
or removed at any time."]
#[doc = ignore!($unstable)]
#[cfg(not(feature = "unstable"))]
#[allow(unused)]
pub(crate) $name: $name<'static>,
)?
)*
}
impl Peripherals {
/// Returns all the peripherals *once*
#[inline]
pub(crate) fn take() -> Self {
#[unsafe(no_mangle)]
static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
critical_section::with(|_| unsafe {
if _ESP_HAL_DEVICE_PERIPHERALS {
panic!("init called more than once!")
}
_ESP_HAL_DEVICE_PERIPHERALS = true;
Self::steal()
})
}
/// Unsafely create an instance of this peripheral out of thin air.
///
/// # Safety
///
/// You must ensure that you're only using one instance of this type at a time.
#[inline]
pub unsafe fn steal() -> Self {
unsafe {
Self {
$(
$name: $name::steal(),
)*
}
}
}
}
};
}

View File

@ -13,9 +13,10 @@ crate::unstable_module! {
}
pub mod cpu_control;
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub(crate) use esp32 as pac;
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub(crate) mod constants {
use crate::time::Rate;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -10,9 +10,10 @@ crate::unstable_module! {
pub mod trng;
}
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub(crate) use esp32c2 as pac;
#[allow(unused)]
pub(crate) mod registers {
pub const INTERRUPT_MAP_BASE: u32 = 0x600c2000;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32c2 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -14,9 +14,10 @@ crate::unstable_module! {
pub mod trng;
}
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub(crate) use esp32c3 as pac;
#[allow(unused)]
pub(crate) mod registers {
pub const INTERRUPT_MAP_BASE: u32 = 0x600c2000;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32c3 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -16,9 +16,10 @@ crate::unstable_module! {
pub mod trng;
}
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub(crate) use esp32c6 as pac;
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub(crate) mod registers {
pub const INTERRUPT_MAP_BASE: u32 = 0x60010000;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32c6 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -15,9 +15,10 @@ crate::unstable_module! {
pub mod trng;
}
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub(crate) use esp32h2 as pac;
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub(crate) mod registers {
pub const INTERRUPT_MAP_BASE: u32 = 0x60010000;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32h2 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -17,9 +17,10 @@ crate::unstable_module! {
pub mod ulp_core;
}
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub(crate) use esp32s2 as pac;
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub(crate) mod constants {
use crate::time::Rate;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32s2 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -18,9 +18,10 @@ crate::unstable_module! {
}
pub mod cpu_control;
pub mod gpio;
pub mod peripherals;
pub(crate) mod regi2c;
pub use esp32s3 as pac;
#[cfg_attr(not(feature = "unstable"), allow(unused))]
pub(crate) mod constants {
use crate::time::Rate;

View File

@ -1,17 +0,0 @@
//! # Peripheral Instances
//!
//! This module creates singleton instances for each of the various peripherals,
//! and re-exports them to allow users to access and use them in their
//! applications.
//!
//! Should be noted that that the module also re-exports the [Interrupt] enum
//! from the PAC, allowing users to handle interrupts associated with these
//! peripherals.
pub(crate) use esp32s3 as pac;
// We need to export this for users to use
#[doc(hidden)]
pub use pac::Interrupt;
include!(concat!(env!("OUT_DIR"), "/_generated_peris.rs"));
include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs"));

View File

@ -6,7 +6,7 @@ use std::str::FromStr;
use proc_macro2::TokenStream;
use quote::format_ident;
use crate::number;
use crate::{generate_for_each_macro, number};
/// Additional properties (besides those defined in cfg.rs) for [device.gpio].
/// These don't get turned into symbols, but are used to generate code.
@ -244,13 +244,6 @@ pub(crate) fn generate_gpios(gpio: &super::GpioProperties) -> TokenStream {
}
};
let mut io_type_macro_calls = vec![];
for (pin, attrs) in pin_peris.iter().zip(pin_attrs.iter()) {
io_type_macro_calls.push(quote::quote! {
#( crate::io_type!(#attrs, #pin); )*
})
}
// Generates a macro that can select between a `then` and an `else` branch based
// on whether a pin implement a certain attribute.
//
@ -314,17 +307,31 @@ pub(crate) fn generate_gpios(gpio: &super::GpioProperties) -> TokenStream {
}
};
quote::quote! {
crate::gpio! {
#( (#pin_numbers, #pin_peris #pin_afs) )*
let mut branches = vec![];
for (((n, p), af), attrs) in pin_numbers
.iter()
.zip(pin_peris.iter())
.zip(pin_afs.iter())
.zip(pin_attrs.iter())
{
branches.push(quote::quote! {
#n, #p #af (#(#attrs)*)
})
}
#( #io_type_macro_calls )*
let for_each = generate_for_each_macro("gpio", &branches);
quote::quote! {
#for_each
#if_pin_is_type
#impl_for_pin_type
macro_rules! define_io_mux_reg {
() => {
#io_mux_accessor
};
}
}
}

View File

@ -515,6 +515,7 @@ impl Config {
fn generate_peripherals_macro(&self) -> TokenStream {
let mut stable = vec![];
let mut unstable = vec![];
let mut all_peripherals = vec![];
let mut stable_peris = vec![];
@ -530,15 +531,16 @@ impl Config {
}
}
let gpios = if let Some(gpio) = self.device.peri_config.gpio.as_ref() {
gpio.pins_and_signals
.pins
.iter()
.map(|pin| format_ident!("GPIO{}", pin.pin))
.collect::<Vec<_>>()
} else {
vec![]
if let Some(gpio) = self.device.peri_config.gpio.as_ref() {
for pin in gpio.pins_and_signals.pins.iter() {
let pin = format_ident!("GPIO{}", pin.pin);
let tokens = quote::quote! {
#pin <= virtual ()
};
all_peripherals.push(quote::quote! { #tokens });
stable.push(tokens);
}
}
for peri in self.device.peripherals.iter() {
let hal = format_ident!("{}", peri.name);
@ -551,9 +553,11 @@ impl Config {
let mut interrupts = peri.interrupts.iter().collect::<Vec<_>>();
interrupts.sort_by_key(|(k, _)| k.as_str());
let interrupts = interrupts.iter().map(|(k, v)| {
let k = format_ident!("{k}");
let v = format_ident!("{v}");
quote::quote! { #k => #v }
let pac_interrupt_name = format_ident!("{v}");
let bind = format_ident!("bind_{k}_interrupt");
let enable = format_ident!("enable_{k}_interrupt");
let disable = format_ident!("disable_{k}_interrupt");
quote::quote! { #pac_interrupt_name: { #bind, #enable, #disable } }
});
let tokens = quote::quote! {
#hal <= #pac ( #(#interrupts),* )
@ -562,23 +566,15 @@ impl Config {
.iter()
.any(|p| peri.name.eq_ignore_ascii_case(p))
{
all_peripherals.push(quote::quote! { #tokens });
stable.push(tokens);
} else {
all_peripherals.push(quote::quote! { #tokens (unstable) });
unstable.push(tokens);
}
}
quote::quote! {
crate::peripherals! {
peripherals: [
#(#stable,)*
],
unstable_peripherals: [
#(#unstable,)*
],
pins: [#(#gpios,)*]
}
}
generate_for_each_macro("peripheral", &all_peripherals)
}
}
@ -590,13 +586,34 @@ fn generate_for_each_macro(name: &str, branches: &[TokenStream]) -> TokenStream
// macro that substitutes the properties into the template provided by the call in esp-hal.
macro_rules! #macro_name {
(
$pattern:tt => $code:tt;
$($pattern:tt => $code:tt;)*
) => {
macro_rules! _for_each_inner {
($pattern) => $code;
$(($pattern) => $code;)*
($other:tt) => {}
}
// Generate single macro calls with each branch
// Usage:
// ```
// for_each_x! {
// ( $pattern ) => {
// $code
// }
// }
// ```
#(_for_each_inner!(( #branches ));)*
// Generate a single macro call with all branches.
// Usage:
// ```
// for_each_x! {
// (all $( ($pattern) ),*) => {
// $( $code )*
// }
// }
// ```
_for_each_inner!((all #((#branches)),*));
};
}