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 }