time.Sleep now compiles on all systems, so lets use that.
Additionally, do a few improvements in time unit handling for the
scheduler. This should lead to somewhat longer sleep durations without
wrapping (on some platforms).

Some examples got smaller, some got bigger. In particular, code using
the scheduler got bigger and the blinky1 example got smaller (especially
on Arduino: 380 -> 314 bytes).
Этот коммит содержится в:
Ayke van Laethem 2018-09-15 01:08:31 +02:00
родитель 1ac67cf8de
коммит 2a20c0c7f0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
12 изменённых файлов: 75 добавлений и 72 удалений

Просмотреть файл

@ -19,17 +19,20 @@ run on even lower level micros.
Example program (blinky): Example program (blinky):
```go ```go
import "machine" import (
"machine"
"time"
)
func main() { func main() {
led := machine.GPIO{machine.LED} led := machine.GPIO{machine.LED}
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT}) led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
for { for {
led.Low() led.Low()
runtime.Sleep(runtime.Millisecond * 1000) time.Sleep(time.Millisecond * 1000)
led.High() led.High()
runtime.Sleep(runtime.Millisecond * 1000) time.Sleep(time.Millisecond * 1000)
} }
} }
``` ```

Просмотреть файл

@ -1736,7 +1736,7 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, con
params = append(params, context) params = append(params, context)
} }
if frame.blocking && llvmFn.Name() == "runtime.Sleep" { if frame.blocking && llvmFn.Name() == "time.Sleep" {
// Set task state to TASK_STATE_SLEEP and set the duration. // Set task state to TASK_STATE_SLEEP and set the duration.
c.builder.CreateCall(c.mod.NamedFunction("runtime.sleepTask"), []llvm.Value{frame.taskHandle, params[0]}, "") c.builder.CreateCall(c.mod.NamedFunction("runtime.sleepTask"), []llvm.Value{frame.taskHandle, params[0]}, "")

Просмотреть файл

@ -99,7 +99,7 @@ func (p *Program) AnalyseCallgraph() {
if child.CName() != "" { if child.CName() != "" {
continue // assume non-blocking continue // assume non-blocking
} }
if child.LinkName() == "runtime.Sleep" { if child.fn.RelString(nil) == "time.Sleep" {
f.blocking = true f.blocking = true
} }
f.children = append(f.children, child) f.children = append(f.children, child)

Просмотреть файл

@ -4,7 +4,7 @@ package main
import ( import (
"machine" "machine"
"runtime" "time"
) )
func main() { func main() {
@ -12,9 +12,9 @@ func main() {
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT}) led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
for { for {
led.Low() led.Low()
runtime.Sleep(runtime.Millisecond * 500) time.Sleep(time.Millisecond * 500)
led.High() led.High()
runtime.Sleep(runtime.Millisecond * 500) time.Sleep(time.Millisecond * 500)
} }
} }

Просмотреть файл

@ -7,7 +7,7 @@ package main
import ( import (
"machine" "machine"
"runtime" "time"
) )
func main() { func main() {
@ -21,11 +21,11 @@ func led1() {
for { for {
println("+") println("+")
led.Low() led.Low()
runtime.Sleep(runtime.Millisecond * 1000) time.Sleep(time.Millisecond * 1000)
println("-") println("-")
led.High() led.High()
runtime.Sleep(runtime.Millisecond * 1000) time.Sleep(time.Millisecond * 1000)
} }
} }
@ -35,10 +35,10 @@ func led2() {
for { for {
println(" +") println(" +")
led.Low() led.Low()
runtime.Sleep(runtime.Millisecond * 420) time.Sleep(time.Millisecond * 420)
println(" -") println(" -")
led.High() led.High()
runtime.Sleep(runtime.Millisecond * 420) time.Sleep(time.Millisecond * 420)
} }
} }

Просмотреть файл

@ -2,7 +2,7 @@ package main
import ( import (
"machine" "machine"
"runtime" "time"
) )
// This example assumes that the button is connected to pin 8. Change the value // This example assumes that the button is connected to pin 8. Change the value
@ -23,6 +23,6 @@ func main() {
led.High() led.High()
} }
runtime.Sleep(runtime.Millisecond * 10) time.Sleep(time.Millisecond * 10)
} }
} }

Просмотреть файл

