mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 02:27:48 +00:00
machine/esp32c3: implement BlockDevice for esp32c3 flash
- Add `machine_esp32c3_flash.go` to implement the `BlockDevice` interface for ESP32-C3. - Map internal ESP ROM SPI flash and cache invalidation functions via CGo. - Update `targets/esp32c3.ld` to expose `__flash_data_start` and `__flash_data_end` linker variables. - Add `esp32c3` to build tags in `src/machine/flash.go`. - Ensure atomic flash operations by disabling interrupts and invalidating cache to prevent stale reads. Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
//go:build esp32c3
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
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
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user