Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rp2040: rtc support #3398

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft

rp2040: rtc support #3398

wants to merge 3 commits into from

Conversation

ysoldak
Copy link
Contributor

@ysoldak ysoldak commented Jan 19, 2023

Real Time Clock on RP2040.

Probably, not very useful by itself, but the ability to fire a scheduled interrupt enables us to support RTC-governed Deep Sleep and Dormant states later.


// Enable the IRQ at the proc
interrupt.New(rp.IRQ_RTC_IRQ, rtcHandleInterrupt).Enable()
irqSet(rp.IRQ_RTC_IRQ, true)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this really needed?
I see this pattern of interrupt.New().Enable() followed by irqSet() across rp2040 related code.
Yet, it works w/o irqSet() call too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aykevl what would be your understanding?
If this irqSet is indeed not needed in such cases, I could make a PR that cleans this in RP2040 machine.

rtc.clkDivM1.Set(rtcFreq)
}

func (rtc *rtcType) SetTime(t RtcTime) error {
Copy link
Member

Choose a reason for hiding this comment

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

Should this accept time.Time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd love to. Unfortunately, that makes a cycle:

package github.com/tinygo-org/tinygo/src/examples/rtc
        imports machine
        imports time
        imports runtime
        imports machine: import cycle not allowed

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd go for a SetDate function that follows the stdlib format of creating a new date (excluding the loc).
This would probably make it more natural to use than with a machine specific type.

Copy link
Contributor

Choose a reason for hiding this comment

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

Check this out, it might be a way of solving this cyclical dependency problem given that we all agree it's time the machine package had it's own Time type.

machine/time.go

type Time struct {
 // Same fields as time.Time
}

func (rtc *RTC) SetTime[T ~Time](t T) error {
   // do the setting.
}

time/time.go

// replace contents of time.Time type.
type Time machine.Time

I have no idea of the far reaching consequences of this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've actually made an alternative PR #3403 that sets RTC interrupt with a delay instead of actual time. Probably, that's better than such tricks.


// ---

type RtcTime struct {
Copy link
Member

Choose a reason for hiding this comment

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

Should this only be used for setting alarms? For get/set time, use time.Time instead?

Also, how generic is this - should it be in a top-level rtc.go, or is it rp2040 specific?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't use time.Time, unfortunately. See above.

This implementation is RP2040 specific.
We don't have any API for RTC as of yet (nRF family uses it, but does not expose, probably others too).
So the idea was we can try and make it for RP2040 first and generalise once we have at least two families exposing RTC?

Copy link
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.

I... really don't like that chip vendors think they can implement the Gregorian calendar in hardware. Because they can't:

  1. It does not deal with timezones or daylight saving time at all.
  2. As admitted in the datasheet, it doesn't correctly deal with the leap century. Admittedly that's rather rare (we're not due for another 77 years) but it shows that this kind of thing is difficult.
  3. It does not verify the day of the week.
  4. This is really something that only the UI should do. Most time protocols (like NTP) only deal with Unix time, which is a plain and simple number. It only adds complexity and bugs between systems that deal with Unix time and systems that attempt to use the Gregorian calendar.

Anyway, that's my view on this and kind of besides the point of this PR.

But regarding this specific PR: it implements a new API for the machine package which I don't want to add without getting it right. So I suggest making a PR to tinygo-site, see tinygo-org/tinygo-site#317 for example (content/docs/reference/machine.md is the important part, the rest is optional). That way, it's easier to see what it is doing. And we'd need to document the API anyway so better do it right from the start.

@ysoldak
Copy link
Contributor Author

ysoldak commented Jan 19, 2023

Example output

SYS: 2006-01-02T15:04:08Z, RTC: 2006-01-02T15:04:09Z, DIFF: 999.148ms
SYS: 2006-01-02T15:04:09Z, RTC: 2006-01-02T15:04:10Z, DIFF: 997.986ms
SYS: 2006-01-02T15:04:10Z, RTC: 2006-01-02T15:04:11Z, DIFF: 997.155ms
SYS: 2006-01-02T15:04:11Z, RTC: 2006-01-02T15:04:12Z, DIFF: 996.361ms
SYS: 2006-01-02T15:04:12Z, RTC: 2006-01-02T15:04:13Z, DIFF: 995.58ms
SYS: 2006-01-02T15:04:13Z, RTC: 2006-01-02T15:04:14Z, DIFF: 994.825ms
SYS: 2006-01-02T15:04:14Z, RTC: 2006-01-02T15:04:15Z, DIFF: 994.061ms
SYS: 2006-01-02T15:04:15Z, RTC: 2006-01-02T15:04:16Z, DIFF: 993.29ms
SYS: 2006-01-02T15:04:16Z, RTC: 2006-01-02T15:04:17Z, DIFF: 992.508ms
SYS: 2006-01-02T15:04:17Z, RTC: 2006-01-02T15:04:18Z, DIFF: 991.733ms
SYS: 2006-01-02T15:04:18Z, RTC: 2006-01-02T15:04:19Z, DIFF: 990.949ms
Pekabo!
SYS: 2006-01-02T15:04:19Z, RTC: 2006-01-02T15:04:20Z, DIFF: 990.173ms
SYS: 2006-01-02T15:04:20Z, RTC: 2006-01-02T15:04:21Z, DIFF: 989.389ms
SYS: 2006-01-02T15:04:21Z, RTC: 2006-01-02T15:04:22Z, DIFF: 988.624ms
SYS: 2006-01-02T15:04:22Z, RTC: 2006-01-02T15:04:23Z, DIFF: 987.844ms

Interesting to see the diff decrease. (approx 800us per second.)
This means RTC ticks faster than the system clock.
Which of them is correct is hard to tell though.

This is all due to the fact RTC returns seconds precision and the loop iteration takes slightly more than a second (1 second and ~800µs to be precise).

Both clocks have exactly same value internally, they run from the same source (XOSC).

The accumulated error is immediately visible in the value of the system clock (µs precision) and is not visible until it adds up to more than a second in RTC case.

The moment error accumulates to a whole second, the output jumps two seconds for both SYS and RTC.

Confirmed by letting the (slightly modified) example run for 20 minutes:

...
SYS: 2006-01-02T15:23:33Z, RTC: 2006-01-02T15:23:33Z, DIFF: -998.923ms, DIFF':846µs
SYS: 2006-01-02T15:23:34Z, RTC: 2006-01-02T15:23:34Z, DIFF: -999.788ms, DIFF':865µs
SYS: 2006-01-02T15:23:36Z, RTC: 2006-01-02T15:23:35Z, DIFF: -1.000628s, DIFF':840µs
SYS: 2006-01-02T15:23:37Z, RTC: 2006-01-02T15:23:37Z, DIFF: -1.507ms, DIFF':-999.121ms
SYS: 2006-01-02T15:23:38Z, RTC: 2006-01-02T15:23:38Z, DIFF: -2.366ms, DIFF':859µs
SYS: 2006-01-02T15:23:39Z, RTC: 2006-01-02T15:23:39Z, DIFF: -3.187ms, DIFF':821µs
...

I shall probably modify the example and remove the confusing DIFF output.

@ysoldak ysoldak marked this pull request as draft January 20, 2023 02:01
@soypat
Copy link
Contributor

soypat commented Feb 14, 2023

If possible I would not expose a RtcTime type. That should be a time.Time. I've suggested having a machine.Time type that basically implements the whole functionality of time.Time, to then be imported by time package.

If we do not create a machine.Time type I believe we should probably give the RTC type similar methods to time.Time, such as Date (copy Date signature) and Clock (copies Clock signature) and Nanosecond (copies Nanosecond signature).

Same for SetAlarm, which could be SetAlarmDate and take same parameters as time.Date if we choose to drop the RtcTime type.

Last Point: If you we do choose to keep RtcTime I'd encourage it be renamed to be RTCTime, in line with Go convention of acronyms according to Code Review Comments.

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.

4 participants