Skip to content

Conversation

@Ivan-Velickovic
Copy link
Collaborator

@Ivan-Velickovic Ivan-Velickovic commented Sep 16, 2025

Draft because I need to cleanup and double check the code for the timer driver, please do not comment until I have marked ready for review.

Signed-off-by: Ivan-Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
Signed-off-by: Ivan Velickovic <i.velickovic@unsw.edu.au>
uint64_t value_whole_seconds = value_ticks / TIMER_TICKS_PER_SECOND;
uint64_t value_subsecond_ticks = value_ticks % TIMER_TICKS_PER_SECOND;
uint64_t value_subsecond_ns = (value_subsecond_ticks * NS_IN_S) / TIMER_TICKS_PER_SECOND;
uint64_t value_ns = value_whole_seconds * NS_IN_S + value_subsecond_ns;
Copy link
Member

Choose a reason for hiding this comment

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

This method of converting is wonky and you should instead calculate this using clock periods.

The conventional way is to use 1/f = T (period), but since we use integer arithmetic we want to convert it to nanos at the same time, so 1/f/1e-9 = 1e9/f = T_nanos. To avoid integer division we want the largest possible numerator and divisor as this avoids integer error and gives a more acccurate calculation. You can then multiple T_nanos by the number of ticks to get a result.

For example: if the current value_ticks = 8440832771 (5 minutes 51.7014 seconds or 351701365458)

Your method:

  • value_whole_seconds = floor(8440832771/ 24000000) = floor(351.7) = 351
  • value_subsecond_ticks = 8440832771 % 24000000) = 16832771
  • value_subsecond_ns = floor((16832771*1e9) / 24000000) = floor(701365458.3) = 701365458
  • value_ns = 351 * 1e9 + 16832771 = 351001682771ns -> error of 699682687ns (700ms)!

Standard method (combine multiplying ticks with NANOS_IN_S and divide):

    ns =  (ticks * 1e9)/f_clk;

so, substituting this for the p550:

  • tick_nano_inv = (8440832771 * 1e9) = 8440832771000000000
  • ns = tick_nano_inv / f_clk = 8440832771000000000 / 24000000 = 351701365458ns
    -> error of 0ns!

note that C always rounds towards zero, hence all these positive valued divisions are floored. I know this method is in a bunch of our timer drivers but we should get rid of it everywhere as the standard method is more compact, more efficient and more accurate.

Copy link
Contributor

@KurtWu10 KurtWu10 Nov 13, 2025

Choose a reason for hiding this comment

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

It may be advantageous to perform manual arithmetic simplification here, from ns = (ticks * 1e9) / f_clk to ns = ticks * 125 / 3 (based on ${1e9} / {\mathsf{f_clk}}$ = ${125} / {3}$).

The compiler is not able to perform such simplification due to modular arithmetic (godbolt), and doing so results in both a smaller application binary and faster execution.

An even better sequence might be ns = ticks * 41 + ticks * 2 / 3.

Choose a reason for hiding this comment

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

When implemented in the rk3568 timer it overflows after 758 seconds and we get bogus timeout data later.

Copy link
Contributor

Choose a reason for hiding this comment

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

A simpler and more explicit alternative is ns = ticks * (1e9 / 24000000) + ticks * (1e9 % 24000000) / 2400000.

Copy link
Member

Choose a reason for hiding this comment

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

@JDuchniewicz you should explicitly split the constant part where you divide nano_inv / f_clk and the multiply after. I've messed around with this a bit and it seems that the compiler indeed can do weird things with this. We should write a single implementation of these tick and timer handling functions to share probably... Doesn't make sense to have a separate one for every device! I'm busy writing something up now.

Choose a reason for hiding this comment

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

Yeah already fixed that and now the timer works fine with the sddf example.

Copy link
Member

@omeh-a omeh-a Nov 19, 2025

Choose a reason for hiding this comment

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

It may be advantageous to perform manual arithmetic simplification here, from ns = (ticks * 1e9) / f_clk to ns = ticks * 125 / 3 (based on 1 e 9 / f clk = 125 / 3 ).

The compiler is not able to perform such simplification due to modular arithmetic (godbolt), and doing so results in both a smaller application binary and faster execution.

An even better sequence might be ns = ticks * 41 + ticks * 2 / 3.

Thinking about this more, we probably need to descend to picosecond resolution if we are representing periods with integers. 1GHz^-1 = 1ns, so we are guaranteed to lose all distinctions above that (very common and relatively low) clock frequency.


timeout_timer_elapses = 0;

uint64_t ticks_whole_seconds = (ns / NS_IN_S) * TIMER_TICKS_PER_SECOND;
Copy link
Member

Choose a reason for hiding this comment

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

Same issue as previous note. You can just flip the math upside down.

assert(device_resources.num_regions == 1);

counter_timer_regs = device_resources.regions[0].region.vaddr;
timeout_timer_regs = device_resources.regions[0].region.vaddr + 0x14;
Copy link
Member

Choose a reason for hiding this comment

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

Magic number (0x14)

counter_timer_regs->ctrl &= 0xfffffffd;
counter_timer_regs->load_count = TIMER_MAX_TICKS;
// 3. Enable timer
counter_timer_regs->ctrl |= (1 << 0);
Copy link
Member

Choose a reason for hiding this comment

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

May be a good idea to #define some of these

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.

5 participants