mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"device/nrf"
|
||||
"encoding/binary"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -382,3 +384,109 @@ func ReadTemperature() int32 {
|
||||
nrf.TEMP.EVENTS_DATARDY.Set(0)
|
||||
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"
|
||||
)
|
||||
|
||||
const eraseBlockSizeValue = 1024
|
||||
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
return nrf.GPIO, uint32(p)
|
||||
|
||||
@@ -63,3 +63,9 @@ var (
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
)
|
||||
|
||||
const eraseBlockSizeValue = 4096
|
||||
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
@@ -84,3 +84,9 @@ var (
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
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
|
||||
}
|
||||
|
||||
const eraseBlockSizeValue = 4096
|
||||
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
"device/stm32"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
"runtime/interrupt"
|
||||
"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 (
|
||||
"device/stm32"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
@@ -543,3 +545,104 @@ func initRNG() {
|
||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_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 (
|
||||
"device/stm32"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
@@ -424,3 +426,115 @@ const (
|
||||
ARR_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);
|
||||
_globals_start = _sdata;
|
||||
_globals_end = _ebss;
|
||||
|
||||
/* For the flash API */
|
||||
__flash_data_start = LOADADDR(.data) + SIZEOF(.data);
|
||||
__flash_data_end = ORIGIN(FLASH_TEXT) + LENGTH(FLASH_TEXT);
|
||||
|
||||
Reference in New Issue
Block a user