Browse Source

machine/samd21: implement Flash interface (#3496)

machine/samd21: implement Flash interface
pull/3382/head
Ron Evans 2 years ago
committed by GitHub
parent
commit
fc28f513c7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/machine/flash.go
  2. 167
      src/machine/machine_atsamd21.go

2
src/machine/flash.go

@ -1,4 +1,4 @@
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx //go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx || atsamd21
package machine package machine

167
src/machine/machine_atsamd21.go

@ -7,8 +7,11 @@
package machine package machine
import ( import (
"bytes"
"device/arm" "device/arm"
"device/sam" "device/sam"
"encoding/binary"
"errors"
"runtime/interrupt" "runtime/interrupt"
"unsafe" "unsafe"
) )
@ -1788,3 +1791,167 @@ func syncDAC() {
for sam.DAC.STATUS.HasBits(sam.DAC_STATUS_SYNCBUSY) { 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
}

Loading…
Cancel
Save