126 строки
3,5 КиБ
Go
126 строки
3,5 КиБ
Go
//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.
|
|
// Note that block 0 should map to the address of FlashDataStart().
|
|
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
|
|
var address uintptr = uintptr(start*f.EraseBlockSize()) + FlashDataStart()
|
|
blk := int64(address-uintptr(memoryStart)) / f.EraseBlockSize()
|
|
|
|
unlockFlash()
|
|
defer lockFlash()
|
|
|
|
for i := blk; i < blk+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 {
|
|
overflow := int64(len(p)) % f.WriteBlockSize()
|
|
if overflow == 0 {
|
|
return p
|
|
}
|
|
|
|
padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow))
|
|
return append(p, padding...)
|
|
}
|
|
|
|
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)
|
|
}
|