tinygo-org / tinygo

Go compiler for small places. Microcontrollers, WebAssembly (WASM/WASI), and command-line tools. Based on LLVM.
https://tinygo.org
Other
15.25k stars 899 forks source link

On RP2040, we got incorrect sleep time after a select with timeout #2970

Open elbert-wong opened 2 years ago

elbert-wong commented 2 years ago

In a channel select with timeout, if the select hit the non-timeout branch, we got incorrect sleep time in the following code.

we got correct sleep time if we: comment out the timer go-routine OR comment out the timeout branch in select

Note: to handle the pin edge interrupt, the #2847 code is merged, the issue is reported as #2877

屏幕截图 2022-07-07 174547

pinOut signal: scope_18

kenbell commented 2 years ago

@elbert-wong - I'm not able to repro on the latest dev branch. I'm seeing the timing below without commenting out any code (I'm using a Pico): SDS00001

This is to code I'm using:

package main

import (
    "machine"
    "runtime"
    "time"
)

const (
    pinIn  = machine.GPIO13
    pinOut = machine.GPIO15
    pinLED = machine.LED
)

var timeout = make(chan bool, 1)
var tstChan = make(chan bool, 1)

func pinConfig() {
    pinIn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
    pinLED.Configure(machine.PinConfig{Mode: machine.PinOutput})
    pinOut.Configure(machine.PinConfig{Mode: machine.PinOutput})
}

func main() {
    pinConfig()

    go func() {
        time.Sleep(10000 * time.Millisecond)
        timeout <- true
    }()

    pinIn.SetInterrupt(machine.PinFalling, pinFallHandler)

    select {
    case <-tstChan:
    case <-timeout:
    }

    pinIn.SetInterrupt(machine.PinFalling, nil)

    pinOut.High()
    time.Sleep(100 * time.Millisecond)
    pinOut.Low()
    time.Sleep(300 * time.Millisecond)
    pinOut.High()

    for {
        runtime.Gosched()
    }
}

func pinFallHandler(pin machine.Pin) {
    tstChan <- true
}
elbert-wong commented 2 years ago

@kenbell Thank you for your prompt reply! Could you confirm that the light sleep is enabled in your dev code? (which is also included in release 0.24.0) If I disable the light sleep, the following sleep in main loop works well. But in that case, the select code is blocked for a very long time, if a new ext interrupt triggers again during this block time, we will get a panic (writing a full channel in interrupt).

my issue was found on 0.22.0(with light sleep merged) and also 0.24.0

I have build a dev branch (tinygo version 0.25.0-dev linux/amd64 (using go version go1.18.3 and LLVM version 14.0.6) with outside llvm-14 this morning, still no lucky. But I am not sure whether I am using the tinygo in dev correctly. I just run it as below:

/tinygo build -target=pico -o pico.uf2 -size short github.go And also I have copied the folder lib/compiler-rt-builtins from release 0.24.0. If anything wrong in my steps, please let me know. Thanks!
elbert-wong commented 2 years ago

Seems like I fixed this issue by correcting the sleep duration in addSleepTask. But I have no idea why this code works if light-sleep is not enabled. Probably without light-sleep the "select" code block does not run before any sleeping go-routine wakes up. The interrupt event doesn't wake up the main loop, although who is waiting for the channel reading. The scheduler is in busy loop when ext interrupt fires.

屏幕截图 2022-07-08 201155