diff --git a/README.markdown b/README.markdown index 9b2fda63..09b604f8 100644 --- a/README.markdown +++ b/README.markdown @@ -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) } } ``` diff --git a/compiler.go b/compiler.go index 7d52ef06..503bf832 100644 --- a/compiler.go +++ b/compiler.go @@ -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]}, "") diff --git a/passes.go b/passes.go index 82962bf3..cedfce3c 100644 --- a/passes.go +++ b/passes.go @@ -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) diff --git a/src/examples/blinky1/blinky1.go b/src/examples/blinky1/blinky1.go index 8889cb69..9e74bd17 100644 --- a/src/examples/blinky1/blinky1.go +++ b/src/examples/blinky1/blinky1.go @@ -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) } } diff --git a/src/examples/blinky2/blinky2.go b/src/examples/blinky2/blinky2.go index 417ca604..b470e923 100644 --- a/src/examples/blinky2/blinky2.go +++ b/src/examples/blinky2/blinky2.go @@ -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) } } diff --git a/src/examples/button/button.go b/src/examples/button/button.go index 3be226c4..30261b8c 100644 --- a/src/examples/button/button.go +++ b/src/examples/button/button.go @@ -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) } } diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index c6e892d6..159e65fa 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -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 diff --git a/src/runtime/runtime_avr.go b/src/runtime/runtime_avr.go index 48e433c8..b94e86d2 100644 --- a/src/runtime/runtime_avr.go +++ b/src/runtime/runtime_avr.go @@ -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 } diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 66418e8a..a359cef4 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -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 } diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index fb59175c..9baf66c4 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -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() { diff --git a/src/runtime/scheduler.go b/src/runtime/scheduler.go index 8d073230..f31a601b 100644 --- a/src/runtime/scheduler.go +++ b/src/runtime/scheduler.go @@ -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 } diff --git a/src/runtime/time.go b/src/runtime/time.go deleted file mode 100644 index 3bdddaf3..00000000 --- a/src/runtime/time.go +++ /dev/null @@ -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 -)