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  }