mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-16 21:05:35 +01:00
Refactor race condition free timer helper (#850)
* Implement half_period_counter in rtic-time * Rename compute_now to calculate_now, use it in stm32 and imxrt * Add more tests * Add some docs * Fix clippy warning, add imxrt timer to monotonics tests * Bump dependency version to make sure monotonics will build properly * Add changelog to rtic-monotonics * Add more docs * Add more docs * Finish documentation * Fix typos * Switch from atomic-polyfill to portable-atomic * Some more doc fixes * More doc fixes * Minor doc fix * Minor doc fix * Fix Atomics not existing * Fix example * Minor example improvement * Revert back to atomic-polyfill * Fix cargo.toml formatting * Remove atomic-polyfill * Attempt to fix unused macro warning * Remove atomics completely from half period counter * Minor doc fix * Doc fixes * Doc fixes * Remove obsolete comment * Fix ordering in monotonic initialization sequence
This commit is contained in:
parent
3de5f793f3
commit
c227a71d24
10 changed files with 350 additions and 49 deletions
|
|
@ -11,6 +11,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
|
|||
|
||||
- **Soundness fix:** Monotonics did not wait long enough in `Duration` based delays.
|
||||
|
||||
### Changed
|
||||
|
||||
- Bump `rtic-time`
|
||||
|
||||
## v1.3.0 - 2023-11-08
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ features = [
|
|||
rustdoc-flags = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rtic-time = { version = "1.0.0", path = "../rtic-time" }
|
||||
rtic-time = { version = "1.1.0", path = "../rtic-time" }
|
||||
embedded-hal = { version = "1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "1.0.0-rc.2", optional = true }
|
||||
fugit = { version = "0.3.6" }
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@
|
|||
//! ```
|
||||
|
||||
use crate::{Monotonic, TimeoutError, TimerQueue};
|
||||
use atomic_polyfill::{compiler_fence, AtomicU32, Ordering};
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rtic_time::half_period_counter::calculate_now;
|
||||
|
||||
use imxrt_ral as ral;
|
||||
|
||||
|
|
@ -73,29 +74,6 @@ macro_rules! create_imxrt_gpt2_token {
|
|||
}};
|
||||
}
|
||||
|
||||
// Credits to the `time-driver` of `embassy-stm32`.
|
||||
//
|
||||
// Clock timekeeping works with something we call "periods", which are time intervals
|
||||
// of 2^31 ticks. The Clock counter value is 32 bits, so one "overflow cycle" is 2 periods.
|
||||
//
|
||||
// A `period` count is maintained in parallel to the Timer hardware `counter`, like this:
|
||||
// - `period` and `counter` start at 0
|
||||
// - `period` is incremented on overflow (at counter value 0)
|
||||
// - `period` is incremented "midway" between overflows (at counter value 0x8000_0000)
|
||||
//
|
||||
// Therefore, when `period` is even, counter is in 0..0x7FFF_FFFF. When odd, counter is in 0x8000_0000..0xFFFF_FFFF
|
||||
// This allows for now() to return the correct value even if it races an overflow.
|
||||
//
|
||||
// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
|
||||
// the expected range for the `period` parity, we're done. If it doesn't, this means that
|
||||
// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
|
||||
// corresponds to the next period.
|
||||
//
|
||||
// `period` is a 32bit integer, so it overflows on 2^32 * 2^31 / 1_000_000 seconds of uptime, which is 292471 years.
|
||||
fn calc_now(period: u32, counter: u32) -> u64 {
|
||||
(u64::from(period) << 31) + u64::from(counter ^ ((period & 1) << 31))
|
||||
}
|
||||
|
||||
macro_rules! make_timer {
|
||||
($mono_name:ident, $timer:ident, $period:ident, $tq:ident$(, doc: ($($doc:tt)*))?) => {
|
||||
/// Timer implementing [`Monotonic`] which runs at 1 MHz.
|
||||
|
|
@ -142,7 +120,7 @@ macro_rules! make_timer {
|
|||
);
|
||||
|
||||
// Reset period
|
||||
$period.store(0, Ordering::Relaxed);
|
||||
$period.store(0, Ordering::SeqCst);
|
||||
|
||||
// Prescaler
|
||||
ral::modify_reg!(ral::gpt, gpt, PR,
|
||||
|
|
@ -231,12 +209,10 @@ macro_rules! make_timer {
|
|||
fn now() -> Self::Instant {
|
||||
let gpt = unsafe{ $timer::instance() };
|
||||
|
||||
// Important: period **must** be read first.
|
||||
let period = $period.load(Ordering::Relaxed);
|
||||
compiler_fence(Ordering::Acquire);
|
||||
let counter = ral::read_reg!(ral::gpt, gpt, CNT);
|
||||
|
||||
Self::Instant::from_ticks(calc_now(period, counter))
|
||||
Self::Instant::from_ticks(calculate_now(
|
||||
$period.load(Ordering::Relaxed),
|
||||
|| ral::read_reg!(ral::gpt, gpt, CNT)
|
||||
))
|
||||
}
|
||||
|
||||
fn set_compare(instant: Self::Instant) {
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@
|
|||
//! ```
|
||||
|
||||
use crate::{Monotonic, TimeoutError, TimerQueue};
|
||||
use atomic_polyfill::{compiler_fence, AtomicU64, Ordering};
|
||||
use atomic_polyfill::{AtomicU64, Ordering};
|
||||
pub use fugit::{self, ExtU64, ExtU64Ceil};
|
||||
use rtic_time::half_period_counter::calculate_now;
|
||||
use stm32_metapac as pac;
|
||||
|
||||
mod _generated {
|
||||
|
|
@ -166,13 +167,14 @@ macro_rules! make_timer {
|
|||
// Since this is not the case, it should be cleared.
|
||||
$timer.sr().modify(|r| r.set_uif(false));
|
||||
|
||||
$tq.initialize(Self {});
|
||||
$overflow.store(0, Ordering::SeqCst);
|
||||
|
||||
// Start the counter.
|
||||
$timer.cr1().modify(|r| {
|
||||
r.set_cen(true);
|
||||
});
|
||||
|
||||
$tq.initialize(Self {});
|
||||
|
||||
// SAFETY: We take full ownership of the peripheral and interrupt vector,
|
||||
// plus we are not using any external shared resources so we won't impact
|
||||
// basepri/source masking based critical sections.
|
||||
|
|
@ -231,18 +233,10 @@ macro_rules! make_timer {
|
|||
const TICK_PERIOD: Self::Duration = Self::Duration::from_ticks(1);
|
||||
|
||||
fn now() -> Self::Instant {
|
||||
// Credits to the `time-driver` of `embassy-stm32`.
|
||||
// For more info, see the `imxrt` driver.
|
||||
fn calc_now(period: u64, counter: $bits) -> u64 {
|
||||
(period << ($bits::BITS - 1)) + u64::from(counter ^ (((period & 1) as $bits) << ($bits::BITS - 1)))
|
||||
}
|
||||
|
||||
// Important: period **must** be read first.
|
||||
let period = $overflow.load(Ordering::Relaxed);
|
||||
compiler_fence(Ordering::Acquire);
|
||||
let counter = $timer.cnt().read().cnt();
|
||||
|
||||
Self::Instant::from_ticks(calc_now(period, counter))
|
||||
Self::Instant::from_ticks(calculate_now(
|
||||
$overflow.load(Ordering::Relaxed),
|
||||
|| $timer.cnt().read().cnt()
|
||||
))
|
||||
}
|
||||
|
||||
fn set_compare(instant: Self::Instant) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue