github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_stm32_flash.go (about)

     1  //go:build stm32f4 || stm32l4 || stm32wlx
     2  
     3  package machine
     4  
     5  import (
     6  	"device/stm32"
     7  
     8  	"bytes"
     9  	"unsafe"
    10  )
    11  
    12  // compile-time check for ensuring we fulfill BlockDevice interface
    13  var _ BlockDevice = flashBlockDevice{}
    14  
    15  var Flash flashBlockDevice
    16  
    17  type flashBlockDevice struct {
    18  }
    19  
    20  // ReadAt reads the given number of bytes from the block device.
    21  func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
    22  	if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
    23  		return 0, errFlashCannotReadPastEOF
    24  	}
    25  
    26  	data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
    27  	copy(p, data)
    28  
    29  	return len(p), nil
    30  }
    31  
    32  // WriteAt writes the given number of bytes to the block device.
    33  // Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
    34  // If the length of p is not long enough it will be padded with 0xFF bytes.
    35  // This method assumes that the destination is already erased.
    36  func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
    37  	if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
    38  		return 0, errFlashCannotWritePastEOF
    39  	}
    40  
    41  	unlockFlash()
    42  	defer lockFlash()
    43  
    44  	return writeFlashData(FlashDataStart()+uintptr(off), f.pad(p))
    45  }
    46  
    47  // Size returns the number of bytes in this block device.
    48  func (f flashBlockDevice) Size() int64 {
    49  	return int64(FlashDataEnd() - FlashDataStart())
    50  }
    51  
    52  // WriteBlockSize returns the block size in which data can be written to
    53  // memory. It can be used by a client to optimize writes, non-aligned writes
    54  // should always work correctly.
    55  func (f flashBlockDevice) WriteBlockSize() int64 {
    56  	return writeBlockSize
    57  }
    58  
    59  func eraseBlockSize() int64 {
    60  	return eraseBlockSizeValue
    61  }
    62  
    63  // EraseBlockSize returns the smallest erasable area on this particular chip
    64  // in bytes. This is used for the block size in EraseBlocks.
    65  // It must be a power of two, and may be as small as 1. A typical size is 4096.
    66  // TODO: correctly handle processors that have differently sized blocks
    67  // in different areas of memory like the STM32F40x and STM32F1x.
    68  func (f flashBlockDevice) EraseBlockSize() int64 {
    69  	return eraseBlockSize()
    70  }
    71  
    72  // EraseBlocks erases the given number of blocks. An implementation may
    73  // transparently coalesce ranges of blocks into larger bundles if the chip
    74  // supports this. The start and len parameters are in block numbers, use
    75  // EraseBlockSize to map addresses to blocks.
    76  // Note that block 0 should map to the address of FlashDataStart().
    77  func (f flashBlockDevice) EraseBlocks(start, len int64) error {
    78  	var address uintptr = uintptr(start*f.EraseBlockSize()) + FlashDataStart()
    79  	blk := int64(address-uintptr(memoryStart)) / f.EraseBlockSize()
    80  
    81  	unlockFlash()
    82  	defer lockFlash()
    83  
    84  	for i := blk; i < blk+len; i++ {
    85  		if err := eraseBlock(uint32(i)); err != nil {
    86  			return err
    87  		}
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // pad data if needed so it is long enough for correct byte alignment on writes.
    94  func (f flashBlockDevice) pad(p []byte) []byte {
    95  	overflow := int64(len(p)) % f.WriteBlockSize()
    96  	if overflow == 0 {
    97  		return p
    98  	}
    99  
   100  	padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow))
   101  	return append(p, padding...)
   102  }
   103  
   104  const memoryStart = 0x08000000
   105  
   106  func unlockFlash() {
   107  	// keys as described rm0461 page 76
   108  	var fkey1 uint32 = 0x45670123
   109  	var fkey2 uint32 = 0xCDEF89AB
   110  
   111  	// Wait for the flash memory not to be busy
   112  	for stm32.FLASH.GetSR_BSY() != 0 {
   113  	}
   114  
   115  	// Check if the controller is unlocked already
   116  	if stm32.FLASH.GetCR_LOCK() != 0 {
   117  		// Write the first key
   118  		stm32.FLASH.SetKEYR(fkey1)
   119  		// Write the second key
   120  		stm32.FLASH.SetKEYR(fkey2)
   121  	}
   122  }
   123  
   124  func lockFlash() {
   125  	stm32.FLASH.SetCR_LOCK(1)
   126  }