Skip to content

Commit 3c55689

Browse files
aykevldeadprogram
authored andcommitted
runtime: refactor time handling
This commit refactors both determining the current time and sleeping for a given time. It also improves precision for many chips. * The nrf chips had a long-standing TODO comment about a slightly inaccurate clock. This should now be fixed. * The SAM D2x/D5x chips may have a slightly more accurate clock, although probably within the error margin of the RTC. Also, by working with RTC ticks and converting in the least number of places, code size is often slightly reduced (usually just a few bytes, up to around 1kB in some cases). * I believe the HiFive1 rev B timer was slightly wrong (32768Hz vs 30517.6Hz). Because the datasheet says the clock runs at 32768Hz, I've used the same conversion code here as in the nrf and sam cases. * I couldn't test both stm32 timers, so I kept them as they currently are. It may be possible to make them more efficient by using the native tick frequency instead of using microseconds everywhere.
1 parent 95f509b commit 3c55689

17 files changed

+168
-50
lines changed

src/runtime/runtime.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func memequal(x, y unsafe.Pointer, n uintptr) bool {
6161
}
6262

6363
func nanotime() int64 {
64-
return int64(ticks()) * tickMicros
64+
return ticksToNanoseconds(ticks())
6565
}
6666

6767
// timeOffset is how long the monotonic clock started after the Unix epoch. It

src/runtime/runtime_arm7tdmi.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import (
99

1010
type timeUnit int64
1111

12-
const tickMicros = 1
13-
1412
func putchar(c byte) {
1513
// dummy, TODO
1614
}
@@ -60,6 +58,14 @@ func preinit() {
6058
}
6159
}
6260

61+
func ticksToNanoseconds(ticks timeUnit) int64 {
62+
return int64(ticks)
63+
}
64+
65+
func nanosecondsToTicks(ns int64) timeUnit {
66+
return timeUnit(ns)
67+
}
68+
6369
func ticks() timeUnit {
6470
// TODO
6571
return 0

src/runtime/runtime_atsamd21.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,6 @@ func waitForSync() {
231231
}
232232
}
233233

