Move timer instance config into driver metadata (#3626)

* Remove timg_timer1 symbol

* Ensure instances exist

* Rename timers to timergroup

* Remove unnecessary cfg
This commit is contained in:
Dániel Buga 2025-06-12 16:49:04 +02:00 committed by GitHub
parent 1b5a85e7d6
commit 793b01beaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 145 additions and 71 deletions

View File

@ -168,7 +168,7 @@ fn main() -> Result<(), Box<dyn Error>> {
for (key, value) in &cfg {
if let Value::Bool(true) = value {
config_symbols.push(key);
config_symbols.push(key.to_string());
}
}
@ -230,7 +230,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// Helper Functions
fn copy_dir_all(
config_symbols: &[&str],
config_symbols: &[String],
cfg: &HashMap<String, Value>,
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
@ -260,7 +260,7 @@ fn copy_dir_all(
/// A naive pre-processor for linker scripts
fn preprocess_file(
config: &[&str],
config: &[String],
cfg: &HashMap<String, Value>,
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
@ -279,7 +279,7 @@ fn preprocess_file(
if let Some(condition) = trimmed.strip_prefix("#IF ") {
let should_take = take.iter().all(|v| *v);
let should_take = should_take && config.contains(&condition);
let should_take = should_take && config.iter().any(|c| c.as_str() == condition);
take.push(should_take);
continue;
} else if trimmed == "#ELSE" {

View File

@ -58,8 +58,9 @@ pub struct WatchdogConfig {
/// Configures the reset watchdog timer.
rwdt: WatchdogStatus,
/// Configures the `timg0` watchdog timer.
#[cfg(timergroup_timg0)]
timg0: WatchdogStatus,
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
/// Configures the `timg1` watchdog timer.
///
/// By default, the bootloader does not enable this watchdog timer.

View File

@ -308,7 +308,7 @@ unstable_module! {
// Drivers needed for initialization or they are tightly coupled to something else.
#[cfg(any(adc1, adc2, dac))]
pub mod analog;
#[cfg(any(systimer, timg0, timg1))]
#[cfg(any(systimer, timergroup))]
pub mod timer;
#[cfg(any(lp_clkrst, rtc_cntl))]
pub mod rtc_cntl;
@ -325,7 +325,6 @@ unstable_driver! {
pub mod aes;
#[cfg(assist_debug)]
pub mod assist_debug;
#[cfg(any(xtensa, all(riscv, systimer)))]
pub mod delay;
#[cfg(ecc)]
pub mod ecc;
@ -634,6 +633,7 @@ pub fn init(config: Config) -> Peripherals {
}
}
#[cfg(timergroup_timg0)]
match config.watchdog.timg0() {
WatchdogStatus::Enabled(duration) => {
let mut timg0_wd = crate::timer::timg::Wdt::<crate::peripherals::TIMG0<'static>>::new();
@ -645,7 +645,7 @@ pub fn init(config: Config) -> Peripherals {
}
}
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
match config.watchdog.timg1() {
WatchdogStatus::Enabled(duration) => {
let mut timg1_wd = crate::timer::timg::Wdt::<crate::peripherals::TIMG1<'static>>::new();
@ -664,9 +664,10 @@ pub fn init(config: Config) -> Peripherals {
rtc.rwdt.disable();
#[cfg(timergroup_timg0)]
crate::timer::timg::Wdt::<crate::peripherals::TIMG0<'static>>::new().disable();
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
crate::timer::timg::Wdt::<crate::peripherals::TIMG1<'static>>::new().disable();
}
}

View File

@ -129,6 +129,7 @@ impl Peripheral {
Peripheral::UsbDevice,
#[cfg(systimer)]
Peripheral::Systimer,
#[cfg(timg0)]
Peripheral::Timg0,
#[cfg(esp32c6)] // used by some wifi calibration steps.
// TODO: We should probably automatically enable this when needed.

View File

@ -60,7 +60,7 @@ use crate::{
#[cfg(systimer)]
pub mod systimer;
#[cfg(any(timg0, timg1))]
#[cfg(timergroup)]
pub mod timg;
/// Timer errors.
@ -416,6 +416,7 @@ where
crate::any_peripheral! {
/// Any Timer peripheral.
pub peripheral AnyTimer<'d> {
#[cfg(timergroup)]
TimgTimer(timg::Timer<'d>),
#[cfg(systimer)]
SystimerAlarm(systimer::Alarm<'d>),

View File

@ -69,7 +69,7 @@
use core::marker::PhantomData;
use super::Error;
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
use crate::peripherals::TIMG1;
#[cfg(any(esp32c6, esp32h2))]
use crate::soc::constants::TIMG_DEFAULT_CLK_SRC;
@ -84,21 +84,21 @@ use crate::{
time::{Duration, Instant, Rate},
};
const NUM_TIMG: usize = 1 + cfg!(timg1) as usize;
const NUM_TIMG: usize = 1 + cfg!(timergroup_timg1) as usize;
cfg_if::cfg_if! {
// We need no locks when a TIMG has a single timer, and we don't need locks for ESP32
// and S2 where the effective interrupt enable register (config) is not shared between
// the timers.
if #[cfg(all(timg_timer1, not(any(esp32, esp32s2))))] {
if #[cfg(all(timergroup_timg_has_timer1, not(any(esp32, esp32s2))))] {
use crate::sync::{lock, RawMutex};
static INT_ENA_LOCK: [RawMutex; NUM_TIMG] = [const { RawMutex::new() }; NUM_TIMG];
}
}
/// A timer group consisting of
#[cfg_attr(not(timg_timer1), doc = "a general purpose timer")]
#[cfg_attr(timg_timer1, doc = "2 timers")]
#[cfg_attr(not(timergroup_timg_has_timer1), doc = "a general purpose timer")]
#[cfg_attr(timergroup_timg_has_timer1, doc = "2 timers")]
/// and a watchdog timer.
pub struct TimerGroup<'d, T>
where
@ -108,7 +108,7 @@ where
/// Timer 0
pub timer0: Timer<'d>,
/// Timer 1
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
pub timer1: Timer<'d>,
/// Watchdog timer
pub wdt: Wdt<T>,
@ -125,6 +125,7 @@ pub trait TimerGroupInstance {
fn wdt_interrupt() -> Interrupt;
}
#[cfg(timergroup_timg0)]
impl TimerGroupInstance for TIMG0<'_> {
fn id() -> u8 {
0
@ -186,7 +187,7 @@ impl TimerGroupInstance for TIMG0<'_> {
}
}
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
impl TimerGroupInstance for crate::peripherals::TIMG1<'_> {
fn id() -> u8 {
1
@ -262,7 +263,7 @@ where
register_block: T::register_block(),
_lifetime: PhantomData,
},
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
timer1: Timer {
timer: 1,
tg: T::id(),
@ -325,11 +326,11 @@ impl super::Timer for Timer<'_> {
fn async_interrupt_handler(&self) -> InterruptHandler {
match (self.timer_group(), self.timer_number()) {
(0, 0) => asynch::timg0_timer0_handler,
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
(0, 1) => asynch::timg0_timer1_handler,
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
(1, 0) => asynch::timg1_timer0_handler,
#[cfg(all(timg_timer1, timg1))]
#[cfg(all(timergroup_timg_has_timer1, timergroup_timg1))]
(1, 1) => asynch::timg1_timer1_handler,
_ => unreachable!(),
}
@ -338,11 +339,11 @@ impl super::Timer for Timer<'_> {
fn peripheral_interrupt(&self) -> Interrupt {
match (self.timer_group(), self.timer_number()) {
(0, 0) => Interrupt::TG0_T0_LEVEL,
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
(0, 1) => Interrupt::TG0_T1_LEVEL,
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
(1, 0) => Interrupt::TG1_T0_LEVEL,
#[cfg(all(timg_timer1, timg1))]
#[cfg(all(timergroup_timg_has_timer1, timergroup_timg1))]
(1, 1) => Interrupt::TG1_T1_LEVEL,
_ => unreachable!(),
}
@ -398,11 +399,11 @@ impl Timer<'_> {
pub(crate) fn set_interrupt_handler(&self, handler: InterruptHandler) {
let interrupt = match (self.timer_group(), self.timer_number()) {
(0, 0) => Interrupt::TG0_T0_LEVEL,
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
(0, 1) => Interrupt::TG0_T1_LEVEL,
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
(1, 0) => Interrupt::TG1_T0_LEVEL,
#[cfg(all(timg_timer1, timg1))]
#[cfg(all(timergroup_timg_has_timer1, timergroup_timg1))]
(1, 1) => Interrupt::TG1_T1_LEVEL,
_ => unreachable!(),
};
@ -560,7 +561,7 @@ impl Timer<'_> {
.t(self.timer as usize)
.config()
.modify(|_, w| w.level_int_en().bit(state));
} else if #[cfg(timg_timer1)] {
} else if #[cfg(timergroup_timg_has_timer1)] {
lock(&INT_ENA_LOCK[self.timer_group() as usize], || {
self.register_block()
.int_ena()
@ -828,7 +829,7 @@ mod asynch {
use crate::asynch::AtomicWaker;
const NUM_WAKERS: usize = {
let timer_per_group = 1 + cfg!(timg_timer1) as usize;
let timer_per_group = 1 + cfg!(timergroup_timg_has_timer1) as usize;
NUM_TIMG * timer_per_group
};
@ -859,7 +860,7 @@ mod asynch {
});
}
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
#[handler]
pub(crate) fn timg1_timer0_handler() {
handle_irq(Timer {
@ -870,7 +871,7 @@ mod asynch {
});
}
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
#[handler]
pub(crate) fn timg0_timer1_handler() {
handle_irq(Timer {
@ -881,7 +882,7 @@ mod asynch {
});
}
#[cfg(all(timg1, timg_timer1))]
#[cfg(all(timergroup_timg1, timergroup_timg_has_timer1))]
#[handler]
pub(crate) fn timg1_timer1_handler() {
handle_irq(Timer {

View File

@ -67,7 +67,6 @@ symbols = [
"pdma",
"phy",
"psram",
"timg_timer1",
"touch",
"large_intr_status",
"gpio_bank_1",
@ -108,6 +107,10 @@ channel_ram_size = 64
[device.spi_master]
status = "supported"
[device.timergroup]
timg_has_timer1 = true
instances = [{ name = "timg0" }, { name = "timg1" }]
[device.uart]
status = "supported"
@ -146,7 +149,7 @@ status = "not_supported"
[device.psram]
[device.temp_sensor]
[device.sleep]
[device.timers]
[device.ulp_fsm]
## Radio

View File

@ -80,6 +80,9 @@ bus_timeout_is_exponential = true
[device.spi_master]
status = "supported"
[device.timergroup]
instances = [{ name = "timg0" }]
[device.uart]
status = "supported"
@ -103,7 +106,6 @@ status = "supported"
[device.io_mux]
[device.temp_sensor]
[device.sleep]
[device.timers]
[device.systimer]
## Radio

View File

@ -100,6 +100,9 @@ channel_ram_size = 48
[device.spi_master]
status = "supported"
[device.timergroup]
instances = [{ name = "timg0" }, { name = "timg1" }]
[device.uart]
status = "supported"
@ -129,7 +132,6 @@ status = "not_supported"
[device.interrupts]
[device.io_mux]
[device.temp_sensor]
[device.timers]
[device.sleep]
[device.systimer]

View File

@ -131,6 +131,9 @@ channel_ram_size = 48
[device.spi_master]
status = "supported"
[device.timergroup]
instances = [{ name = "timg0" }, { name = "timg1" }]
[device.uart]
status = "supported"
@ -172,7 +175,6 @@ has_wifi6 = true
[device.sleep]
[device.temp_sensor]
[device.systimer]
[device.timers]
[device.ulp_riscv]
## Radio

View File

@ -113,6 +113,9 @@ channel_ram_size = 48
[device.spi_master]
status = "supported"
[device.timergroup]
instances = [{ name = "timg0" }, { name = "timg1" }]
[device.uart]
status = "supported"
@ -149,7 +152,6 @@ status = "not_supported"
[device.sleep]
[device.systimer]
[device.temp_sensor]
[device.timers]
## Radio
[device.bt]

View File

@ -65,7 +65,6 @@ symbols = [
"psram",
"psram_dma",
"ulp_riscv_core",
"timg_timer1",
"large_intr_status",
"gpio_bank_1",
"spi_octal",
@ -107,6 +106,10 @@ channel_ram_size = 64
[device.spi_master]
status = "supported"
[device.timergroup]
timg_has_timer1 = true
instances = [{ name = "timg0" }, { name = "timg1" }]
[device.uart]
status = "supported"
@ -148,7 +151,6 @@ status = "not_supported"
[device.psram]
[device.sleep]
[device.systimer]
[device.timers]
[device.temp_sensor]
[device.ulp_fsm]
[device.ulp_riscv]

View File

@ -79,7 +79,6 @@ symbols = [
"psram_dma",
"octal_psram",
"ulp_riscv_core",
"timg_timer1",
"very_large_intr_status",
"gpio_bank_1",
"spi_octal",
@ -127,6 +126,10 @@ channel_ram_size = 48
[device.spi_master]
status = "supported"
[device.timergroup]
timg_has_timer1 = true
instances = [{ name = "timg0" }, { name = "timg1" }]
[device.uart]
status = "supported"
@ -168,7 +171,6 @@ status = "not_supported"
[device.sleep]
[device.systimer]
[device.temp_sensor]
[device.timers]
[device.ulp_fsm]
[device.ulp_riscv]

View File

@ -1,7 +1,7 @@
use core::str::FromStr;
use std::{fmt::Write, sync::OnceLock};
use anyhow::{Result, bail};
use anyhow::{Result, bail, ensure};
use proc_macro2::TokenStream;
use strum::IntoEnumIterator;
@ -257,6 +257,20 @@ impl SupportStatus {
}
}
/// An empty configuration, used when a driver just wants to declare that
/// it supports a peripheral, but does not have any configuration options.
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
struct EmptyInstanceConfig {}
/// A peripheral instance for which a driver is implemented.
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
struct PeriInstance<I> {
/// The name of the instance
name: String,
#[serde(flatten)]
instance_config: I,
}
struct SupportItem {
name: &'static str,
config_group: &'static str,
@ -276,9 +290,10 @@ macro_rules! driver_configs {
struct $struct {
#[serde(default)]
status: SupportStatus,
// If empty, the driver supports a single instance only.
/// The list of peripherals for which this driver is implemented.
/// If empty, the driver supports a single instance only.
#[serde(default)]
instances: Vec<String>,
instances: Vec<PeriInstance<EmptyInstanceConfig>>,
$(
$(#[$meta])?
$config: $ty,
@ -362,6 +377,14 @@ macro_rules! driver_configs {
} )))*
}
fn driver_instances(&self) -> impl Iterator<Item = String> {
// Chain all driver instances from each driver.
std::iter::empty()
$(.chain(self.$driver.iter().flat_map(|d| {
d.instances.iter().map(|i| format!("{}.{}", stringify!($driver), i.name.as_str()))
})))*
}
/// Returns an iterator over all properties of all peripherals.
fn properties(&self) -> impl Iterator<Item = (&str, Value)> {
// Chain all properties from each driver.
@ -617,10 +640,13 @@ driver_configs![
properties: {}
},
TimersProperties {
driver: timers,
driver: timergroup,
name: "Timers",
peripherals: &["timg0", "timg1"],
properties: {}
peripherals: &[],
properties: {
#[serde(default)]
timg_has_timer1: bool,
}
},
TouchProperties {
driver: touch,
@ -702,7 +728,7 @@ pub struct Config {
impl Config {
/// The configuration for the specified chip.
pub fn for_chip(chip: &Chip) -> &Self {
match chip {
let config = match chip {
Chip::Esp32 => include_toml!(Config, "../devices/esp32.toml"),
Chip::Esp32c2 => include_toml!(Config, "../devices/esp32c2.toml"),
Chip::Esp32c3 => include_toml!(Config, "../devices/esp32c3.toml"),
@ -710,7 +736,11 @@ impl Config {
Chip::Esp32h2 => include_toml!(Config, "../devices/esp32h2.toml"),
Chip::Esp32s2 => include_toml!(Config, "../devices/esp32s2.toml"),
Chip::Esp32s3 => include_toml!(Config, "../devices/esp32s3.toml"),
}
};
config.validate().expect("Invalid device configuration");
config
}
/// Create an empty configuration
@ -729,6 +759,19 @@ impl Config {
}
}
fn validate(&self) -> Result<()> {
for instance in self.device.peri_config.driver_instances() {
let (driver, peri) = instance.split_once('.').unwrap();
ensure!(
self.device.peripherals.iter().any(|p| p == peri),
"Driver {driver} marks an implementation for '{peri}' but this peripheral is not defined for '{}'",
self.device.name
);
}
Ok(())
}
/// The name of the device.
pub fn name(&self) -> String {
self.device.name.clone()
@ -767,25 +810,31 @@ impl Config {
}
/// All configuration values for the device.
pub fn all(&self) -> impl Iterator<Item = &str> + '_ {
pub fn all(&self) -> impl Iterator<Item = String> + '_ {
[
self.device.name.as_str(),
self.device.arch.as_ref(),
self.device.name.clone(),
self.device.arch.to_string(),
match self.cores() {
Cores::Single => "single_core",
Cores::Multi => "multi_core",
Cores::Single => String::from("single_core"),
Cores::Multi => String::from("multi_core"),
},
]
.into_iter()
.chain(self.device.peripherals.iter().map(|s| s.as_str()))
.chain(self.device.symbols.iter().map(|s| s.as_str()))
.chain(self.device.peri_config.driver_names())
.chain(self.device.peripherals.iter().cloned())
.chain(self.device.symbols.iter().cloned())
.chain(
self.device
.peri_config
.driver_names()
.map(|name| name.to_string()),
)
.chain(self.device.peri_config.driver_instances())
.chain(
self.device
.peri_config
.properties()
.filter_map(|(name, value)| match value {
Value::Boolean(true) => Some(name),
Value::Boolean(true) => Some(name.to_string()),
_ => None,
}),
)

View File

@ -137,14 +137,14 @@ pub use executor::Executor;
macro_rules! init_embassy {
($peripherals:expr, 2) => {{
cfg_if::cfg_if! {
if #[cfg(timg_timer1)] {
if #[cfg(timergroup_timg_has_timer1)] {
use esp_hal::timer::timg::TimerGroup;
let timg0 = TimerGroup::new($peripherals.TIMG0);
esp_hal_embassy::init([
timg0.timer0,
timg0.timer1,
]);
} else if #[cfg(timg1)] {
} else if #[cfg(timergroup_timg1)] {
use esp_hal::timer::timg::TimerGroup;
let timg0 = TimerGroup::new($peripherals.TIMG0);
let timg1 = TimerGroup::new($peripherals.TIMG1);

View File

@ -94,22 +94,23 @@ mod tests {
test_async_delay_ns(OneShotTimer::new(alarms.alarm0).into_async(), 10_000).await;
}
#[cfg(timergroup_timg0)]
#[test]
async fn test_timg0_async_delay_ns(ctx: Context) {
let timg0 = TimerGroup::new(ctx.peripherals.TIMG0);
test_async_delay_ns(OneShotTimer::new(timg0.timer0).into_async(), 10_000).await;
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
test_async_delay_ns(OneShotTimer::new(timg0.timer1).into_async(), 10_000).await;
}
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
#[test]
async fn test_timg1_async_delay_ns(ctx: Context) {
let timg1 = TimerGroup::new(ctx.peripherals.TIMG1);
test_async_delay_ns(OneShotTimer::new(timg1.timer0).into_async(), 10_000).await;
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
test_async_delay_ns(OneShotTimer::new(timg1.timer1).into_async(), 10_000).await;
}
@ -121,22 +122,23 @@ mod tests {
test_async_delay_us(OneShotTimer::new(alarms.alarm0).into_async(), 10).await;
}
#[cfg(timergroup_timg0)]
#[test]
async fn test_timg0_async_delay_us(ctx: Context) {
let timg0 = TimerGroup::new(ctx.peripherals.TIMG0);
test_async_delay_us(OneShotTimer::new(timg0.timer0).into_async(), 10).await;
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
test_async_delay_us(OneShotTimer::new(timg0.timer1).into_async(), 10).await;
}
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
#[test]
async fn test_timg1_async_delay_us(ctx: Context) {
let timg1 = TimerGroup::new(ctx.peripherals.TIMG1);
test_async_delay_us(OneShotTimer::new(timg1.timer0).into_async(), 10).await;
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
test_async_delay_us(OneShotTimer::new(timg1.timer1).into_async(), 10).await;
}
@ -148,22 +150,23 @@ mod tests {
test_async_delay_ms(OneShotTimer::new(alarms.alarm0).into_async(), 1).await;
}
#[cfg(timergroup_timg0)]
#[test]
async fn test_timg0_async_delay_ms(ctx: Context) {
let timg0 = TimerGroup::new(ctx.peripherals.TIMG0);
test_async_delay_ms(OneShotTimer::new(timg0.timer0).into_async(), 1).await;
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
test_async_delay_ms(OneShotTimer::new(timg0.timer1).into_async(), 1).await;
}
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
#[test]
async fn test_timg1_async_delay_ms(ctx: Context) {
let timg1 = TimerGroup::new(ctx.peripherals.TIMG1);
test_async_delay_ms(OneShotTimer::new(timg1.timer0).into_async(), 1).await;
#[cfg(timg_timer1)]
#[cfg(timergroup_timg_has_timer1)]
test_async_delay_ms(OneShotTimer::new(timg1.timer1).into_async(), 1).await;
}
}

View File

@ -25,6 +25,7 @@ mod tests {
use super::*;
#[test]
#[cfg(timergroup_timg0)]
fn test_feeding_timg0_wdt() {
let peripherals = esp_hal::init(
Config::default().with_watchdog(
@ -44,7 +45,7 @@ mod tests {
}
#[test]
#[cfg(timg1)]
#[cfg(timergroup_timg1)]
fn test_feeding_timg1_wdt() {
let peripherals = esp_hal::init(
Config::default().with_watchdog(
@ -64,6 +65,7 @@ mod tests {
}
#[test]
#[cfg(timergroup_timg0)]
fn test_feeding_timg0_wdt_max_clock() {
let peripherals = esp_hal::init(
Config::default()