all: rewrite sleep function
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).
Этот коммит содержится в:
родитель
1ac67cf8de
коммит
2a20c0c7f0
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)
|
||||
rtc_sleep(ticks) // TODO: not accurate (must be d / 30.5175...)
|
||||
ticks64 -= Duration(ticks)
|
||||
//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...)
|
||||
d -= timeUnit(ticks)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
timestamp uint64 // microseconds since boottime
|
||||
rtcLastCounter uint32 // 24 bits ticks
|
||||
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
|
||||
)
|
Загрузка…
Создание таблицы
Сослаться в новой задаче