Skip to content

Interrupt based time for AVR. #2428

Merged
deadprogram merged 1 commit intotinygo-org:devfrom
zdima:avr-timer
Dec 30, 2021
Merged

Interrupt based time for AVR. #2428
deadprogram merged 1 commit intotinygo-org:devfrom
zdima:avr-timer

Conversation

@zdima
Copy link
Copy Markdown
Contributor

@zdima zdima commented Dec 24, 2021

Timer-0 is used as interrupt source.
Keep all time in nanoseconds to simplify the math and increase performance.
Adjust tick cost when timer-0 is reconfigured by PWM (the time precision affected when timer-0 reconfigured).

@cweagans
Copy link
Copy Markdown
Contributor

This PR helps a lot. See test results in related issue:

#2423 (comment)

Comment on lines +79 to +86
// recalibrate if we have some time (>100ms) and it was a while when we did it last time.
if d > 100000 {
now := waitTill - d
if nextTimerRecalibrate < now {
nextTimerRecalibrate = now + timerRecalibrateInterval
machine.AdjustMonotonicTimer()
}
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@aykevl, you had added TODO comment some time back, and I have added code to recalibrate timer with interval 1 minute when sleep is called for 100ms or more. Do you think one minute interval is too short?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I doubt we need to recalibrate more often. Pretty sure one minute is sufficient.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But AdjustMonotonicTimer doesn't calibrate anything? Therefore calling it every minute is totally unnecessary.

In my comment I was referring to calibrating the WDT based on the timer. The timer is already accurate (or as accurate as it can get).

@zdima zdima marked this pull request as ready for review December 25, 2021 20:38
@deadprogram
Copy link
Copy Markdown
Member

This looks like a really great contribution once we can get it merged. Solves a longstanding issue!

I left some minor feedback. Also perhaps you want to squash a couple of the commits?

…the time precision affected when timer-0 reconfigured). Keep all time in nanoseconds.

Interrupt based time. Adjust tick cost every 1 minute and when timer-0 is reconfigured (the time precision affected when timer-0 reconfigured). Keep all time in nanoseconds.
@zdima
Copy link
Copy Markdown
Contributor Author

zdima commented Dec 27, 2021

fixed comments and commits are squash into one.

@deadprogram
Copy link
Copy Markdown
Member

@aykevl or anyone else have any further feedback before we merge?

@deadprogram
Copy link
Copy Markdown
Member

Thank you for all your work on this contribution @zdima now merging.

@deadprogram deadprogram merged commit 92150bd into tinygo-org:dev Dec 30, 2021
Copy link
Copy Markdown
Member

@aykevl aykevl left a comment

Choose a reason for hiding this comment

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

Normally all time related code lives in the runtime. It looks like the only reason it is in the machine package is so that the machine package can call AdjustMonotonicTimer. Perhaps it can be moved to the runtime package and called via a special //go:linkname? That would avoid exposing lots of internal functions in the machine package.

Also, this PR is probably going to increase power consumption and possibly reduce performance because of the constant interrupts. That may be a reasonable tradeoff, but I'm mentioning it here just in case.

Comment on lines +79 to +86
// recalibrate if we have some time (>100ms) and it was a while when we did it last time.
if d > 100000 {
now := waitTill - d
if nextTimerRecalibrate < now {
nextTimerRecalibrate = now + timerRecalibrateInterval
machine.AdjustMonotonicTimer()
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But AdjustMonotonicTimer doesn't calibrate anything? Therefore calling it every minute is totally unnecessary.

In my comment I was referring to calibrating the WDT based on the timer. The timer is already accurate (or as accurate as it can get).

@zdima
Copy link
Copy Markdown
Contributor Author

zdima commented Dec 30, 2021

But AdjustMonotonicTimer doesn't calibrate anything? Therefore calling it every minute is totally unnecessary.

The "calibrate" in this case means adjust the increment value for each interrupt, which depends on timer0 settings.
There is code to address time0 changes when made by PWM. But user can change the time0 settings behind out back and this will address this scenario.

this PR is probably going to increase power consumption and possibly reduce performance because of the constant interrupts.

Agree. User can address this by changing timer0 settings to reduce interrupt intervals. This will reduce power consumption (also may affect accuracy of timer, which is another tradeoff).

@zdima
Copy link
Copy Markdown
Contributor Author

zdima commented Dec 30, 2021

called via a special //go:linkname

Yes, that would be better. I didn't want to expose those internal functions and that would save me some time :(
Should I create new PR with change to use //go:linkname?

@aykevl
Copy link
Copy Markdown
Member

aykevl commented Jan 3, 2022

Looks like this PR broke -opt=1 on AVR: #2478

There is code to address time0 changes when made by PWM. But user can change the time0 settings behind out back and this will address this scenario.

Yes, but this is true for many other things. If users use hardware that is used by the runtime (such as Timer0), they should not expect the runtime to magically deal with that. Therefore, this is in my opinion unnecessary.

Agree. User can address this by changing timer0 settings to reduce interrupt intervals. This will reduce power consumption (also may affect accuracy of timer, which is another tradeoff).

A timer will always increase power consumption, even at low frequency. This varies by chip of course. Other chips (such as nrf) have dedicated low-power clocks for this purpose running from a separate crystal.

Anyway, just wanted to mentioned this to note as a side effect.

@zdima zdima deleted the avr-timer branch January 29, 2022 13:51
aykevl added a commit that referenced this pull request Apr 20, 2022
Simplify the interrupt-based timer code in a few ways:

  - Do not recalibrate the timer every 100ms. Instead, rely on the fact
    that the machine package will calbrate the timer if necessary if it
    makes changes to Timer0.
  - Do not configure Timer0 and then set nanosecondsInTick based on that
    value. Instead, use a fixed value.

These two changes together mean that in code that doesn't use PWM,
nanosecondsInTick will be constant which makes the TIMER0_OVF interrupt
handler a lot smaller.

Together this reduces the code size of AVR binaries by about 1200 bytes,
making it pretty close to the pre-timer code size (only about 250 bytes
larger).

It also somehow fixes a problem with
tinygo.org/x/drivers/examples/ws2812 on the Arduino Uno. I'm not quite
sure what was going wrong, but bisecting pointed towards the timer code
(#2428) and with this
simplification the bug appears to be gone.
deadprogram pushed a commit that referenced this pull request May 25, 2022
Simplify the interrupt-based timer code in a few ways:

  - Do not recalibrate the timer every 100ms. Instead, rely on the fact
    that the machine package will calbrate the timer if necessary if it
    makes changes to Timer0.
  - Do not configure Timer0 and then set nanosecondsInTick based on that
    value. Instead, use a fixed value.

These two changes together mean that in code that doesn't use PWM,
nanosecondsInTick will be constant which makes the TIMER0_OVF interrupt
handler a lot smaller.

Together this reduces the code size of AVR binaries by about 1200 bytes,
making it pretty close to the pre-timer code size (only about 250 bytes
larger).

It also somehow fixes a problem with
tinygo.org/x/drivers/examples/ws2812 on the Arduino Uno. I'm not quite
sure what was going wrong, but bisecting pointed towards the timer code
(#2428) and with this
simplification the bug appears to be gone.
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