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.
Этот коммит содержится в:
родитель
8bf94b9231
коммит
6e1b8a54aa
12 изменённых файлов: 817 добавлений и 0 удалений
57
src/examples/flash/main.go
Обычный файл
57
src/examples/flash/main.go
Обычный файл
|
@ -0,0 +1,57 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
message = "1234567887654321123456788765432112345678876543211234567887654321"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
// Print out general information
|
||||||
|
println("Flash data start: ", machine.FlashDataStart())
|
||||||
|
println("Flash data end: ", machine.FlashDataEnd())
|
||||||
|
println("Flash data size, bytes:", machine.Flash.Size())
|
||||||
|
println("Flash write block size:", machine.Flash.WriteBlockSize())
|
||||||
|
println("Flash erase block size:", machine.Flash.EraseBlockSize())
|
||||||
|
println()
|
||||||
|
|
||||||
|
flash := machine.OpenFlashBuffer(machine.Flash, machine.FlashDataStart())
|
||||||
|
original := make([]byte, len(message))
|
||||||
|
saved := make([]byte, len(message))
|
||||||
|
|
||||||
|
// Read flash contents on start (data shall survive power off)
|
||||||
|
print("Reading data from flash: ")
|
||||||
|
_, err = flash.Read(original)
|
||||||
|
checkError(err)
|
||||||
|
println(string(original))
|
||||||
|
|
||||||
|
// Write the message to flash
|
||||||
|
print("Writing data to flash: ")
|
||||||
|
flash.Seek(0, 0) // rewind back to beginning
|
||||||
|
_, err = flash.Write([]byte(message))
|
||||||
|
checkError(err)
|
||||||
|
println(string(message))
|
||||||
|
|
||||||
|
// Read back flash contents after write (verify data is the same as written)
|
||||||
|
print("Reading data back from flash: ")
|
||||||
|
flash.Seek(0, 0) // rewind back to beginning
|
||||||
|
_, err = flash.Read(saved)
|
||||||
|
checkError(err)
|
||||||
|
println(string(saved))
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
for {
|
||||||
|
println(err.Error())
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
144
src/machine/flash.go
Обычный файл
144
src/machine/flash.go
Обычный файл
|
@ -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()
|
||||||
|
}
|
|
@ -3,7 +3,9 @@
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
|
"encoding/binary"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -382,3 +384,109 @@ func ReadTemperature() int32 {
|
||||||
nrf.TEMP.EVENTS_DATARDY.Set(0)
|
nrf.TEMP.EVENTS_DATARDY.Set(0)
|
||||||
return temp
|
return temp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const memoryStart = 0x0
|
||||||
|
|
||||||
|
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||||
|
var _ BlockDevice = flashBlockDevice{}
|
||||||
|
|
||||||
|
var Flash flashBlockDevice
|
||||||
|
|
||||||
|
type flashBlockDevice struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
data := unsafe.Slice((*byte)(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 double-word (64 bits) length data can be programmed. See rm0461 page 78.
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
address := FlashDataStart() + uintptr(off)
|
||||||
|
padded := f.pad(p)
|
||||||
|
|
||||||
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
|
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
|
||||||
|
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
|
||||||
|
|
||||||
|
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())])
|
||||||
|
address += uintptr(f.WriteBlockSize())
|
||||||
|
waitWhileFlashBusy()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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 {
|
||||||
|
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
|
||||||
|
waitWhileFlashBusy()
|
||||||
|
|
||||||
|
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Een)
|
||||||
|
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
|
||||||
|
|
||||||
|
for i := start; i < start+len; i++ {
|
||||||
|
nrf.NVMC.ERASEPAGE.Set(uint32(address))
|
||||||
|
waitWhileFlashBusy()
|
||||||
|
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 waitWhileFlashBusy() {
|
||||||
|
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,12 @@ import (
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 1024
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
return nrf.GPIO, uint32(p)
|
return nrf.GPIO, uint32(p)
|
||||||
|
|
|
@ -63,3 +63,9 @@ var (
|
||||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 4096
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|
|
@ -84,3 +84,9 @@ var (
|
||||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 4096
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|
|
@ -102,3 +102,9 @@ func (pdm *PDM) Read(buf []int16) (uint32, error) {
|
||||||
|
|
||||||
return uint32(len(buf)), nil
|
return uint32(len(buf)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 4096
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|
122
src/machine/machine_stm32_flash.go
Обычный файл
122
src/machine/machine_stm32_flash.go
Обычный файл
|
@ -0,0 +1,122 @@
|
||||||
|
//go:build stm32f4 || stm32l4 || stm32wlx
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||||
|
var _ BlockDevice = flashBlockDevice{}
|
||||||
|
|
||||||
|
var Flash flashBlockDevice
|
||||||
|
|
||||||
|
type flashBlockDevice struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
data := unsafe.Slice((*byte)(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 double-word (64 bits) length data can be programmed. See rm0461 page 78.
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
unlockFlash()
|
||||||
|
defer lockFlash()
|
||||||
|
|
||||||
|
return writeFlashData(FlashDataStart()+uintptr(off), f.pad(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the number of bytes in this block device.
|
||||||
|
func (f flashBlockDevice) Size() int64 {
|
||||||
|
return int64(FlashDataEnd() - FlashDataStart())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
// It must be a power of two, and may be as small as 1. A typical size is 4096.
|
||||||
|
// TODO: correctly handle processors that have differently sized blocks
|
||||||
|
// in different areas of memory like the STM32F40x and STM32F1x.
|
||||||
|
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 {
|
||||||
|
unlockFlash()
|
||||||
|
defer lockFlash()
|
||||||
|
|
||||||
|
for i := start; i < start+len; i++ {
|
||||||
|
if err := eraseBlock(uint32(i)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
padded := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
|
||||||
|
return append(p, padded...)
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoryStart = 0x08000000
|
||||||
|
|
||||||
|
func unlockFlash() {
|
||||||
|
// keys as described rm0461 page 76
|
||||||
|
var fkey1 uint32 = 0x45670123
|
||||||
|
var fkey2 uint32 = 0xCDEF89AB
|
||||||
|
|
||||||
|
// Wait for the flash memory not to be busy
|
||||||
|
for stm32.FLASH.GetSR_BSY() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the controller is unlocked already
|
||||||
|
if stm32.FLASH.GetCR_LOCK() != 0 {
|
||||||
|
// Write the first key
|
||||||
|
stm32.FLASH.SetKEYR(fkey1)
|
||||||
|
// Write the second key
|
||||||
|
stm32.FLASH.SetKEYR(fkey2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockFlash() {
|
||||||
|
stm32.FLASH.SetCR_LOCK(1)
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
|
@ -791,3 +793,142 @@ func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------- Flash related code
|
||||||
|
|
||||||
|
// the block size actually depends on the sector.
|
||||||
|
// TODO: handle this correctly for sectors > 3
|
||||||
|
const eraseBlockSizeValue = 16384
|
||||||
|
|
||||||
|
// see RM0090 page 75
|
||||||
|
func sectorNumber(address uintptr) uint32 {
|
||||||
|
switch {
|
||||||
|
// 0x0800 0000 - 0x0800 3FFF
|
||||||
|
case address >= 0x08000000 && address <= 0x08003FFF:
|
||||||
|
return 0
|
||||||
|
// 0x0800 4000 - 0x0800 7FFF
|
||||||
|
case address >= 0x08004000 && address <= 0x08007FFF:
|
||||||
|
return 1
|
||||||
|
// 0x0800 8000 - 0x0800 BFFF
|
||||||
|
case address >= 0x08008000 && address <= 0x0800BFFF:
|
||||||
|
return 2
|
||||||
|
// 0x0800 C000 - 0x0800 FFFF
|
||||||
|
case address >= 0x0800C000 && address <= 0x0800FFFF:
|
||||||
|
return 3
|
||||||
|
// 0x0801 0000 - 0x0801 FFFF
|
||||||
|
case address >= 0x08010000 && address <= 0x0801FFFF:
|
||||||
|
return 4
|
||||||
|
// 0x0802 0000 - 0x0803 FFFF
|
||||||
|
case address >= 0x08020000 && address <= 0x0803FFFF:
|
||||||
|
return 5
|
||||||
|
// 0x0804 0000 - 0x0805 FFFF
|
||||||
|
case address >= 0x08040000 && address <= 0x0805FFFF:
|
||||||
|
return 6
|
||||||
|
case address >= 0x08060000 && address <= 0x0807FFFF:
|
||||||
|
return 7
|
||||||
|
case address >= 0x08080000 && address <= 0x0809FFFF:
|
||||||
|
return 8
|
||||||
|
case address >= 0x080A0000 && address <= 0x080BFFFF:
|
||||||
|
return 9
|
||||||
|
case address >= 0x080C0000 && address <= 0x080DFFFF:
|
||||||
|
return 10
|
||||||
|
case address >= 0x080E0000 && address <= 0x080FFFFF:
|
||||||
|
return 11
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate sector number from address
|
||||||
|
// var sector uint32 = sectorNumber(address)
|
||||||
|
|
||||||
|
// see RM0090 page 85
|
||||||
|
// eraseBlock at the passed in block number
|
||||||
|
func eraseBlock(block uint32) error {
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
// clear any previous errors
|
||||||
|
stm32.FLASH.SR.SetBits(0xF0)
|
||||||
|
|
||||||
|
// set SER bit
|
||||||
|
stm32.FLASH.SetCR_SER(1)
|
||||||
|
defer stm32.FLASH.SetCR_SER(0)
|
||||||
|
|
||||||
|
// set the block (aka sector) to be erased
|
||||||
|
stm32.FLASH.SetCR_SNB(block)
|
||||||
|
defer stm32.FLASH.SetCR_SNB(0)
|
||||||
|
|
||||||
|
// start the page erase
|
||||||
|
stm32.FLASH.SetCR_STRT(1)
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
if err := checkError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const writeBlockSize = 2
|
||||||
|
|
||||||
|
// see RM0090 page 86
|
||||||
|
// must write data in word-length
|
||||||
|
func writeFlashData(address uintptr, data []byte) (int, error) {
|
||||||
|
if len(data)%writeBlockSize != 0 {
|
||||||
|
return 0, errFlashInvalidWriteLength
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
// clear any previous errors
|
||||||
|
stm32.FLASH.SR.SetBits(0xF0)
|
||||||
|
|
||||||
|
// set parallelism to x32
|
||||||
|
stm32.FLASH.SetCR_PSIZE(2)
|
||||||
|
|
||||||
|
for i := 0; i < len(data); i += writeBlockSize {
|
||||||
|
// start write operation
|
||||||
|
stm32.FLASH.SetCR_PG(1)
|
||||||
|
|
||||||
|
*(*uint16)(unsafe.Pointer(address)) = binary.BigEndian.Uint16(data[i : i+writeBlockSize])
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
if err := checkError(); err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// end write operation
|
||||||
|
stm32.FLASH.SetCR_PG(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitUntilFlashDone() {
|
||||||
|
for stm32.FLASH.GetSR_BSY() != 0 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFlashPGS = errors.New("errFlashPGS")
|
||||||
|
errFlashPGP = errors.New("errFlashPGP")
|
||||||
|
errFlashPGA = errors.New("errFlashPGA")
|
||||||
|
errFlashWRP = errors.New("errFlashWRP")
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkError() error {
|
||||||
|
switch {
|
||||||
|
case stm32.FLASH.GetSR_PGSERR() != 0:
|
||||||
|
return errFlashPGS
|
||||||
|
case stm32.FLASH.GetSR_PGPERR() != 0:
|
||||||
|
return errFlashPGP
|
||||||
|
case stm32.FLASH.GetSR_PGAERR() != 0:
|
||||||
|
return errFlashPGA
|
||||||
|
case stm32.FLASH.GetSR_WRPERR() != 0:
|
||||||
|
return errFlashWRP
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -543,3 +545,104 @@ func initRNG() {
|
||||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
|
||||||
stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN)
|
stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------- Flash related code
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 2048
|
||||||
|
|
||||||
|
// see RM0394 page 83
|
||||||
|
// eraseBlock of the passed in block number
|
||||||
|
func eraseBlock(block uint32) error {
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
// clear any previous errors
|
||||||
|
stm32.FLASH.SR.SetBits(0x3FA)
|
||||||
|
|
||||||
|
// page erase operation
|
||||||
|
stm32.FLASH.SetCR_PER(1)
|
||||||
|
defer stm32.FLASH.SetCR_PER(0)
|
||||||
|
|
||||||
|
// set the page to be erased
|
||||||
|
stm32.FLASH.SetCR_PNB(block)
|
||||||
|
|
||||||
|
// start the page erase
|
||||||
|
stm32.FLASH.SetCR_START(1)
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
if err := checkError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const writeBlockSize = 8
|
||||||
|
|
||||||
|
// see RM0394 page 84
|
||||||
|
// It is only possible to program double word (2 x 32-bit data).
|
||||||
|
func writeFlashData(address uintptr, data []byte) (int, error) {
|
||||||
|
if len(data)%writeBlockSize != 0 {
|
||||||
|
return 0, errFlashInvalidWriteLength
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
// clear any previous errors
|
||||||
|
stm32.FLASH.SR.SetBits(0x3FA)
|
||||||
|
|
||||||
|
for j := 0; j < len(data); j += writeBlockSize {
|
||||||
|
// start page write operation
|
||||||
|
stm32.FLASH.SetCR_PG(1)
|
||||||
|
|
||||||
|
// write first word using double-word low order word
|
||||||
|
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
|
||||||
|
|
||||||
|
address += writeBlockSize / 2
|
||||||
|
|
||||||
|
// write second word using double-word high order word
|
||||||
|
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
if err := checkError(); err != nil {
|
||||||
|
return j, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// end flash write
|
||||||
|
stm32.FLASH.SetCR_PG(0)
|
||||||
|
address += writeBlockSize / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitUntilFlashDone() {
|
||||||
|
for stm32.FLASH.GetSR_BSY() != 0 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFlashPGS = errors.New("errFlashPGS")
|
||||||
|
errFlashSIZE = errors.New("errFlashSIZE")
|
||||||
|
errFlashPGA = errors.New("errFlashPGA")
|
||||||
|
errFlashWRP = errors.New("errFlashWRP")
|
||||||
|
errFlashPROG = errors.New("errFlashPROG")
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkError() error {
|
||||||
|
switch {
|
||||||
|
case stm32.FLASH.GetSR_PGSERR() != 0:
|
||||||
|
return errFlashPGS
|
||||||
|
case stm32.FLASH.GetSR_SIZERR() != 0:
|
||||||
|
return errFlashSIZE
|
||||||
|
case stm32.FLASH.GetSR_PGAERR() != 0:
|
||||||
|
return errFlashPGA
|
||||||
|
case stm32.FLASH.GetSR_WRPERR() != 0:
|
||||||
|
return errFlashWRP
|
||||||
|
case stm32.FLASH.GetSR_PROGERR() != 0:
|
||||||
|
return errFlashPROG
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
|
@ -424,3 +426,115 @@ const (
|
||||||
ARR_MAX = 0x10000
|
ARR_MAX = 0x10000
|
||||||
PSC_MAX = 0x10000
|
PSC_MAX = 0x10000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//---------- Flash related code
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 2048
|
||||||
|
|
||||||
|
// eraseBlock of the passed in block number
|
||||||
|
func eraseBlock(block uint32) error {
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
// check if operation is allowed.
|
||||||
|
if stm32.FLASH.GetSR_PESD() != 0 {
|
||||||
|
return errFlashCannotErasePage
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear any previous errors
|
||||||
|
stm32.FLASH.SR.SetBits(0x3FA)
|
||||||
|
|
||||||
|
// page erase operation
|
||||||
|
stm32.FLASH.SetCR_PER(1)
|
||||||
|
defer stm32.FLASH.SetCR_PER(0)
|
||||||
|
|
||||||
|
// set the address to the page to be written
|
||||||
|
stm32.FLASH.SetCR_PNB(block)
|
||||||
|
defer stm32.FLASH.SetCR_PNB(0)
|
||||||
|
|
||||||
|
// start the page erase
|
||||||
|
stm32.FLASH.SetCR_STRT(1)
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
if err := checkError(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const writeBlockSize = 8
|
||||||
|
|
||||||
|
func writeFlashData(address uintptr, data []byte) (int, error) {
|
||||||
|
if len(data)%writeBlockSize != 0 {
|
||||||
|
return 0, errFlashInvalidWriteLength
|
||||||
|
}
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
// check if operation is allowed
|
||||||
|
if stm32.FLASH.GetSR_PESD() != 0 {
|
||||||
|
return 0, errFlashNotAllowedWriteData
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear any previous errors
|
||||||
|
stm32.FLASH.SR.SetBits(0x3FA)
|
||||||
|
|
||||||
|
for j := 0; j < len(data); j += writeBlockSize {
|
||||||
|
// start page write operation
|
||||||
|
stm32.FLASH.SetCR_PG(1)
|
||||||
|
|
||||||
|
// write first word using double-word low order word
|
||||||
|
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
|
||||||
|
|
||||||
|
address += writeBlockSize / 2
|
||||||
|
|
||||||
|
// write second word using double-word high order word
|
||||||
|
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
|
||||||
|
|
||||||
|
waitUntilFlashDone()
|
||||||
|
|
||||||
|
if err := checkError(); err != nil {
|
||||||
|
return j, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// end flash write
|
||||||
|
stm32.FLASH.SetCR_PG(0)
|
||||||
|
address += writeBlockSize / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitUntilFlashDone() {
|
||||||
|
for stm32.FLASH.GetSR_BSY() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
for stm32.FLASH.GetSR_CFGBSY() != 0 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFlashPGS = errors.New("errFlashPGS")
|
||||||
|
errFlashSIZE = errors.New("errFlashSIZE")
|
||||||
|
errFlashPGA = errors.New("errFlashPGA")
|
||||||
|
errFlashWRP = errors.New("errFlashWRP")
|
||||||
|
errFlashPROG = errors.New("errFlashPROG")
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkError() error {
|
||||||
|
switch {
|
||||||
|
case stm32.FLASH.GetSR_PGSERR() != 0:
|
||||||
|
return errFlashPGS
|
||||||
|
case stm32.FLASH.GetSR_SIZERR() != 0:
|
||||||
|
return errFlashSIZE
|
||||||
|
case stm32.FLASH.GetSR_PGAERR() != 0:
|
||||||
|
return errFlashPGA
|
||||||
|
case stm32.FLASH.GetSR_WRPERR() != 0:
|
||||||
|
return errFlashWRP
|
||||||
|
case stm32.FLASH.GetSR_PROGERR() != 0:
|
||||||
|
return errFlashPROG
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -69,3 +69,7 @@ _heap_start = _ebss;
|
||||||
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
|
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
_globals_start = _sdata;
|
_globals_start = _sdata;
|
||||||
_globals_end = _ebss;
|
_globals_end = _ebss;
|
||||||
|
|
||||||
|
/* For the flash API */
|
||||||
|
__flash_data_start = LOADADDR(.data) + SIZEOF(.data);
|
||||||
|
__flash_data_end = ORIGIN(FLASH_TEXT) + LENGTH(FLASH_TEXT);
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче