#![cfg_attr(esp32s2, doc = "64-bit")]
#![cfg_attr(not(esp32s2), doc = "52-bit")]
use core::{fmt::Debug, marker::PhantomData, num::NonZeroU32};
use esp_sync::RawMutex;
use super::{Error, Timer as _};
use crate::{
asynch::AtomicWaker,
interrupt::{self, InterruptHandler},
peripherals::{Interrupt, SYSTIMER},
system::{Cpu, Peripheral as PeripheralEnable, PeripheralClockControl},
time::{Duration, Instant},
};
#[unsafe(no_mangle)]
#[cfg(feature = "rt")]
static mut ESP_HAL_SYSTIMER_CORRECTION: NonZeroU32 = NonZeroU32::new(SHIFT_TIMESTAMP_FLAG).unwrap();
#[cfg(not(feature = "rt"))]
unsafe extern "Rust" {
static mut ESP_HAL_SYSTIMER_CORRECTION: NonZeroU32;
}
const SHIFT_TIMESTAMP_FLAG: u32 = 0x8000_0000;
const SHIFT_MASK: u32 = 0x0000_FFFF;
const UNEVEN_DIVIDER_FLAG: u32 = 0x4000_0000;
const UNEVEN_MULTIPLIER: u32 = if cfg!(esp32h2) { 2 } else { 5 };
const UNEVEN_DIVIDER_MASK: u32 = 0x0000_FFFF;
#[derive(Copy, Clone)]
pub enum UnitConfig {
Disabled,
DisabledIfCpuIsStalled(Cpu),
Enabled,
}
pub struct SystemTimer<'d> {
pub alarm0: Alarm<'d>,
pub alarm1: Alarm<'d>,
pub alarm2: Alarm<'d>,
}
impl<'d> SystemTimer<'d> {
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
pub const BIT_MASK: u64 = u64::MAX;
const PERIOD_MASK: u64 = 0x1FFF_FFFF;
} else {
pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF;
const PERIOD_MASK: u64 = 0x3FF_FFFF;
}
}
#[cfg(feature = "rt")]
pub(crate) fn init_timestamp_scaler() {
let systimer_rate = Self::ticks_per_second();
let packed_rate_and_method = if systimer_rate.is_multiple_of(1_000_000) {
let ticks_per_us = systimer_rate as u32 / 1_000_000;
if ticks_per_us.is_power_of_two() {
SHIFT_TIMESTAMP_FLAG | (ticks_per_us.ilog2() & SHIFT_MASK)
} else {
ticks_per_us
}
} else {
let multiplied_ticks_per_us = (systimer_rate * UNEVEN_MULTIPLIER as u64) / 1_000_000;
UNEVEN_DIVIDER_FLAG | (multiplied_ticks_per_us as u32)
};
unsafe {
let correction_ptr = &raw mut ESP_HAL_SYSTIMER_CORRECTION;
*correction_ptr = unwrap!(NonZeroU32::new(packed_rate_and_method));
}
}
#[inline]
pub(crate) fn ticks_to_us(ticks: u64) -> u64 {
let correction = unsafe { ESP_HAL_SYSTIMER_CORRECTION };
let correction = correction.get();
match correction & (SHIFT_TIMESTAMP_FLAG | UNEVEN_DIVIDER_FLAG) {
v if v == SHIFT_TIMESTAMP_FLAG => ticks >> (correction & SHIFT_MASK),
v if v == UNEVEN_DIVIDER_FLAG => {
let multiplied = if UNEVEN_MULTIPLIER.is_power_of_two() {
ticks << UNEVEN_MULTIPLIER.ilog2()
} else {
ticks * UNEVEN_MULTIPLIER as u64
};
let divider = correction & UNEVEN_DIVIDER_MASK;
multiplied / divider as u64
}
_ => ticks / correction as u64,
}
}
#[inline]
fn us_to_ticks(us: u64) -> u64 {
let correction = unsafe { ESP_HAL_SYSTIMER_CORRECTION };
let correction = correction.get();
match correction & (SHIFT_TIMESTAMP_FLAG | UNEVEN_DIVIDER_FLAG) {
v if v == SHIFT_TIMESTAMP_FLAG => us << (correction & SHIFT_MASK),
v if v == UNEVEN_DIVIDER_FLAG => {
let multiplier = correction & UNEVEN_DIVIDER_MASK;
let multiplied = us * multiplier as u64;
if UNEVEN_MULTIPLIER.is_power_of_two() {
multiplied >> UNEVEN_MULTIPLIER.ilog2()
} else {
multiplied / UNEVEN_MULTIPLIER as u64
}
}
_ => us * correction as u64,
}
}
#[inline]
pub fn ticks_per_second() -> u64 {
cfg_if::cfg_if! {
if #[cfg(esp32c5)] {
16_000_000
} else {
crate::soc::clocks::ClockTree::with(|clocks| {
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
crate::soc::clocks::apb_clk_frequency(clocks) as u64
} else if #[cfg(esp32h2)] {
(crate::soc::clocks::xtal_clk_frequency(clocks) / 2) as u64
} else {
(crate::soc::clocks::xtal_clk_frequency(clocks) * 10 / 25) as u64
}
}
})
}
}
}
pub fn new(_systimer: SYSTIMER<'d>) -> Self {
if PeripheralClockControl::enable(PeripheralEnable::Systimer) {
PeripheralClockControl::reset(PeripheralEnable::Systimer);
} else {
PeripheralClockControl::disable(PeripheralEnable::Systimer);
}
#[cfg(etm_driver_supported)]
etm::enable_etm();
Self {
alarm0: Alarm::new(Comparator::Comparator0),
alarm1: Alarm::new(Comparator::Comparator1),
alarm2: Alarm::new(Comparator::Comparator2),
}
}
#[inline]
pub fn unit_value(unit: Unit) -> u64 {
unit.read_count()
}
#[cfg(not(esp32s2))]
pub unsafe fn configure_unit(unit: Unit, config: UnitConfig) {
unit.configure(config)
}
pub unsafe fn set_unit_value(unit: Unit, value: u64) {
unit.set_count(value)
}
}
#[cfg_attr(esp32s2, doc = "64-bit")]
#[cfg_attr(not(esp32s2), doc = "52-bit")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Unit {
Unit0 = 0,
#[cfg(not(esp32s2))]
Unit1 = 1,
}
impl Unit {
#[inline]
fn channel(&self) -> u8 {
*self as _
}
#[cfg(not(esp32s2))]
fn configure(&self, config: UnitConfig) {
CONF_LOCK.lock(|| {
SYSTIMER::regs().conf().modify(|_, w| match config {
UnitConfig::Disabled => match self.channel() {
0 => w.timer_unit0_work_en().clear_bit(),
1 => w.timer_unit1_work_en().clear_bit(),
_ => unreachable!(),
},
UnitConfig::DisabledIfCpuIsStalled(cpu) => match self.channel() {
0 => {
w.timer_unit0_work_en().set_bit();
w.timer_unit0_core0_stall_en().bit(cpu == Cpu::ProCpu);
w.timer_unit0_core1_stall_en().bit(cpu != Cpu::ProCpu)
}
1 => {
w.timer_unit1_work_en().set_bit();
w.timer_unit1_core0_stall_en().bit(cpu == Cpu::ProCpu);
w.timer_unit1_core1_stall_en().bit(cpu != Cpu::ProCpu)
}
_ => unreachable!(),
},
UnitConfig::Enabled => match self.channel() {
0 => {
w.timer_unit0_work_en().set_bit();
w.timer_unit0_core0_stall_en().clear_bit();
w.timer_unit0_core1_stall_en().clear_bit()
}
1 => {
w.timer_unit1_work_en().set_bit();
w.timer_unit1_core0_stall_en().clear_bit();
w.timer_unit1_core1_stall_en().clear_bit()
}
_ => unreachable!(),
},
});
});
}
fn set_count(&self, value: u64) {
let systimer = SYSTIMER::regs();
#[cfg(not(esp32s2))]
{
let unitload = systimer.unitload(self.channel() as _);
let unit_load = systimer.unit_load(self.channel() as _);
unitload.hi().write(|w| w.load_hi().set((value << 32) as _));
unitload
.lo()
.write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _));
unit_load.write(|w| w.load().set_bit());
}
#[cfg(esp32s2)]
{
systimer
.load_hi()
.write(|w| w.load_hi().set((value << 32) as _));
systimer
.load_lo()
.write(|w| w.load_lo().set((value & 0xFFFF_FFFF) as _));
systimer.load().write(|w| w.load().set_bit());
}
}
#[inline]
fn read_count(&self) -> u64 {
let channel = self.channel() as usize;
let systimer = SYSTIMER::regs();
systimer.unit_op(channel).write(|w| w.update().set_bit());
while !systimer.unit_op(channel).read().value_valid().bit_is_set() {}
let unit_value = systimer.unit_value(channel);
let mut lo_prev = unit_value.lo().read().bits();
loop {
let lo = lo_prev;
let hi = unit_value.hi().read().bits();
lo_prev = unit_value.lo().read().bits();
if lo == lo_prev {
return ((hi as u64) << 32) | lo as u64;
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum Comparator {
Comparator0,
Comparator1,
Comparator2,
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Alarm<'d> {
comp: Comparator,
unit: Unit,
_lifetime: PhantomData<&'d mut ()>,
}
impl Alarm<'_> {
const fn new(comp: Comparator) -> Self {
Alarm {
comp,
unit: Unit::Unit0,
_lifetime: PhantomData,
}
}
pub unsafe fn clone_unchecked(&self) -> Self {
Self {
comp: self.comp,
unit: self.unit,
_lifetime: PhantomData,
}
}
pub fn reborrow(&mut self) -> Alarm<'_> {
unsafe { self.clone_unchecked() }
}
#[inline]
fn channel(&self) -> u8 {
self.comp as u8
}
fn set_enable(&self, enable: bool) {
CONF_LOCK.lock(|| {
#[cfg(not(esp32s2))]
SYSTIMER::regs().conf().modify(|_, w| match self.channel() {
0 => w.target0_work_en().bit(enable),
1 => w.target1_work_en().bit(enable),
2 => w.target2_work_en().bit(enable),
_ => unreachable!(),
});
});
#[cfg(esp32s2)]
SYSTIMER::regs()
.target_conf(self.channel() as usize)
.modify(|_r, w| w.work_en().bit(enable));
}
fn is_enabled(&self) -> bool {
#[cfg(not(esp32s2))]
match self.channel() {
0 => SYSTIMER::regs().conf().read().target0_work_en().bit(),
1 => SYSTIMER::regs().conf().read().target1_work_en().bit(),
2 => SYSTIMER::regs().conf().read().target2_work_en().bit(),
_ => unreachable!(),
}
#[cfg(esp32s2)]
SYSTIMER::regs()
.target_conf(self.channel() as usize)
.read()
.work_en()
.bit()
}
#[cfg(not(esp32s2))]
pub fn set_unit(&self, unit: Unit) {
SYSTIMER::regs()
.target_conf(self.channel() as usize)
.modify(|_, w| w.timer_unit_sel().bit(matches!(unit, Unit::Unit1)));
}
fn set_mode(&self, mode: ComparatorMode) {
let is_period_mode = match mode {
ComparatorMode::Period => true,
ComparatorMode::Target => false,
};
SYSTIMER::regs()
.target_conf(self.channel() as usize)
.modify(|_, w| w.period_mode().bit(is_period_mode));
}
fn mode(&self) -> ComparatorMode {
if SYSTIMER::regs()
.target_conf(self.channel() as usize)
.read()
.period_mode()
.bit()
{
ComparatorMode::Period
} else {
ComparatorMode::Target
}
}
fn set_period(&self, value: u32) {
let systimer = SYSTIMER::regs();
let tconf = systimer.target_conf(self.channel() as usize);
unsafe { tconf.modify(|_, w| w.period().bits(value)) };
#[cfg(not(esp32s2))]
{
let comp_load = systimer.comp_load(self.channel() as usize);
comp_load.write(|w| w.load().set_bit());
}
}
fn set_target(&self, value: u64) {
let systimer = SYSTIMER::regs();
let target = systimer.trgt(self.channel() as usize);
target.hi().write(|w| w.hi().set((value >> 32) as u32));
target
.lo()
.write(|w| w.lo().set((value & 0xFFFF_FFFF) as u32));
#[cfg(not(esp32s2))]
{
let comp_load = systimer.comp_load(self.channel() as usize);
comp_load.write(|w| w.load().set_bit());
}
}
fn set_interrupt_handler(&self, handler: InterruptHandler) {
let interrupt = match self.channel() {
0 => Interrupt::SYSTIMER_TARGET0,
1 => Interrupt::SYSTIMER_TARGET1,
2 => Interrupt::SYSTIMER_TARGET2,
_ => unreachable!(),
};
for core in crate::system::Cpu::other() {
crate::interrupt::disable(core, interrupt);
}
#[cfg(not(esp32s2))]
interrupt::bind_handler(interrupt, handler);
#[cfg(esp32s2)]
{
static mut HANDLERS: [Option<crate::interrupt::IsrCallback>; 3] = [None, None, None];
#[crate::ram]
extern "C" fn _handle_interrupt<const CH: u8>() {
if SYSTIMER::regs().int_raw().read().target(CH).bit_is_set() {
let handler = unsafe { HANDLERS[CH as usize] };
if let Some(handler) = handler {
(handler.callback())();
}
}
}
let priority = handler.priority();
unsafe {
HANDLERS[self.channel() as usize] = Some(handler.handler());
}
let handler = match self.channel() {
0 => _handle_interrupt::<0>,
1 => _handle_interrupt::<1>,
2 => _handle_interrupt::<2>,
_ => unreachable!(),
};
interrupt::bind_handler(
interrupt,
crate::interrupt::InterruptHandler::new(handler, priority),
);
}
}
}
#[derive(Copy, Clone)]
enum ComparatorMode {
Period,
Target,
}
impl super::Timer for Alarm<'_> {
fn start(&self) {
self.set_enable(true);
}
fn stop(&self) {
self.set_enable(false);
}
fn reset(&self) {
#[cfg(esp32s2)]
SYSTIMER::regs()
.step()
.modify(|_, w| unsafe { w.xtal_step().bits(0x1) });
#[cfg(not(esp32s2))]
SYSTIMER::regs()
.conf()
.modify(|_, w| w.timer_unit0_core0_stall_en().clear_bit());
}
fn is_running(&self) -> bool {
self.is_enabled()
}
fn now(&self) -> Instant {
let ticks = self.unit.read_count();
let us = SystemTimer::ticks_to_us(ticks);
Instant::from_ticks(us)
}
fn load_value(&self, value: Duration) -> Result<(), Error> {
let mode = self.mode();
let us = value.as_micros();
let ticks = SystemTimer::us_to_ticks(us);
if matches!(mode, ComparatorMode::Period) {
if (ticks & !SystemTimer::PERIOD_MASK) != 0 {
return Err(Error::InvalidTimeout);
}
self.set_period(ticks as u32);
self.set_mode(ComparatorMode::Target);
self.set_mode(ComparatorMode::Period);
} else {
#[cfg(not(esp32s2))]
if (ticks & !SystemTimer::BIT_MASK) != 0 {
return Err(Error::InvalidTimeout);
}
let v = self.unit.read_count();
let t = v + ticks;
self.set_target(t);
}
Ok(())
}
fn enable_auto_reload(&self, auto_reload: bool) {
let mode = if auto_reload {
ComparatorMode::Period
} else {
ComparatorMode::Target
};
self.set_mode(mode)
}
fn enable_interrupt(&self, state: bool) {
INT_ENA_LOCK.lock(|| {
SYSTIMER::regs()
.int_ena()
.modify(|_, w| w.target(self.channel()).bit(state));
});
}
fn clear_interrupt(&self) {
SYSTIMER::regs()
.int_clr()
.write(|w| w.target(self.channel()).clear_bit_by_one());
}
fn is_interrupt_set(&self) -> bool {
SYSTIMER::regs()
.int_raw()
.read()
.target(self.channel())
.bit_is_set()
}
fn async_interrupt_handler(&self) -> InterruptHandler {
match self.channel() {
0 => asynch::target0_handler,
1 => asynch::target1_handler,
2 => asynch::target2_handler,
_ => unreachable!(),
}
}
fn peripheral_interrupt(&self) -> Interrupt {
match self.channel() {
0 => Interrupt::SYSTIMER_TARGET0,
1 => Interrupt::SYSTIMER_TARGET1,
2 => Interrupt::SYSTIMER_TARGET2,
_ => unreachable!(),
}
}
fn set_interrupt_handler(&self, handler: InterruptHandler) {
self.set_interrupt_handler(handler)
}
fn waker(&self) -> &AtomicWaker {
asynch::waker(self)
}
}
impl crate::private::Sealed for Alarm<'_> {}
static CONF_LOCK: RawMutex = RawMutex::new();
static INT_ENA_LOCK: RawMutex = RawMutex::new();
mod asynch {
use core::marker::PhantomData;
use procmacros::handler;
use super::*;
use crate::asynch::AtomicWaker;
const NUM_ALARMS: usize = 3;
static WAKERS: [AtomicWaker; NUM_ALARMS] = [const { AtomicWaker::new() }; NUM_ALARMS];
pub(super) fn waker(alarm: &Alarm<'_>) -> &'static AtomicWaker {
&WAKERS[alarm.channel() as usize]
}
#[inline]
fn handle_alarm(comp: Comparator) {
Alarm {
comp,
unit: Unit::Unit0,
_lifetime: PhantomData,
}
.enable_interrupt(false);
WAKERS[comp as usize].wake();
}
#[handler]
pub(crate) fn target0_handler() {
handle_alarm(Comparator::Comparator0);
}
#[handler]
pub(crate) fn target1_handler() {
handle_alarm(Comparator::Comparator1);
}
#[handler]
pub(crate) fn target2_handler() {
handle_alarm(Comparator::Comparator2);
}
}
#[cfg(etm_driver_supported)]
pub mod etm {
#![cfg_attr(docsrs, procmacros::doc_replace)]
use super::*;
pub struct Event {
id: u8,
}
impl Event {
pub fn new(alarm: &Alarm<'_>) -> Self {
Self {
id: 50 + alarm.channel(),
}
}
}
impl crate::private::Sealed for Event {}
impl crate::etm::EtmEvent for Event {
fn id(&self) -> u8 {
self.id
}
}
pub(super) fn enable_etm() {
SYSTIMER::regs().conf().modify(|_, w| w.etm_en().set_bit());
}
}