Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix C2 delays #1981

Merged
merged 9 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Auto detect crystal frequency based on `RtcClock::estimate_xtal_frequency()` (#1165)
- ESP32-S3: Configure 32k ICACHE (#1169)
- Lift the minimal buffer size requirement for I2S (#1189)
- Replaced `SystemTimer::TICKS_PER_SEC` with `SystemTimer::ticks_per_sec()` (#1981)

### Removed

Expand Down
24 changes: 24 additions & 0 deletions esp-hal/src/clock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
//! ```

use fugit::HertzU32;
#[cfg(esp32c2)]
use portable_atomic::{AtomicU32, Ordering};

#[cfg(any(esp32, esp32c2))]
use crate::rtc_cntl::RtcClock;
Expand Down Expand Up @@ -333,6 +335,26 @@ pub struct RawClocks {
pub pll_96m_clock: HertzU32,
}

cfg_if::cfg_if! {
if #[cfg(esp32c2)] {
static XTAL_FREQ_MHZ: AtomicU32 = AtomicU32::new(40);

pub(crate) fn xtal_freq_mhz() -> u32 {
XTAL_FREQ_MHZ.load(Ordering::Relaxed)
}
} else if #[cfg(esp32h2)] {
pub(crate) fn xtal_freq_mhz() -> u32 {
32
}
} else if #[cfg(any(esp32, esp32s2))] {
// Function would be unused
} else {
pub(crate) fn xtal_freq_mhz() -> u32 {
40
}
}
}

/// Used to configure the frequencies of the clocks present in the chip.
///
/// After setting all frequencies, call the freeze function to apply the
Expand Down Expand Up @@ -429,6 +451,7 @@ impl<'d> ClockControl<'d> {
} else {
26
};
XTAL_FREQ_MHZ.store(xtal_freq, Ordering::Relaxed);

ClockControl {
_private: clock_control.into_ref(),
Expand All @@ -452,6 +475,7 @@ impl<'d> ClockControl<'d> {
} else {
XtalClock::RtcXtalFreq26M
};
XTAL_FREQ_MHZ.store(xtal_freq.mhz(), Ordering::Relaxed);

let pll_freq = PllClock::Pll480MHz;

Expand Down
122 changes: 38 additions & 84 deletions esp-hal/src/delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,25 @@
//! [DelayUs]: embedded_hal_02::blocking::delay::DelayUs
//! [embedded-hal]: https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html

use fugit::HertzU64;
pub use fugit::MicrosDurationU64;

use crate::clock::Clocks;

/// Delay driver
///
/// Uses the `SYSTIMER` peripheral internally for RISC-V devices, and the
/// built-in Xtensa timer for Xtensa devices.
#[derive(Clone, Copy)]
pub struct Delay {
freq: HertzU64,
}

impl Delay {
/// Delay for the specified number of milliseconds
pub fn delay_millis(&self, ms: u32) {
for _ in 0..ms {
self.delay_micros(1000);
}
}
}
#[non_exhaustive]
pub struct Delay;

#[cfg(feature = "embedded-hal-02")]
impl<T> embedded_hal_02::blocking::delay::DelayMs<T> for Delay
where
T: Into<u32>,
{
fn delay_ms(&mut self, ms: T) {
for _ in 0..ms.into() {
self.delay_micros(1000);
}
self.delay_millis(ms.into());
}
}

Expand All @@ -80,83 +69,48 @@ impl embedded_hal::delay::DelayNs for Delay {
}
}

#[cfg(riscv)]
mod implementation {
use super::*;
use crate::{clock::Clocks, timer::systimer::SystemTimer};

impl Delay {
/// Create a new `Delay` instance
pub fn new(clocks: &Clocks<'_>) -> Self {
// The counters and comparators are driven using `XTAL_CLK`.
// The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
// The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
Self {
#[cfg(not(esp32h2))]
freq: HertzU64::MHz(clocks.xtal_clock.to_MHz() as u64 * 10 / 25),
#[cfg(esp32h2)]
// esp32h2 TRM, section 11.2 Clock Source Selection
freq: HertzU64::MHz(clocks.xtal_clock.to_MHz() as u64 * 10 / 20),
}
}

/// Delay for the specified time
pub fn delay(&self, time: MicrosDurationU64) {
let t0 = SystemTimer::now();
let rate: HertzU64 = MicrosDurationU64::from_ticks(1).into_rate();
let clocks = time.ticks() * (self.freq / rate);
impl Delay {
/// Creates a new `Delay` instance.
// Do not remove the argument, it makes sure that the clocks are initialized.
pub fn new(_clocks: &Clocks<'_>) -> Self {
Self {}
}

while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {}
}
/// Delay for the specified time
pub fn delay(&self, delay: MicrosDurationU64) {
let start = crate::time::current_time();

/// Delay for the specified number of microseconds
pub fn delay_micros(&self, us: u32) {
let t0 = SystemTimer::now();
let clocks = us as u64 * (self.freq / HertzU64::MHz(1));
while elapsed_since(start) < delay {}
}

while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {}
/// Delay for the specified number of milliseconds
pub fn delay_millis(&self, ms: u32) {
for _ in 0..ms {
self.delay_micros(1000);
}
}

/// Delay for the specified number of nanoseconds
pub fn delay_nanos(&self, ns: u32) {
let t0 = SystemTimer::now();
let clocks = ns as u64 * (self.freq / HertzU64::MHz(1)) / 1000;
/// Delay for the specified number of microseconds
pub fn delay_micros(&self, us: u32) {
let delay = MicrosDurationU64::micros(us as u64);
self.delay(delay);
}

while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {}
}
/// Delay for the specified number of nanoseconds
pub fn delay_nanos(&self, ns: u32) {
let delay = MicrosDurationU64::nanos(ns as u64);
self.delay(delay);
}
}

#[cfg(xtensa)]
mod implementation {
use super::*;
use crate::clock::Clocks;

impl Delay {
/// Create a new `Delay` instance
pub fn new(clocks: &Clocks<'_>) -> Self {
Self {
freq: clocks.cpu_clock.into(),
}
}

/// Delay for the specified time
pub fn delay(&self, time: MicrosDurationU64) {
let rate: HertzU64 = MicrosDurationU64::from_ticks(1).into_rate();
let clocks = time.ticks() * (self.freq / rate);
xtensa_lx::timer::delay(clocks as u32);
}

/// Delay for the specified number of microseconds
pub fn delay_micros(&self, us: u32) {
let clocks = us as u64 * (self.freq / HertzU64::MHz(1));
xtensa_lx::timer::delay(clocks as u32);
}
fn elapsed_since(start: fugit::Instant<u64, 1, 1_000_000>) -> MicrosDurationU64 {
let now = crate::time::current_time();

/// Delay for the specified number of nanoseconds
pub fn delay_nanos(&self, ns: u32) {
let clocks = ns as u64 * (self.freq / HertzU64::MHz(1)) / 1000;
xtensa_lx::timer::delay(clocks as u32);
}
if start.ticks() <= now.ticks() {
now - start
} else {
// current_time specifies at least 7 happy years, let's ignore this issue for
// now.
panic!("Time has wrapped around, which we currently don't handle");
}
}
2 changes: 1 addition & 1 deletion esp-hal/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn current_time() -> fugit::Instant<u64, 1, 1_000_000> {
let ticks = crate::timer::systimer::SystemTimer::now();
(
ticks,
(crate::timer::systimer::SystemTimer::TICKS_PER_SECOND / 1_000_000),
(crate::timer::systimer::SystemTimer::ticks_per_second() / 1_000_000),
)
};

Expand Down
31 changes: 24 additions & 7 deletions esp-hal/src/timer/systimer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,37 @@ impl<'d> SystemTimer<'d> {
if #[cfg(esp32s2)] {
/// Bitmask to be applied to the raw register value.
pub const BIT_MASK: u64 = u64::MAX;
/// The ticks per second the underlying peripheral uses.
pub const TICKS_PER_SECOND: u64 = 80_000_000;
// Bitmask to be applied to the raw period register value.
const PERIOD_MASK: u64 = 0x1FFF_FFFF;
} else {
/// Bitmask to be applied to the raw register value.
pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF;
/// The ticks per second the underlying peripheral uses.
pub const TICKS_PER_SECOND: u64 = 16_000_000;
// Bitmask to be applied to the raw period register value.
const PERIOD_MASK: u64 = 0x3FF_FFFF;
}
}

/// Returns the tick frequency of the underlying timer unit.
pub fn ticks_per_second() -> u64 {
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
80_000_000
} else if #[cfg(esp32h2)] {
// The counters and comparators are driven using `XTAL_CLK`.
// The average clock frequency is fXTAL_CLK/2, which is 16 MHz.
// The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
const MULTIPLIER: u64 = 10_000_000 / 20;
crate::clock::xtal_freq_mhz() as u64 * MULTIPLIER
} else {
// The counters and comparators are driven using `XTAL_CLK`.
// The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
// The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
const MULTIPLIER: u64 = 10_000_000 / 25;
crate::clock::xtal_freq_mhz() as u64 * MULTIPLIER
}
}
}

/// Create a new instance.
pub fn new(_systimer: impl Peripheral<P = SYSTIMER> + 'd) -> Self {
#[cfg(soc_etm)]
Expand Down Expand Up @@ -810,7 +827,7 @@ where
}

let us = period.ticks();
let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000) as u32;
let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000) as u32;

