github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_flash.go (about) 1 //go:build rp2040 2 3 package machine 4 5 import ( 6 "bytes" 7 "unsafe" 8 ) 9 10 // EnterBootloader should perform a system reset in preparation 11 // to switch to the bootloader to flash new firmware. 12 func EnterBootloader() { 13 enterBootloader() 14 } 15 16 // 13 = 1 + FLASH_RUID_DUMMY_BYTES(4) + FLASH_RUID_DATA_BYTES(8) 17 var deviceIDBuf [13]byte 18 19 // DeviceID returns an identifier that is unique within 20 // a particular chipset. 21 // 22 // The identity is one burnt into the MCU itself, or the 23 // flash chip at time of manufacture. 24 // 25 // It's possible that two different vendors may allocate 26 // the same DeviceID, so callers should take this into 27 // account if needing to generate a globally unique id. 28 // 29 // The length of the hardware ID is vendor-specific, but 30 // 8 bytes (64 bits) is common. 31 func DeviceID() []byte { 32 deviceIDBuf[0] = 0x4b // FLASH_RUID_CMD 33 34 err := doFlashCommand(deviceIDBuf[:], deviceIDBuf[:]) 35 if err != nil { 36 panic(err) 37 } 38 39 return deviceIDBuf[5:13] 40 } 41 42 // compile-time check for ensuring we fulfill BlockDevice interface 43 var _ BlockDevice = flashBlockDevice{} 44 45 var Flash flashBlockDevice 46 47 type flashBlockDevice struct { 48 } 49 50 // ReadAt reads the given number of bytes from the block device. 51 func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) { 52 if readAddress(off) > FlashDataEnd() { 53 return 0, errFlashCannotReadPastEOF 54 } 55 56 data := unsafe.Slice((*byte)(unsafe.Pointer(readAddress(off))), len(p)) 57 copy(p, data) 58 59 return len(p), nil 60 } 61 62 // WriteAt writes the given number of bytes to the block device. 63 // Only word (32 bits) length data can be programmed. 64 // If the length of p is not long enough it will be padded with 0xFF bytes. 65 // This method assumes that the destination is already erased. 66 func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) { 67 return f.writeAt(p, off) 68 } 69 70 // Size returns the number of bytes in this block device. 71 func (f flashBlockDevice) Size() int64 { 72 return int64(FlashDataEnd() - FlashDataStart()) 73 } 74 75 const writeBlockSize = 1 << 8 76 77 // WriteBlockSize returns the block size in which data can be written to 78 // memory. It can be used by a client to optimize writes, non-aligned writes 79 // should always work correctly. 80 func (f flashBlockDevice) WriteBlockSize() int64 { 81 return writeBlockSize 82 } 83 84 const eraseBlockSizeValue = 1 << 12 85 86 func eraseBlockSize() int64 { 87 return eraseBlockSizeValue 88 } 89 90 // EraseBlockSize returns the smallest erasable area on this particular chip 91 // in bytes. This is used for the block size in EraseBlocks. 92 func (f flashBlockDevice) EraseBlockSize() int64 { 93 return eraseBlockSize() 94 } 95 96 // EraseBlocks erases the given number of blocks. An implementation may 97 // transparently coalesce ranges of blocks into larger bundles if the chip 98 // supports this. The start and len parameters are in block numbers, use 99 // EraseBlockSize to map addresses to blocks. 100 func (f flashBlockDevice) EraseBlocks(start, length int64) error { 101 return f.eraseBlocks(start, length) 102 } 103 104 // pad data if needed so it is long enough for correct byte alignment on writes. 105 func (f flashBlockDevice) pad(p []byte) []byte { 106 overflow := int64(len(p)) % f.WriteBlockSize() 107 if overflow == 0 { 108 return p 109 } 110 111 padding := bytes.Repeat([]byte{0xff}, int(f.WriteBlockSize()-overflow)) 112 return append(p, padding...) 113 } 114 115 // return the correct address to be used for write 116 func writeAddress(off int64) uintptr { 117 return readAddress(off) - uintptr(memoryStart) 118 } 119 120 // return the correct address to be used for reads 121 func readAddress(off int64) uintptr { 122 return FlashDataStart() + uintptr(off) 123 }