diff --git a/src/machine/flash.go b/src/machine/flash.go index bfbb28794..bcbc64e12 100644 --- a/src/machine/flash.go +++ b/src/machine/flash.go @@ -1,4 +1,4 @@ -//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350 +//go:build esp32c3 || nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350 package machine diff --git a/src/machine/machine_esp32c3_flash.go b/src/machine/machine_esp32c3_flash.go new file mode 100644 index 000000000..5229abfb4 --- /dev/null +++ b/src/machine/machine_esp32c3_flash.go @@ -0,0 +1,137 @@ +//go:build esp32c3 + +package machine + +import ( + "runtime/interrupt" + "unsafe" +) + +/* +#include +extern int esp_rom_spiflash_read(uint32_t src_addr, uint32_t *data, uint32_t len); +extern int esp_rom_spiflash_write(uint32_t dest_addr, const uint32_t *data, uint32_t len); +extern int esp_rom_spiflash_erase_sector(uint32_t sector_num); +extern int esp_rom_spiflash_unlock(void); +extern void Cache_Invalidate_Addr(uint32_t addr, uint32_t size); +*/ +import "C" + +// 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 readAddress(off)+uintptr(len(p)) > FlashDataEnd() { + return 0, errFlashCannotReadPastEOF + } + + data := unsafe.Slice((*byte)(unsafe.Add(unsafe.Pointer(FlashDataStart()), off)), len(p)) + copy(p, data) + + return len(p), nil +} + +// WriteAt writes the given number of bytes to the block device. +// Only word (32 bits) length data can be programmed. +// 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) { + return f.writeAt(p, off) +} + +// Size returns the number of bytes in this block device. +func (f flashBlockDevice) Size() int64 { + return int64(FlashDataEnd() - FlashDataStart()) +} + +const writeBlockSize = 4 + +// 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 +} + +const eraseBlockSizeValue = 1 << 12 + +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. +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. +func (f flashBlockDevice) EraseBlocks(start, length int64) error { + return f.eraseBlocks(start, length) +} + +// return the correct address to be used for reads +func readAddress(off int64) uintptr { + return FlashDataStart() + uintptr(off) +} + +const flashDROMStart = 0x3C000000 + +// return the correct physical address to be used for write/erase +func writeAddress(off int64) uint32 { + // DROM maps 1:1 with flash physical offset, starting at 0x3C000000. + return uint32(readAddress(off) - flashDROMStart) +} + +func (f flashBlockDevice) writeAt(p []byte, off int64) (n int, err error) { + if readAddress(off)+uintptr(len(p)) > FlashDataEnd() { + return 0, errFlashCannotWritePastEOF + } + + address := writeAddress(off) + padded := flashPad(p, int(f.WriteBlockSize())) + + state := interrupt.Disable() + defer interrupt.Restore(state) + + C.esp_rom_spiflash_unlock() + res := C.esp_rom_spiflash_write(C.uint32_t(address), (*C.uint32_t)(unsafe.Pointer(&padded[0])), C.uint32_t(len(padded))) + C.Cache_Invalidate_Addr(C.uint32_t(readAddress(off)), C.uint32_t(len(padded))) + if res != 0 { + return 0, errFlashCannotWriteData + } + + return len(padded), nil +} + +func (f flashBlockDevice) eraseBlocks(start, length int64) error { + address := writeAddress(start * f.EraseBlockSize()) + if uintptr(unsafe.Add(unsafe.Pointer(uintptr(address)+flashDROMStart), length*f.EraseBlockSize())) > FlashDataEnd() { + return errFlashCannotErasePastEOF + } + + state := interrupt.Disable() + defer interrupt.Restore(state) + + C.esp_rom_spiflash_unlock() + sector := address / uint32(f.EraseBlockSize()) + + for i := int64(0); i < length; i++ { + res := C.esp_rom_spiflash_erase_sector(C.uint32_t(sector + uint32(i))) + C.Cache_Invalidate_Addr(C.uint32_t(readAddress((start+i)*f.EraseBlockSize())), C.uint32_t(f.EraseBlockSize())) + if res != 0 { + return errFlashCannotErasePage + } + } + + return nil +} diff --git a/targets/esp32c3.ld b/targets/esp32c3.ld index 4bdc72c83..a5c09182a 100644 --- a/targets/esp32c3.ld +++ b/targets/esp32c3.ld @@ -161,8 +161,12 @@ SECTIONS *(.text.exception_vectors) . = ALIGN (4); *(.text .text.*) + _irom_end = .; } >IROM + PROVIDE(__flash_data_start = ALIGN(_irom_end - 0x42000000 + 0x3C000000, 4096)); + PROVIDE(__flash_data_end = 0x3C000000 + 4M); + /DISCARD/ : { *(.eh_frame) /* causes 'no memory region specified' error in lld */