Skip to content

Timer: universal time conversion#695

Open
omeh-a wants to merge 8 commits intomainfrom
new_time_conv
Open

Timer: universal time conversion#695
omeh-a wants to merge 8 commits intomainfrom
new_time_conv

Conversation

@omeh-a
Copy link
Copy Markdown
Member

@omeh-a omeh-a commented Apr 9, 2026

This change adds a universal time conversion module for our timer driver. The design goals are as follows:

  • Minimise error in calculations
  • Support ALL possible sDDF timer devices. I.e. a range of frequencies from 1->~4GHz (for TSC and similar devices)
  • Run for at least 100 years without overflow
  • Simple interface that minimises the surface area for human error when writing new drivers.

I based the method on Linux's method of calculating a multiple and shift to miminise integer division error for arbitrary frequencies. Originally, this was optimal as I made the common code work entirely with 64-bit arithmetic, but I needed to add 128 bit calls for devices like the TSC which go to many GHz. As a result, there is arguably a discussion to be had here re: bothering with this method. We could simply do in-place 128 bit division to calculate the frequency and not worry about error, but we should probably do the math to work out how much error occurs.

Common time conversion logic

Previously, every timer driver tried to do its own frequency conversion math. Unfortunately, most of them did it in ways that are either wrong, cannot run for a long enough time, or both. Given the complexity of the math required and the significant room for error in performing the integer arithmetic with a wide range of possible clock frequencies, I have added a common time conversion module for all drivers to use in time_conv.c.

This function uses a method heavily based on Linux's time conversion method. Rather than calculating terms like ns = (ticks * freq) / NS_IN_S directly, this method converts all such fractions into a multiply-and-shift operation representing the fraction more precisely for (efficient) integer arithmetic.

Rather than manually defining conversions to certain time units as needed, we instead model all jiffy-to-time conversions as a shift of frequencies. For example, rather than converting "to nanoseconds", we can instead convert the number of periods at some frequency f_a to the number of periods in another frequency, f_b. If f_b is 1GHz, a period is equal to exactly 1 nanosecond. This means that we can have a single robust method that handles the conversion in both directions, for all frequencies. If we say the number of periods in f_a is t_a and the number of periods in f_b is t_b, the algebra looks as follows, where we want to find t_b given f_a, f_bandt_a`.

$$ t_{f_b} = \frac{{t_{f_a}} \times f_b} {f_a} $$

To avoid loss in integer division, we convert the equation into the following shape:

$$ t_{f_b} = \frac{t_{f_a} \times M}{2^{S}} $$

Where M and S are arbitrary numbers encoding the value of f_a and f_b by effectively moving everything but f_b's factor of 2 to the numerator of the expression. In the code, we evaluate this expression as

uint64_t do_freq_shift(uint64_t t_a, uint64_t mult, uint64_t shift) {
     uint64_t multiplicand = t_a * mult;
    return (uint64_t) (multiplicand >> shift);
}

... returning t_b, since we can find a division by 2 to the power of S with a left shift.

This method of conversion should guarantee about 100 years of time conversions accurately for any frequency in Hz that can be stored in 32 bits.

An iterative method is used to calculate the mult and shift, where we effectively test a bunch of possible shift values and settle on one that gives the desired accuracy. This should find an optimal set of magic numbers for all frequencies that fit in 32 bits (up to ~4.2GHz) for 100 years of runtime.

Common time cached time conversion logic

To make the above mult-shift operation more friendly, I have added timer_common.c, a file implementing two methods:

  • tick_to_ns_cached
  • ns_to_tick_cached

These methods take in the two required frequencies and do all conversions necessary transparently. The mult and shift used for ns->ticks and ticks-> ns are cached for efficiency. Timer drivers are not forced to use this logic - they may want to use their own method if they do something complex like dynamically reprogramming the prescaler. For all ten of the existing drivers however, the caching behaviour is optimal and should be used.

Why hide state / supply these methods commonly?

Time conversion in particular has proven itself to be very capable of confusing contributors to the sDDF. The mult-shift interface is not particularly friendly or intuitive, and extracting the cached mult-shift values to the front of the interface is likely to be error prone since the significance of these values may not be obvious at a glance. Using this common logic is "fool proof" and will provide optimal behaviour in all cases, and as a result I think it's a suitable compromise to have internal caching, since this is the only way we can provide a "convert to ticks" and "convert to nanoseconds" API efficiently and without risking confusion if we use the Linux-style runtime optimisation. If we decide to drop the runtime-optimising time conversion the common module is obviously irrelevant and we can drop it.

omeh-a added 7 commits April 9, 2026 14:42
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>

wip

Added universal time conversion to common timer driver logic, inc. memoisation. Tested, works well! Only added to imx and apb_timer for now

Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>

Minor changes to timer common API for clarity + exposing raw API, added support for all timer drivers

Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Signed-off-by: Lesley Rossouw <lesley.rossouw@unsw.edu.au>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant