mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
6e1b8a54aa
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.
145 lines
4.4 KiB
Go
145 lines
4.4 KiB
Go
//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()
|
|
}
|