machine/stm32, nrf: flash API (#3472)

machine/stm32, nrf: implement machine.Flash

Implements the machine.Flash interface using the same definition as the tinyfs BlockDevice.

This implementation covers the stm32f4, stm32l4, stm32wlx, nrf51, nrf52, and nrf528xx processors.
This commit is contained in:
Ron Evans
2023-02-27 13:55:38 +01:00
committed by GitHub
parent 8bf94b9231
commit 6e1b8a54aa
12 changed files with 817 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx
package machine
import (
"errors"
"io"
"unsafe"
)
//go:extern __flash_data_start
var flashDataStart [0]byte
//go:extern __flash_data_end
var flashDataEnd [0]byte
// Return the start of the writable flash area, aligned on a page boundary. This
// is usually just after the program and static data.
func FlashDataStart() uintptr {
pagesize := uintptr(eraseBlockSize())
return (uintptr(unsafe.Pointer(&flashDataStart)) + pagesize - 1) &^ (pagesize - 1)
}
// Return the end of the writable flash area. Usually this is the address one
// past the end of the on-chip flash.
func FlashDataEnd() uintptr {
return uintptr(unsafe.Pointer(&flashDataEnd))
}
var (
errFlashCannotErasePage = errors.New("cannot erase flash page")
errFlashInvalidWriteLength = errors.New("write flash data must align to correct number of bits")
errFlashNotAllowedWriteData = errors.New("not allowed to write flash data")
errFlashCannotWriteData = errors.New("cannot write flash data")
errFlashCannotReadPastEOF = errors.New("cannot read beyond end of flash data")
errFlashCannotWritePastEOF = errors.New("cannot write beyond end of flash data")
)
// BlockDevice is the raw device that is meant to store flash data.
type BlockDevice interface {
// ReadAt reads the given number of bytes from the block device.
io.ReaderAt
// WriteAt writes the given number of bytes to the block device.
io.WriterAt
// Size returns the number of bytes in this block device.
Size() int64
// 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.
WriteBlockSize() int64
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
EraseBlockSize() int64
// 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.
EraseBlocks(start, len int64) error
}
// FlashBuffer implements the ReadWriteCloser interface using the BlockDevice interface.
type FlashBuffer struct {
b BlockDevice
start uintptr
current uintptr
}
// OpenFlashBuffer opens a FlashBuffer.
func OpenFlashBuffer(b BlockDevice, address uintptr) *FlashBuffer {
return &FlashBuffer{b: b, start: address, current: address}
}
// Read data from a FlashBuffer.
func (fl *FlashBuffer) Read(p []byte) (n int, err error) {
fl.b.ReadAt(p, int64(fl.current))
fl.current += uintptr(len(p))
return len(p), nil
}
// Write data to a FlashBuffer.
func (fl *FlashBuffer) Write(p []byte) (n int, err error) {
// any new pages needed?
// NOTE probably will not work as expected if you try to write over page boundary
// of pages with different sizes.
pagesize := uintptr(fl.b.EraseBlockSize())
currentPageCount := (fl.current - fl.start + pagesize - 1) / pagesize
totalPagesNeeded := (fl.current - fl.start + uintptr(len(p)) + pagesize - 1) / pagesize
if currentPageCount == totalPagesNeeded {
// just write the data
n, err := fl.b.WriteAt(p, int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(n)
return n, nil
}
// erase enough blocks to hold the data
page := fl.flashPageFromAddress(fl.start + (currentPageCount * pagesize))
fl.b.EraseBlocks(page, int64(totalPagesNeeded-currentPageCount))
// write the data
for i := 0; i < len(p); i += int(pagesize) {
var last int = i + int(pagesize)
if i+int(pagesize) > len(p) {
last = len(p)
}
_, err := fl.b.WriteAt(p[i:last], int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(last - i)
}
return len(p), nil
}
// Close the FlashBuffer.
func (fl *FlashBuffer) Close() error {
return nil
}
// Seek implements io.Seeker interface, but with limitations.
// You can only seek relative to the start.
// Also, you cannot use seek before write operations, only read.
func (fl *FlashBuffer) Seek(offset int64, whence int) (int64, error) {
fl.current = fl.start + uintptr(offset)
return offset, nil
}
// calculate page number from address
func (fl *FlashBuffer) flashPageFromAddress(address uintptr) int64 {
return int64(address-memoryStart) / fl.b.EraseBlockSize()
}