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):
```go
import "machine"
import (
"machine"
"time"
)
func main() {
led := machine.GPIO{machine.LED}
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
for {
led.Low()
runtime.Sleep(runtime.Millisecond * 1000)
time.Sleep(time.Millisecond * 1000)
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)
}
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.
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() != "" {
continue // assume non-blocking
}
if child.LinkName() == "runtime.Sleep" {
if child.fn.RelString(nil) == "time.Sleep" {
f.blocking = true
}
f.children = append(f.children, child)

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

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

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

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

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

@ -2,7 +2,7 @@ package main
import (
"machine"
"runtime"
"time"
)
// This example assumes that the button is connected to pin 8. Change the value
@ -23,6 +23,6 @@ func main() {
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 {
// Note: setting GOMAXPROCS is ignored.
return 1

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

@ -8,13 +8,11 @@ import (
const BOARD = "arduino"
const Microsecond = 1
type timeUnit uint32
var currentTime uint64
var currentTime timeUnit
func init() {
currentTime = 0
}
const tickMicros = 1024 * 16384
// 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
@ -53,9 +51,13 @@ func putchar(c byte) {
//
// TODO: not very accurate. Improve accuracy by calibrating on startup and every
// once in a while.
func sleep(d Duration) {
currentTime += uint64(d)
d /= 16384 // about 16ms
//go:linkname sleep time.Sleep
func sleep(d int64) {
sleepTicks(timeUnit(d / tickMicros))
}
func sleepTicks(d timeUnit) {
currentTime += d
for d != 0 {
sleepWDT(WDT_PERIOD_16MS)
d -= 1
@ -90,7 +92,7 @@ func sleepWDT(period uint8) {
*avr.SMCR = 0
}
func monotime() uint64 {
func ticks() timeUnit {
return currentTime
}

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

@ -7,7 +7,9 @@ import (
"device/nrf"
)
const Microsecond = 1
type timeUnit int64
const tickMicros = 1024 * 32
//go:export _start
func _start() {
@ -48,31 +50,35 @@ func putchar(c byte) {
nrf.UART0.EVENTS_TXDRDY = 0
}
func sleep(d Duration) {
ticks64 := d / 32
for ticks64 != 0 {
monotime() // update timestamp
ticks := uint32(ticks64) & 0x7fffff // 23 bits (to be on the safe side)
//go:linkname sleep time.Sleep
func sleep(d timeUnit) {
sleepTicks(d / tickMicros)
}
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...)
ticks64 -= Duration(ticks)
d -= timeUnit(ticks)
}
}
var (
timestamp uint64 // microseconds since boottime
timestamp timeUnit // nanoseconds since boottime
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
// overflow the counter, leading to incorrect results. This might be fixed by
// handling the overflow event.
func monotime() uint64 {
func ticks() timeUnit {
rtcCounter := uint32(nrf.RTC0.COUNTER)
offset := (rtcCounter - rtcLastCounter) % 0xffffff // change since last measurement
rtcLastCounter = rtcCounter
timestamp += uint64(offset * 32) // TODO: not precise
timestamp += timeUnit(offset) // TODO: not precise
return timestamp
}

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

@ -6,14 +6,16 @@ import (
"unsafe"
)
const Microsecond = 1
func _Cfunc_putchar(c int) int
func _Cfunc_usleep(usec uint) int
func _Cfunc_calloc(nmemb, size uintptr) unsafe.Pointer
func _Cfunc_exit(status int)
func _Cfunc_clock_gettime(clk_id uint, ts *timespec)
type timeUnit int64
const tickMicros = 1
// TODO: Linux/amd64-specific
type timespec struct {
tv_sec int64
@ -26,18 +28,26 @@ func putchar(c byte) {
_Cfunc_putchar(int(c))
}
func sleep(d Duration) {
_Cfunc_usleep(uint(d))
//go:linkname sleep time.Sleep
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
func monotime() uint64 {
ts := timespec{}
_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() {

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

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