Conversation
Bond storage already needed sd_flash_write/sd_flash_page_erase instead of direct NVMC access while the SoftDevice is enabled. Expose that as SDFlash, a machine.BlockDevice-compatible type applications can use to store their own data (for example a keymap) on the same flash region, without conflicting with the SoftDevice. machine.Flash's own SoftDevice-aware path does not work here: it polls sd_evt_get for the completion event, but this package's SWI2 handler drains that queue first, so machine.Flash would wait forever. Flash operations complete asynchronously and share a single result flag (flashOpResult) between callers, so SDFlash and bond storage must not run concurrently; serialize them with flashOpMu.
|
@sago35 isn't this something it would be best to solve in https://github.com/tinygo-org/tinygo/blob/dev/src/machine/machine_nrf.go#L400 itself? Since apparently that is not working? |
|
@deadprogram I agree that machine.Flash is the appropriate place for this, but since it cannot be fixed solely within the machine package, this PR adds a temporary fix that works on released TinyGo versions. As noted in the comments below, because of its relationship with
|
|
I wrote two programs. The first one uses // machine.Flash hangs forever once the bluetooth SoftDevice backend is
// enabled: its completion event is drained by this package's interrupt
// handler, so the sd_evt_get poll in machine_nrf.go never sees it.
//
// tinygo flash -monitor -target=xiao-ble ./repro-machineflash
//
// Expected: "erasing..." is printed, then nothing more (the heartbeat
// stops too, since the poll loop never yields).
package main
import (
"machine"
"time"
"tinygo.org/x/bluetooth"
)
func main() {
time.Sleep(3 * time.Second)
go func() {
for {
println("heartbeat")
time.Sleep(time.Second)
}
}()
println("enabling the SoftDevice...")
if err := bluetooth.DefaultAdapter.Enable(); err != nil {
panic("failed to enable BLE stack: " + err.Error())
}
var dev = machine.Flash
// A page in the middle of the flash data area, away from data other
// programs may keep at either end (bonds live in the last page).
block := dev.Size() / dev.EraseBlockSize() / 2
println("erasing one page via machine.Flash...")
if err := dev.EraseBlocks(block, 1); err != nil {
println("erase failed:", err.Error())
return
}
println("...erase done") // never reached
for {
time.Sleep(time.Second)
}
}The second one uses // The same flash operation as repro-machineflash, but through
// bluetooth.SDFlash, whose completion event is delivered by this package's
// own interrupt handler: it completes.
//
// tinygo flash -monitor -target=xiao-ble ./repro-sdflash
//
// Expected: the erase completes and the heartbeat keeps printing.
package main
import (
"time"
"tinygo.org/x/bluetooth"
)
func main() {
time.Sleep(3 * time.Second)
go func() {
for {
println("heartbeat")
time.Sleep(time.Second)
}
}()
println("enabling the SoftDevice...")
if err := bluetooth.DefaultAdapter.Enable(); err != nil {
panic("failed to enable BLE stack: " + err.Error())
}
var dev bluetooth.SDFlash
// A page in the middle of the flash data area, away from data other
// programs may keep at either end (bonds live in the last page).
block := dev.Size() / dev.EraseBlockSize() / 2
println("erasing one page via bluetooth.SDFlash...")
if err := dev.EraseBlocks(block, 1); err != nil {
println("erase failed:", err.Error())
return
}
println("...erase done")
for {
time.Sleep(time.Second)
}
} |
Bond storage already needed sd_flash_write/sd_flash_page_erase instead of direct NVMC access while the SoftDevice is enabled. Expose that as SDFlash, a machine.BlockDevice-compatible type applications can use to store their own data (for example a keymap) on the same flash region, without conflicting with the SoftDevice. machine.Flash's own SoftDevice-aware path does not work here: it polls sd_evt_get for the completion event, but this package's SWI2 handler drains that queue first, so machine.Flash would wait forever.
Flash operations complete asynchronously and share a single result flag (flashOpResult) between callers, so SDFlash and bond storage must not run concurrently; serialize them with flashOpMu.