@ -39,13 +39,6 @@ func main() int {
} }
} }
func Sleep(d Duration) {
// This function is treated specially by the compiler: when goroutines are
// used, it is transformed into a llvm.coro.suspend() call.
// When goroutines are not used this function behaves as normal.
sleep(d)
}
func GOMAXPROCS(n int) int { func GOMAXPROCS(n int) int {
// Note: setting GOMAXPROCS is ignored. // Note: setting GOMAXPROCS is ignored.
return 1 return 1

Просмотреть файл

@ -8,13 +8,11 @@ import (
const BOARD = "arduino" const BOARD = "arduino"
const Microsecond = 1 type timeUnit uint32
var currentTime uint64 var currentTime timeUnit
func init() { const tickMicros = 1024 * 16384
currentTime = 0
}
// Watchdog timer periods. These can be off by a large margin (hence the jump // Watchdog timer periods. These can be off by a large margin (hence the jump
// between 64ms and 125ms which is not an exact double), so don't rely on this // between 64ms and 125ms which is not an exact double), so don't rely on this
@ -53,9 +51,13 @@ func putchar(c byte) {
// //
// TODO: not very accurate. Improve accuracy by calibrating on startup and every // TODO: not very accurate. Improve accuracy by calibrating on startup and every
// once in a while. // once in a while.
func sleep(d Duration) { //go:linkname sleep time.Sleep
currentTime += uint64(d) func sleep(d int64) {
d /= 16384 // about 16ms sleepTicks(timeUnit(d / tickMicros))
}
func sleepTicks(d timeUnit) {
currentTime += d
for d != 0 { for d != 0 {
sleepWDT(WDT_PERIOD_16MS) sleepWDT(WDT_PERIOD_16MS)
d -= 1 d -= 1
@ -90,7 +92,7 @@ func sleepWDT(period uint8) {
*avr.SMCR = 0 *avr.SMCR = 0
} }
func monotime() uint64 { func ticks() timeUnit {
return currentTime return currentTime
} }

Просмотреть файл

@ -7,7 +7,9 @@ import (
"device/nrf" "device/nrf"
) )
const Microsecond = 1 type timeUnit int64
const tickMicros = 1024 * 32
//go:export _start //go:export _start
func _start() { func _start() {
@ -48,31 +50,35 @@ func putchar(c byte) {
nrf.UART0.EVENTS_TXDRDY = 0 nrf.UART0.EVENTS_TXDRDY = 0
} }
func sleep(d Duration) { //go:linkname sleep time.Sleep
ticks64 := d / 32 func sleep(d timeUnit) {
for ticks64 != 0 { sleepTicks(d / tickMicros)
monotime() // update timestamp }
ticks := uint32(ticks64) & 0x7fffff // 23 bits (to be on the safe side)
func sleepTicks(d timeUnit) {
for d != 0 {
ticks() // update timestamp
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
rtc_sleep(ticks) // TODO: not accurate (must be d / 30.5175...) rtc_sleep(ticks) // TODO: not accurate (must be d / 30.5175...)
ticks64 -= Duration(ticks) d -= timeUnit(ticks)
} }
} }
var ( var (
timestamp uint64 // microseconds since boottime timestamp timeUnit // nanoseconds since boottime
rtcLastCounter uint32 // 24 bits ticks rtcLastCounter uint32 // 24 bits ticks
) )
// Monotonically increasing numer of microseconds since start. // Monotonically increasing numer of ticks since start.
// //
// Note: very long pauses between measurements (more than 8 minutes) may // Note: very long pauses between measurements (more than 8 minutes) may
// overflow the counter, leading to incorrect results. This might be fixed by // overflow the counter, leading to incorrect results. This might be fixed by
// handling the overflow event. // handling the overflow event.
func monotime() uint64 { func ticks() timeUnit {
rtcCounter := uint32(nrf.RTC0.COUNTER) rtcCounter := uint32(nrf.RTC0.COUNTER)
offset := (rtcCounter - rtcLastCounter) % 0xffffff // change since last measurement offset := (rtcCounter - rtcLastCounter) % 0xffffff // change since last measurement
rtcLastCounter = rtcCounter rtcLastCounter = rtcCounter
timestamp += uint64(offset * 32) // TODO: not precise timestamp += timeUnit(offset) // TODO: not precise
return timestamp return timestamp
} }

Просмотреть файл

@ -6,14 +6,16 @@ import (
"unsafe" "unsafe"
) )
const Microsecond = 1
func _Cfunc_putchar(c int) int func _Cfunc_putchar(c int) int
func _Cfunc_usleep(usec uint) int func _Cfunc_usleep(usec uint) int
func _Cfunc_calloc(nmemb, size uintptr) unsafe.Pointer func _Cfunc_calloc(nmemb, size uintptr) unsafe.Pointer
func _Cfunc_exit(status int) func _Cfunc_exit(status int)
func _Cfunc_clock_gettime(clk_id uint, ts *timespec) func _Cfunc_clock_gettime(clk_id uint, ts *timespec)
type timeUnit int64
const tickMicros = 1
// TODO: Linux/amd64-specific // TODO: Linux/amd64-specific
type timespec struct { type timespec struct {
tv_sec int64 tv_sec int64
@ -26,18 +28,26 @@ func putchar(c byte) {
_Cfunc_putchar(int(c)) _Cfunc_putchar(int(c))
} }
func sleep(d Duration) { //go:linkname sleep time.Sleep
_Cfunc_usleep(uint(d)) func sleep(d int64) {
_Cfunc_usleep(uint(d) / 1000)
} }
// Return monotonic time in microseconds. func sleepTicks(d timeUnit) {
sleep(int64(d))
}
// Return monotonic time in nanoseconds.
// //
// TODO: use nanoseconds?
// TODO: noescape // TODO: noescape
func monotime() uint64 { func monotime() uint64 {
ts := timespec{} ts := timespec{}
_Cfunc_clock_gettime(CLOCK_MONOTONIC_RAW, &ts) _Cfunc_clock_gettime(CLOCK_MONOTONIC_RAW, &ts)
return uint64(ts.tv_sec)*1000*1000 + uint64(ts.tv_nsec)/1000 return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec)
}
func ticks() timeUnit {
return timeUnit(monotime())
} }
func abort() { func abort() {

Просмотреть файл

@ -72,7 +72,7 @@ var (
runqueueFront *coroutine runqueueFront *coroutine
runqueueBack *coroutine runqueueBack *coroutine
sleepQueue *coroutine sleepQueue *coroutine
sleepQueueBaseTime uint64 sleepQueueBaseTime timeUnit
) )
// Simple logging, for debugging. // Simple logging, for debugging.
@ -92,13 +92,13 @@ func scheduleLogTask(msg string, t *coroutine) {
// Set the task state to sleep for a given time. // Set the task state to sleep for a given time.
// //
// This is a compiler intrinsic. // This is a compiler intrinsic.
func sleepTask(caller *coroutine, duration Duration) { func sleepTask(caller *coroutine, duration int64) {
if schedulerDebug { if schedulerDebug {
println(" set state sleep:", caller, uint32(duration)) println(" set state sleep:", caller, uint32(duration/tickMicros))
} }
promise := caller.promise() promise := caller.promise()
promise.state = TASK_STATE_SLEEP promise.state = TASK_STATE_SLEEP
promise.data = uint32(duration) // TODO: longer durations promise.data = uint32(duration / tickMicros) // TODO: longer durations
} }
// Wait for the result of an async call. This means that the parent goroutine // Wait for the result of an async call. This means that the parent goroutine
@ -194,7 +194,7 @@ func addSleepTask(t *coroutine) {
panic("runtime: addSleepTask: task not sleeping") panic("runtime: addSleepTask: task not sleeping")
} }
} }
now := monotime() now := ticks()
if sleepQueue == nil { if sleepQueue == nil {
scheduleLog(" -> sleep new queue") scheduleLog(" -> sleep new queue")
// Create new linked list for the sleep queue. // Create new linked list for the sleep queue.
@ -244,15 +244,15 @@ func scheduler(main *coroutine) {
// Main scheduler loop. // Main scheduler loop.
for { for {
scheduleLog("\n schedule") scheduleLog("\n schedule")
now := monotime() now := ticks()
// Add tasks that are done sleeping to the end of the runqueue so they // Add tasks that are done sleeping to the end of the runqueue so they
// will be executed soon. // will be executed soon.
if sleepQueue != nil && now-sleepQueueBaseTime >= uint64(sleepQueue.promise().data) { if sleepQueue != nil && now-sleepQueueBaseTime >= timeUnit(sleepQueue.promise().data) {
t := sleepQueue t := sleepQueue
scheduleLogTask(" awake:", t) scheduleLogTask(" awake:", t)
promise := t.promise() promise := t.promise()
sleepQueueBaseTime += uint64(promise.data) sleepQueueBaseTime += timeUnit(promise.data)
sleepQueue = promise.next sleepQueue = promise.next
promise.state = TASK_STATE_RUNNABLE promise.state = TASK_STATE_RUNNABLE
promise.next = nil promise.next = nil
@ -269,11 +269,11 @@ func scheduler(main *coroutine) {
scheduleLog(" no tasks left!") scheduleLog(" no tasks left!")
return return
} }
timeLeft := uint64(sleepQueue.promise().data) - (now - sleepQueueBaseTime) timeLeft := timeUnit(sleepQueue.promise().data) - (now - sleepQueueBaseTime)
if schedulerDebug { if schedulerDebug {
println(" sleeping...", sleepQueue, uint32(timeLeft)) println(" sleeping...", sleepQueue, uint32(timeLeft))
} }
sleep(Duration(timeLeft)) sleepTicks(timeUnit(timeLeft))
continue continue
} }

Просмотреть файл

@ -1,11 +0,0 @@
package runtime
// TODO: use the time package for this.
type Duration uint64
// Use microseconds as the smallest time unit
const (
Millisecond = Microsecond * 1000
Second = Millisecond * 1000
)