aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/machine/machine_atsamd21.go
diff options
context:
space:
mode:
authorRon Evans <[email protected]>2023-03-08 06:06:49 +0100
committerGitHub <[email protected]>2023-03-08 06:06:49 +0100
commitfc28f513c7cb60c4c1f25d12caa6ae381f6f0308 (patch)
tree095a5228ee4101afa89b7f974d7fb0863d2c12f4 /src/machine/machine_atsamd21.go
parent34ba0c1be1e5adc4b5095fa9d1e15a7a26e2f519 (diff)
downloadtinygo-fc28f513c7cb60c4c1f25d12caa6ae381f6f0308.tar.gz
tinygo-fc28f513c7cb60c4c1f25d12caa6ae381f6f0308.zip
machine/samd21: implement Flash interface (#3496)
machine/samd21: implement Flash interface
Diffstat (limited to 'src/machine/machine_atsamd21.go')
-rw-r--r--src/machine/machine_atsamd21.go167
1 files changed, 167 insertions, 0 deletions
diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go
index 7a5a20c7e..2f4f7338e 100644
--- a/src/machine/machine_atsamd21.go
+++ b/src/machine/machine_atsamd21.go
@@ -7,8 +7,11 @@
package machine
import (
+ "bytes"
"device/arm"
"device/sam"
+ "encoding/binary"
+ "errors"
"runtime/interrupt"
"unsafe"
)
@@ -1788,3 +1791,167 @@ func syncDAC() {
for sam.DAC.STATUS.HasBits(sam.DAC_STATUS_SYNCBUSY) {
}
}
+
+// Flash related code
+const memoryStart = 0x0
+
+// compile-time check for ensuring we fulfill BlockDevice interface
+var _ BlockDevice = flashBlockDevice{}
+
+var Flash flashBlockDevice
+
+type flashBlockDevice struct {
+ initComplete bool
+}
+
+// ReadAt reads the given number of bytes from the block device.
+func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
+ if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
+ return 0, errFlashCannotReadPastEOF
+ }
+
+ f.ensureInitComplete()
+
+ waitWhileFlashBusy()
+
+ data := unsafe.Slice((*byte)(unsafe.Add(unsafe.Pointer(FlashDataStart()), uintptr(off))), len(p))
+ copy(p, data)
+
+ return len(p), nil
+}
+
+// WriteAt writes the given number of bytes to the block device.
+// Only word (32 bits) length data can be programmed.
+// See Atmel-42181G–SAM-D21_Datasheet–09/2015 page 359.
+// If the length of p is not long enough it will be padded with 0xFF bytes.
+// This method assumes that the destination is already erased.
+func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
+ if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
+ return 0, errFlashCannotWritePastEOF
+ }
+
+ f.ensureInitComplete()
+
+ address := FlashDataStart() + uintptr(off)
+ padded := f.pad(p)
+
+ waitWhileFlashBusy()
+
+ for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
+ // write word
+ *(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(padded[j : j+int(f.WriteBlockSize())])
+
+ sam.NVMCTRL.SetADDR(uint32(address >> 1))
+ sam.NVMCTRL.CTRLA.Set(sam.NVMCTRL_CTRLA_CMD_WP | (sam.NVMCTRL_CTRLA_CMDEX_KEY << sam.NVMCTRL_CTRLA_CMDEX_Pos))
+
+ waitWhileFlashBusy()
+
+ if err := checkFlashError(); err != nil {
+ return j, err
+ }
+
+ address += uintptr(f.WriteBlockSize())
+ }
+
+ return len(padded), nil
+}
+
+// Size returns the number of bytes in this block device.
+func (f flashBlockDevice) Size() int64 {
+ return int64(FlashDataEnd() - FlashDataStart())
+}
+
+const writeBlockSize = 4
+
+// WriteBlockSize returns the block size in which data can be written to
+// memory. It can be used by a client to optimize writes, non-aligned writes
+// should always work correctly.
+func (f flashBlockDevice) WriteBlockSize() int64 {
+ return writeBlockSize
+}
+
+const eraseBlockSizeValue = 256
+
+func eraseBlockSize() int64 {
+ return eraseBlockSizeValue
+}
+
+// EraseBlockSize returns the smallest erasable area on this particular chip
+// in bytes. This is used for the block size in EraseBlocks.
+func (f flashBlockDevice) EraseBlockSize() int64 {
+ return eraseBlockSize()
+}
+
+// EraseBlocks erases the given number of blocks. An implementation may
+// transparently coalesce ranges of blocks into larger bundles if the chip
+// supports this. The start and len parameters are in block numbers, use
+// EraseBlockSize to map addresses to blocks.
+func (f flashBlockDevice) EraseBlocks(start, len int64) error {
+ f.ensureInitComplete()
+
+ address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
+ waitWhileFlashBusy()
+
+ for i := start; i < start+len; i++ {
+ sam.NVMCTRL.SetADDR(uint32(address >> 1))
+ sam.NVMCTRL.CTRLA.Set(sam.NVMCTRL_CTRLA_CMD_ER | (sam.NVMCTRL_CTRLA_CMDEX_KEY << sam.NVMCTRL_CTRLA_CMDEX_Pos))
+
+ waitWhileFlashBusy()
+
+ if err := checkFlashError(); err != nil {
+ return err
+ }
+
+ address += uintptr(f.EraseBlockSize())
+ }
+
+ return nil
+}
+
+// pad data if needed so it is long enough for correct byte alignment on writes.
+func (f flashBlockDevice) pad(p []byte) []byte {
+ paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
+ if paddingNeeded == 0 {
+ return p
+ }
+
+ padding := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
+ return append(p, padding...)
+}
+
+func (f flashBlockDevice) ensureInitComplete() {
+ if f.initComplete {
+ return
+ }
+
+ sam.NVMCTRL.SetCTRLB_READMODE(sam.NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY)
+ sam.NVMCTRL.SetCTRLB_SLEEPPRM(sam.NVMCTRL_CTRLB_SLEEPPRM_WAKEONACCESS)
+
+ waitWhileFlashBusy()
+
+ f.initComplete = true
+}
+
+func waitWhileFlashBusy() {
+ for sam.NVMCTRL.GetINTFLAG_READY() != sam.NVMCTRL_INTFLAG_READY {
+ }
+}
+
+var (
+ errFlashPROGE = errors.New("errFlashPROGE")
+ errFlashLOCKE = errors.New("errFlashLOCKE")
+ errFlashNVME = errors.New("errFlashNVME")
+)
+
+func checkFlashError() error {
+ switch {
+ case sam.NVMCTRL.GetSTATUS_PROGE() != 0:
+ return errFlashPROGE
+ case sam.NVMCTRL.GetSTATUS_LOCKE() != 0:
+ return errFlashLOCKE
+ case sam.NVMCTRL.GetSTATUS_NVME() != 0:
+ return errFlashNVME
+ }
+
+ return nil
+}