rtic icon indicating copy to clipboard operation
rtic copied to clipboard

Monotonics: non-hardcoded tick rate

Open AfoHT opened this issue 1 year ago • 2 comments

By @Finomnis in Matrix chat


I wonder if it would be worth redesigning monotonics to not have a hardcoded tick rate. Then the given input clock frequency could simply become the denominator of the Instant/Duration.

There is also another reason I am interested in a rewrite: the Monotonic trait is a weird mix of an internal and an external API. It is intended to be used as an interface for the Timer Queue, which is kind of an internal implementation detail. And yet its now function is kind of a public end-user API.

Steps that I think about:

  • make Monotonic purely internal
  • make Monotonic purely tick passed, no fugit involved
  • create a wrapper struct in the user's code via a macro that then converts the "now" from the tick based monotonic to a fugit based timestamp

We need to proxy the delay functions of the timer queue anyway, so we could simply perform the conversion in those proxy functions.

One more thing- I would like to rewrite all those if now >= wakeup to if signed(now - wakeup) >= 0 to allow for a clean handling of wrapping monotonics.

I know all of this would definitely require a new major version bump of rtic-monotonics and rtic-time.

What are your thoughts?

AfoHT avatar Dec 25 '23 09:12 AfoHT

Still interested in thoughts :)

Finomnis avatar Dec 25 '23 18:12 Finomnis

Here is a sketchup of what I kind of had in mind:

// Macro generated user code, like
// gpt1_mono!(1_000_000);
// Could then also be used to generate the interrupt handler.
struct Gpt1;

impl TimerQueueBasedMono for Gpt1 {
    type Backend = Gpt1Backend;
    type Instant = fugit::Instant<
        <<Self as TimerQueueBasedMono>::Backend as TimerQueueMonoBackend>::TickType,
        1,
        1_000_000,
    >;
}

// Code in `rtic-monotonics`
struct Gpt1Backend;
impl TimerQueueMonoBackend for Gpt1Backend {
    type TickType = u64;
}

// Code in `rtic-time`
trait TimerQueueBasedMono {
    type Backend: TimerQueueMonoBackend;
    type Instant;
}

trait TimerQueueMonoBackend {
    type TickType;

    // ... timer queue required functions.
    // IMPORTANT: Timer queue is completely tick based
    // and does not know about instants. It only does `u32`/`u64` ticks.
    // It doesn't need to know about absolute time at any point.
}

trait Monotonic {
    type Instant;

    fn now(&mut self) -> Self::Instant;

    // ... other user-facing mono functions like delay, delay_until
}

impl<T> Monotonic for T
where
    T: TimerQueueBasedMono,
{
    type Instant = <T as TimerQueueBasedMono>::Instant;

    fn now(&mut self) -> Self::Instant {
        // We know T is `TimerQueueBasedMono`, so we can call `TimerQueue` stuff here.
        todo!()
    }
}

Previous version that required generic const expressions:

Hidden
// Macro generated user code, like
// gpt1_mono!(1_000_000);
// Could then also be used to generate the interrupt handler.
struct Gpt1;

impl TimerQueueBasedMono for Gpt1 {
    type Backend = Gpt1Backend;
    const TICK_FREQUENCY: u32 = 1_000_000;
}

// Code in `rtic-monotonics`
struct Gpt1Backend;
impl TimerQueueMonoBackend for Gpt1Backend {
    type TickType = u64;
}

// Code in `rtic-time`
trait TimerQueueBasedMono {
    type Backend: TimerQueueMonoBackend;
    const TICK_FREQUENCY: u32;
}

trait TimerQueueMonoBackend {
    type TickType;

    // ... timer queue required functions.
    // IMPORTANT: Timer queue is completely tick based
    // and does not know about instants. It only does `u32`/`u64` ticks.
    // It doesn't need to know about absolute time at any point.
}

trait Monotonic {
    type Instant;

    fn now(&mut self) -> Self::Instant;

    // ... other user-facing mono functions like delay, delay_until
}

impl<T> Monotonic for T
where
    T: TimerQueueBasedMono,
{
    type Instant = fugit::Instant<
        <<T as TimerQueueBasedMono>::Backend as TimerQueueMonoBackend>::TickType,
        1,
        { <T as TimerQueueBasedMono>::TICK_FREQUENCY }, // <- This is a problem currently, requires `generic_const_exprs`
    >;

    fn now(&mut self) -> Self::Instant {
        // We know T is `TimerQueueBasedMono`, so we can call `TimerQueue` stuff here.
    }
}

Finomnis avatar Dec 27 '23 05:12 Finomnis