234-
// treat all ticks params coming from runtime as being in microseconds
235-
const tickMicros = 1000
236-
237234
var (
238235
timestamp timeUnit // ticks since boottime
239236
timerLastCounter uint64
@@ -243,6 +240,22 @@ var timerWakeup volatile.Register8
243240

244241
const asyncScheduler = false
245242

243+
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
244+
func ticksToNanoseconds(ticks timeUnit) int64 {
245+
// The following calculation is actually the following, but with both sides
246+
// reduced to reduce the risk of overflow:
247+
// ticks * 1e9 / 32768
248+
return int64(ticks) * 1953125 / 64
249+
}
250+
251+
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
252+
func nanosecondsToTicks(ns int64) timeUnit {
253+
// The following calculation is actually the following, but with both sides
254+
// reduced to reduce the risk of overflow:
255+
// ns * 32768 / 1e9
256+
return timeUnit(ns * 64 / 1953125)
257+
}
258+
246259
// sleepTicks should sleep for d number of microseconds.
247260
func sleepTicks(d timeUnit) {
248261
for d != 0 {
@@ -259,22 +272,22 @@ func ticks() timeUnit {
259272
sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ)
260273
waitForSync()
261274

262-
rtcCounter := (uint64(sam.RTC_MODE0.COUNT.Get()) * 305) / 10 // each counter tick == 30.5us
263-
offset := (rtcCounter - timerLastCounter) // change since last measurement
275+
rtcCounter := uint64(sam.RTC_MODE0.COUNT.Get()) // each counter tick == 30.5us
276+
offset := (rtcCounter - timerLastCounter) // change since last measurement
264277
timerLastCounter = rtcCounter
265-
timestamp += timeUnit(offset) // TODO: not precise
278+
timestamp += timeUnit(offset)
266279
return timestamp
267280
}
268281

269282
// ticks are in microseconds
270283
func timerSleep(ticks uint32) {
271284
timerWakeup.Set(0)
272-
if ticks < 214 {
273-
// due to around 183us delay waiting for the register value to sync, the minimum sleep value
274-
// for the SAMD21 is 214us.
285+
if ticks < 7 {
286+
// Due to around 6 clock ticks delay waiting for the register value to
287+
// sync, the minimum sleep value for the SAMD21 is 214us.
275288
// For related info, see:
276289
// https://community.atmel.com/comment/2507091#comment-2507091
277-
ticks = 214
290+
ticks = 7
278291
}
279292

280293
// request read of count
@@ -283,7 +296,7 @@ func timerSleep(ticks uint32) {
283296

284297
// set compare value
285298
cnt := sam.RTC_MODE0.COUNT.Get()
286-
sam.RTC_MODE0.COMP0.Set(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
299+
sam.RTC_MODE0.COMP0.Set(uint32(cnt) + ticks)
287300
waitForSync()
288301

289302
// enable IRQ for CMP0 compare

src/runtime/runtime_atsamd51.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,6 @@ func waitForSync() {
219219
}
220220
}
221221

222-
// treat all ticks params coming from runtime as being in microseconds
223-
const tickMicros = 1000
224-
225222
var (
226223
timestamp timeUnit // ticks since boottime
227224
timerLastCounter uint64
@@ -231,6 +228,22 @@ var timerWakeup volatile.Register8
231228

232229
const asyncScheduler = false
233230

231+
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
232+
func ticksToNanoseconds(ticks timeUnit) int64 {
233+
// The following calculation is actually the following, but with both sides
234+
// reduced to reduce the risk of overflow:
235+
// ticks * 1e9 / 32768
236+
return int64(ticks) * 1953125 / 64
237+
}
238+
239+
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
240+
func nanosecondsToTicks(ns int64) timeUnit {
241+
// The following calculation is actually the following, but with both sides
242+
// reduced to reduce the risk of overflow:
243+
// ns * 32768 / 1e9
244+
return timeUnit(ns * 64 / 1953125)
245+
}
246+
234247
// sleepTicks should sleep for d number of microseconds.
235248
func sleepTicks(d timeUnit) {
236249
for d != 0 {
@@ -245,22 +258,22 @@ func sleepTicks(d timeUnit) {
245258
func ticks() timeUnit {
246259
waitForSync()
247260

248-
rtcCounter := (uint64(sam.RTC_MODE0.COUNT.Get()) * 305) / 10 // each counter tick == 30.5us
249-
offset := (rtcCounter - timerLastCounter) // change since last measurement
261+
rtcCounter := uint64(sam.RTC_MODE0.COUNT.Get())
262+
offset := (rtcCounter - timerLastCounter) // change since last measurement
250263
timerLastCounter = rtcCounter
251-
timestamp += timeUnit(offset) // TODO: not precise
264+
timestamp += timeUnit(offset)
252265
return timestamp
253266
}
254267

255268
// ticks are in microseconds
256269
func timerSleep(ticks uint32) {
257270
timerWakeup.Set(0)
258-
if ticks < 260 {
271+
if ticks < 8 {
259272
// due to delay waiting for the register value to sync, the minimum sleep value
260273
// for the SAMD51 is 260us.
261274
// For related info for SAMD21, see:
262275
// https://community.atmel.com/comment/2507091#comment-2507091
263-
ticks = 260
276+
ticks = 8
264277
}
265278

266279
// request read of count
@@ -269,7 +282,7 @@ func timerSleep(ticks uint32) {
269282
// set compare value
270283
cnt := sam.RTC_MODE0.COUNT.Get()
271284

272-
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
285+
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + ticks)
273286

274287
// enable IRQ for CMP0 compare
275288
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)

src/runtime/runtime_avr.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ type timeUnit uint32
1414

1515
var currentTime timeUnit
1616

17-
const tickMicros = 1024 * 16384
18-
1917
// Watchdog timer periods. These can be off by a large margin (hence the jump
2018
// between 64ms and 125ms which is not an exact double), so don't rely on this
2119
// for accurate time keeping.
@@ -71,6 +69,16 @@ func putchar(c byte) {
7169

7270
const asyncScheduler = false
7371

72+
const tickNanos = 1024 * 16384 // roughly 16ms in nanoseconds
73+
74+
func ticksToNanoseconds(ticks timeUnit) int64 {
75+
return int64(ticks) * tickNanos
76+
}
77+
78+
func nanosecondsToTicks(ns int64) timeUnit {
79+
return timeUnit(ns / tickNanos)
80+
}
81+
7482
// Sleep this number of ticks of 16ms.
7583
//
7684
// TODO: not very accurate. Improve accuracy by calibrating on startup and every

src/runtime/runtime_cortexm_qemu.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import (
1313

1414
type timeUnit int64
1515

16-
const tickMicros = 1
17-
1816
var timestamp timeUnit
1917

2018
func postinit() {}
@@ -29,6 +27,14 @@ func main() {
2927

3028
const asyncScheduler = false
3129

30+
func ticksToNanoseconds(ticks timeUnit) int64 {
31+
return int64(ticks)
32+
}
33+
34+
func nanosecondsToTicks(ns int64) timeUnit {
35+
return timeUnit(ns)
36+
}
37+
3238
func sleepTicks(d timeUnit) {
3339
// TODO: actually sleep here for the given time.
3440
timestamp += d

src/runtime/runtime_fe310_baremetal.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,21 @@ import (
66
"device/riscv"
77
)
88

9-
const tickMicros = 32768 // RTC clock runs at 32.768kHz
9+
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
10+
func ticksToNanoseconds(ticks timeUnit) int64 {
11+
// The following calculation is actually the following, but with both sides
12+
// reduced to reduce the risk of overflow:
13+
// ticks * 1e9 / 32768
14+
return int64(ticks) * 1953125 / 64
15+
}
16+
17+
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
18+
func nanosecondsToTicks(ns int64) timeUnit {
19+
// The following calculation is actually the following, but with both sides
20+
// reduced to reduce the risk of overflow:
21+
// ns * 32768 / 1e9
22+
return timeUnit(ns * 64 / 1953125)
23+
}
1024

1125
func abort() {
1226
// lock up forever

src/runtime/runtime_fe310_qemu.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ import (
77
"unsafe"
88
)
99

10-
const tickMicros = 100 // CLINT.MTIME increments every 100ns
11-
1210
// Special memory-mapped device to exit tests, created by SiFive.
1311
var testExit = (*volatile.Register32)(unsafe.Pointer(uintptr(0x100000)))
1412

15-
var timestamp timeUnit
13+
// ticksToNanoseconds converts CLINT ticks (at 100ns per tick) to nanoseconds.
14+
func ticksToNanoseconds(ticks timeUnit) int64 {
15+
return int64(ticks) * 100
16+
}
17+
18+
// nanosecondsToTicks converts nanoseconds to CLINT ticks (at 100ns per tick).
19+
func nanosecondsToTicks(ns int64) timeUnit {
20+
return timeUnit(ns / 100)
21+
}
1622

1723
func abort() {
1824
// Signal a successful exit.

src/runtime/runtime_nrf.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import (
1212

1313
type timeUnit int64
1414

15-
const tickMicros = 1024 * 32
16-
1715
//go:linkname systemInit SystemInit
1816
func systemInit()
1917

@@ -64,7 +62,7 @@ func sleepTicks(d timeUnit) {
6462
for d != 0 {
6563
ticks() // update timestamp
6664
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
67-
rtc_sleep(ticks) // TODO: not accurate (must be d / 30.5175...)
65+
rtc_sleep(ticks)
6866
d -= timeUnit(ticks)
6967
}
7068
}
@@ -74,6 +72,22 @@ var (
7472
rtcLastCounter uint32 // 24 bits ticks
7573
)
7674

75+
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
76+
func ticksToNanoseconds(ticks timeUnit) int64 {
77+
// The following calculation is actually the following, but with both sides
78+
// reduced to reduce the risk of overflow:
79+
// ticks * 1e9 / 32768
80+
return int64(ticks) * 1953125 / 64
81+
}
82+
83+
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
84+
func nanosecondsToTicks(ns int64) timeUnit {
85+
// The following calculation is actually the following, but with both sides
86+
// reduced to reduce the risk of overflow:
87+
// ns * 32768 / 1e9
88+
return timeUnit(ns * 64 / 1953125)
89+
}
90+
7791
// Monotonically increasing numer of ticks since start.
7892
//
7993
// Note: very long pauses between measurements (more than 8 minutes) may
@@ -83,7 +97,7 @@ func ticks() timeUnit {
8397
rtcCounter := uint32(nrf.RTC1.COUNTER.Get())
8498
offset := (rtcCounter - rtcLastCounter) & 0xffffff // change since last measurement
8599
rtcLastCounter = rtcCounter
86-
timestamp += timeUnit(offset) // TODO: not precise
100+
timestamp += timeUnit(offset)
87101
return timestamp
88102
}
89103

src/runtime/runtime_stm32f103xx.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ func initCLK() {
5353
}
5454
}
5555

56-
const tickMicros = 1000
57-
5856
var (
5957
timestamp timeUnit // microseconds since boottime
6058
timerLastCounter uint64
@@ -109,6 +107,14 @@ func initTIM() {
109107

110108
const asyncScheduler = false
111109

110+
func ticksToNanoseconds(ticks timeUnit) int64 {
111+
return int64(ticks) * 1000
112+
}
113+
114+
func nanosecondsToTicks(ns int64) timeUnit {
115+
return timeUnit(ns / 1000)
116+
}
117+
112118
// sleepTicks should sleep for specific number of microseconds.
113119
func sleepTicks(d timeUnit) {
114120
for d != 0 {

0 commit comments

Comments
 (0)