self.comparator.set_mode(ComparatorMode::Period);
self.comparator.set_period(ticks);
Expand Down Expand Up @@ -879,7 +896,7 @@ where
}
};

let us = ticks / (SystemTimer::TICKS_PER_SECOND / 1_000_000);
let us = ticks / (SystemTimer::ticks_per_second() / 1_000_000);

Instant::<u64, 1, 1_000_000>::from_ticks(us)
}
Expand All @@ -888,7 +905,7 @@ where
let mode = self.comparator.get_mode();

let us = value.ticks();
let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000);
let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000);

if matches!(mode, ComparatorMode::Period) {
// Period mode
Expand Down
4 changes: 2 additions & 2 deletions examples/src/bin/systimer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ fn main() -> ! {
alarm0.enable_interrupt(true);

alarm1.set_interrupt_handler(systimer_target1);
alarm1.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 2));
alarm1.set_target(SystemTimer::now() + (SystemTimer::ticks_per_second() * 2));
alarm1.enable_interrupt(true);

alarm2.set_interrupt_handler(systimer_target2);
alarm2.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 3));
alarm2.set_target(SystemTimer::now() + (SystemTimer::ticks_per_second() * 3));
alarm2.enable_interrupt(true);

ALARM0.borrow_ref_mut(cs).replace(alarm0);
Expand Down
31 changes: 23 additions & 8 deletions hil-test/tests/delay.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Delay Test

