Skip to content

Commit 6dee98c

Browse files
aykevldeadprogram
authored andcommitted
nrf: support flash operations while the SoftDevice is enabled
Flash operations must go through the SoftDevice if it is enabled, otherwise they will crash the chip. (And even then the SoftDevice doesn't guarantee they'll succeed, depending on advertisement frequency etc). But this patch makes sure to call the appropriate APIs so that flash is usable while using Bluetooth.
1 parent 484ccf8 commit 6dee98c

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

src/machine/machine_nrf.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
package machine
44

55
import (
6+
"device/arm"
67
"device/nrf"
8+
"errors"
79
"internal/binary"
810
"runtime/interrupt"
911
"unsafe"
@@ -380,13 +382,45 @@ func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
380382
// If the length of p is not long enough it will be padded with 0xFF bytes.
381383
// This method assumes that the destination is already erased.
382384
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
385+
if len(p) == 0 {
386+
return 0, nil // nothing to do (and it would fail in sd_flash_write)
387+
}
388+
383389
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
384390
return 0, errFlashCannotWritePastEOF
385391
}
386392

387393
address := FlashDataStart() + uintptr(off)
388394
padded := flashPad(p, int(f.WriteBlockSize()))
389395

396+
// When the SoftDevice is enabled, access to the flash is restricted and
397+
// must go through the SoftDevice API.
398+
if isSoftDeviceEnabled() {
399+
// Call sd_flash_write, which is SVC_SOC_BASE + 9 in all the
400+
// SoftDevices I've checked.
401+
// Documentation:
402+
// https://docs.nordicsemi.com/bundle/s140_v6.0.0_api/page/group_n_r_f_s_o_c_f_u_n_c_t_i_o_n_s.html
403+
result := arm.SVCall3(0x20+9, address, &p[0], uint32(len(p)))
404+
if result != 0 {
405+
// Could not queue flash operation? Not sure when this can
406+
// happen.
407+
return 0, flashError
408+
}
409+
410+
// Wait until the SoftDevice is finished.
411+
flashStatus = flashStatusBusy
412+
for flashStatus == flashStatusBusy {
413+
handleSoftDeviceEvents()
414+
}
415+
416+
// Check whether the operation was successful.
417+
if flashStatus != flashStatusOk {
418+
flashStatus = flashStatusOk
419+
return 0, flashError
420+
}
421+
return len(p), nil
422+
}
423+
390424
waitWhileFlashBusy()
391425

392426
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
@@ -428,6 +462,40 @@ func (f flashBlockDevice) EraseBlockSize() int64 {
428462
// supports this. The start and len parameters are in block numbers, use
429463
// EraseBlockSize to map addresses to blocks.
430464
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
465+
// When the SoftDevice is enabled, access to the flash is restricted and
466+
// must go through the SoftDevice API.
467+
if isSoftDeviceEnabled() {
468+
for i := range uint32(len) {
469+
flashPage := uint32(FlashDataStart())/eraseBlockSizeValue + uint32(start) + i
470+
471+
// Call sd_flash_page_erase, which is SVC_SOC_BASE + 8 in all the
472+
// SoftDevices I've checked.
473+
// Documentation:
474+
// https://docs.nordicsemi.com/bundle/s140_v6.0.0_api/page/group_n_r_f_s_o_c_f_u_n_c_t_i_o_n_s.html#ga9c93dd94a138ad8b5ed3693ea38ffb3e
475+
result := arm.SVCall1(0x20+8, flashPage)
476+
if result != 0 {
477+
// Could not queue flash operation? Not sure when this can
478+
// happen.
479+
return flashError
480+
}
481+
482+
// Wait until the SoftDevice is finished.
483+
flashStatus = flashStatusBusy
484+
for flashStatus == flashStatusBusy {
485+
handleSoftDeviceEvents()
486+
}
487+
488+
// Check whether the operation was successful.
489+
if flashStatus != flashStatusOk {
490+
flashStatus = flashStatusOk
491+
return flashError
492+
}
493+
}
494+
return nil
495+
}
496+
497+
// SoftDevice is not used or enabled. Use NVIC directly.
498+
431499
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
432500
waitWhileFlashBusy()
433501

@@ -447,3 +515,52 @@ func waitWhileFlashBusy() {
447515
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
448516
}
449517
}
518+
519+
var flashError = errors.New("machine: flash operation failed")
520+
521+
const (
522+
flashStatusOk = iota
523+
flashStatusError
524+
flashStatusBusy
525+
)
526+
527+
var flashStatus uint8 = flashStatusOk
528+
529+
var sdEvent uint32
530+
531+
// Process all queued SoftDevice events. May only be called when the SoftDevice is enabled.
532+
//
533+
// Normally these are handled in the same interrupt where Bluetooth events are
534+
// handled. But in TinyGo, that's complicated. One option would be to put it in
535+
// the tinygo.org/x/bluetooth package, but that would cause a circular
536+
// dependency between the machine and the bluetooth package. Another would be to
537+
// put it here, but let the bluetooth package call handleSoftDeviceEvents, but
538+
// that relies on updating the bluetooth package at the same time. As a
539+
// compromise, these events are handled directly where they are expected (here
540+
// in the machine package). This works in practice since there are only very few
541+
// of such events (at the moment, only flash-related ones which is in the
542+
// machine package anyway).
543+
func handleSoftDeviceEvents() {
544+
for {
545+
var result uintptr
546+
if nrf.Device == "nrf52" || nrf.Device == "nrf52840" || nrf.Device == "nrf52833" {
547+
// sd_evt_get: SOC_SVC_BASE_NOT_AVAILABLE + 31
548+
result = arm.SVCall1(0x2C+31, &sdEvent)
549+
} else {
550+
return // TODO: nrf51 etc
551+
}
552+
if result != 0 {
553+
// Some error occured. The only possible error is
554+
// NRF_ERROR_NOT_FOUND, which means there are no more events.
555+
return
556+
}
557+
558+
// The following events are the same numbers in all SoftDevices I've checked.
559+
switch sdEvent {
560+
case 2: // NRF_EVT_FLASH_OPERATION_SUCCESS
561+
flashStatus = flashStatusOk
562+
case 3: // NRF_EVT_FLASH_OPERATION_ERROR
563+
flashStatus = flashStatusError
564+
}
565+
}
566+
}

0 commit comments

Comments
 (0)