// esp32c2 is disabled currently as it fails
//% CHIPS: esp32 esp32c3 esp32c6 esp32s3
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32s2 esp32s3

#![no_std]
#![no_main]
Expand Down Expand Up @@ -37,25 +36,33 @@ mod tests {
}

#[test]
#[timeout(1)]
#[timeout(2)]
fn delay_ns(mut ctx: Context) {
let t1 = esp_hal::time::current_time();
ctx.delay.delay_ns(600_000_000);
let t2 = esp_hal::time::current_time();

assert!(t2 > t1);
assert!((t2 - t1).to_nanos() >= 600_000_000u64);
assert!(
(t2 - t1).to_nanos() >= 600_000_000u64,
"diff: {:?}",
(t2 - t1).to_nanos()
);
}

#[test]
#[timeout(1)]
#[timeout(2)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some weirdness here. This test finishes in ~1.23s. Considering a simple assert!(1+1=2) takes ~0.6s, I hope the timeout I saw here isn't caused by a still-wrong tick rate.

fn delay_700millis(ctx: Context) {
let t1 = esp_hal::time::current_time();
ctx.delay.delay_millis(700);
let t2 = esp_hal::time::current_time();

assert!(t2 > t1);
assert!((t2 - t1).to_millis() >= 700u64);
assert!(
(t2 - t1).to_millis() >= 700u64,
"diff: {:?}",
(t2 - t1).to_millis()
);
}

#[test]
Expand All @@ -66,7 +73,11 @@ mod tests {
let t2 = esp_hal::time::current_time();

assert!(t2 > t1);
assert!((t2 - t1).to_micros() >= 1_500_000u64);
assert!(
(t2 - t1).to_micros() >= 1_500_000u64,
"diff: {:?}",
(t2 - t1).to_micros()
);
}

#[test]
Expand All @@ -77,6 +88,10 @@ mod tests {
let t2 = esp_hal::time::current_time();

assert!(t2 > t1);
assert!((t2 - t1).to_millis() >= 3000u64);
assert!(
(t2 - t1).to_millis() >= 3000u64,
"diff: {:?}",
(t2 - t1).to_millis()
);
}
}
3 changes: 1 addition & 2 deletions hil-test/tests/embassy_timers_executors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Embassy timer and executor Test

// esp32c2 is disabled currently as it fails
//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s3
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3

#![no_std]
#![no_main]
Expand Down
Loading