diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index 661ec2c8b4..c8535d8300 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -213,6 +213,10 @@ else
# arm_atsam EEPROM
OPT_DEFS += -DEEPROM_SAMD
SRC += $(PLATFORM_COMMON_DIR)/eeprom_samd.c
+ SRC += $(PLATFORM_COMMON_DIR)/eeprom.c
+ else ifeq ($(PLATFORM),PICO_SDK)
+ SRC += $(PLATFORM_COMMON_DIR)/eeprom.c
+ SRC += $(PLATFORM_COMMON_DIR)/flash_pico.c
else ifeq ($(PLATFORM),TEST)
# Test harness "EEPROM"
OPT_DEFS += -DEEPROM_TEST_HARNESS
diff --git a/keyboards/rp2040_example/keymaps/adc/keymap.c b/keyboards/rp2040_example/keymaps/adc/keymap.c
index 2ef00700fd..88dc046c1c 100644
--- a/keyboards/rp2040_example/keymaps/adc/keymap.c
+++ b/keyboards/rp2040_example/keymaps/adc/keymap.c
@@ -59,18 +59,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// when keycode QMKURL is released
}
break;
-
- case KC_FN0:
- if (record->event.pressed) {
- printf("save keymap to eeprom\n");
- pico_eepemu_flash_dynamic_keymap();
- printf("complete\n");
- printf("save eeconfig to eeprom\n");
- pico_eepemu_flash_eeconfig();
- printf("complete\n");
- }
- return false;
- break;
}
return true;
}
diff --git a/keyboards/rp2040_example/keymaps/default/keymap.c b/keyboards/rp2040_example/keymaps/default/keymap.c
index 3cd7c597f6..eda75e2ed0 100644
--- a/keyboards/rp2040_example/keymaps/default/keymap.c
+++ b/keyboards/rp2040_example/keymaps/default/keymap.c
@@ -14,7 +14,6 @@
* along with this program. If not, see .
*/
#include QMK_KEYBOARD_H
-#include "pico_eeprom.h"
// Defines names for use in layer keycodes and the keymap
enum layer_names {
@@ -58,18 +57,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// when keycode QMKURL is released
}
break;
-
- case KC_FN0:
- if (record->event.pressed) {
- printf("save keymap to eeprom\n");
- pico_eepemu_flash_dynamic_keymap();
- printf("complete\n");
- printf("save eeconfig to eeprom\n");
- pico_eepemu_flash_eeconfig();
- printf("complete\n");
- }
- return false;
- break;
}
return true;
}
diff --git a/keyboards/rp2040_example/keymaps/rgblight/keymap.c b/keyboards/rp2040_example/keymaps/rgblight/keymap.c
index a4479f7a38..1bcae85fc9 100644
--- a/keyboards/rp2040_example/keymaps/rgblight/keymap.c
+++ b/keyboards/rp2040_example/keymaps/rgblight/keymap.c
@@ -58,18 +58,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// when keycode QMKURL is released
}
break;
-
- case KC_FN0:
- if (record->event.pressed) {
- printf("save keymap to eeprom\n");
- pico_eepemu_flash_dynamic_keymap();
- printf("complete\n");
- printf("save eeconfig to eeprom\n");
- pico_eepemu_flash_eeconfig();
- printf("complete\n");
- }
- return false;
- break;
}
return true;
}
diff --git a/keyboards/rp2040_split/keymaps/default/keymap.c b/keyboards/rp2040_split/keymaps/default/keymap.c
index 60a2b5f472..8ab9ad498e 100644
--- a/keyboards/rp2040_split/keymaps/default/keymap.c
+++ b/keyboards/rp2040_split/keymaps/default/keymap.c
@@ -14,7 +14,6 @@
* along with this program. If not, see .
*/
#include QMK_KEYBOARD_H
-#include "pico_eeprom.h"
// Defines names for use in layer keycodes and the keymap
enum layer_names {
@@ -62,18 +61,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// when keycode QMKURL is released
}
break;
-
- case KC_FN0:
- if (record->event.pressed) {
- printf("save keymap to eeprom\n");
- pico_eepemu_flash_dynamic_keymap();
- printf("complete\n");
- printf("save eeconfig to eeprom\n");
- pico_eepemu_flash_eeconfig();
- printf("complete\n");
- }
- return false;
- break;
}
return true;
}
diff --git a/platforms/pico/eeprom.c b/platforms/pico/eeprom.c
index 56f90431d9..f222882e62 100644
--- a/platforms/pico/eeprom.c
+++ b/platforms/pico/eeprom.c
@@ -1,272 +1,748 @@
+/*
+ * This software is experimental and a work in progress.
+ * Under no circumstances should these files be used in relation to any critical system(s).
+ * Use of these files is at your own risk.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
+ * Artur F.
+ *
+ * Modifications for QMK and STM32F303 by Yiancar
+ * Modifications to add flash wear leveling by Ilya Zhuravlev
+ * Modifications to increase flash density by Don Kjer
+ * Modifications for QMK and RP2040 by sekigon-gonnoc
+ */
-#include
#include
-
-// qmk
-#include "eeprom.h"
-#include "eeconfig.h"
-#include "timer.h"
+#include
+#include "util.h"
#include "debug.h"
-#include "print.h"
+#include "platforms/pico/flash_pico.h"
+#include "hardware/watchdog.h"
-#include "pico_eeprom.h"
+/*
+ * We emulate eeprom by writing a snapshot compacted view of eeprom contents,
+ * followed by a write log of any change since that snapshot:
+ *
+ * === SIMULATED EEPROM CONTENTS ===
+ *
+ * ┌─ Compacted ┬ Write Log ─┐
+ * │............│[BYTE][BYTE]│
+ * │FFFF....FFFF│[WRD0][WRD1]│
+ * │FFFFFFFFFFFF│[WORD][NEXT]│
+ * │....FFFFFFFF│[BYTE][WRD0]│
+ * ├────────────┼────────────┤
+ * └──PAGE_BASE │ │
+ * PAGE_LAST─┴─WRITE_BASE │
+ * WRITE_LAST ┘
+ *
+ * Compacted contents are the 1's complement of the actual EEPROM contents.
+ * e.g. An 'FFFF' represents a '0000' value.
+ *
+ * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
+ * The size of the compacted-area and write log are configurable, and the combined
+ * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
+ * Simulated Eeprom contents are located at the end of available flash space.
+ *
+ * The following configuration defines can be set:
+ *
+ * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log)
+ * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
+ * NOTE: The current implementation does not include page swapping,
+ * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
+ *
+ * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
+ * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
+ * The larger the write log, the less frequently the compacted area needs to be rewritten.
+ *
+ *
+ * *** General Algorithm ***
+ *
+ * During initialization:
+ * The contents of the Compacted-flash area are loaded and the 1's complement value
+ * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
+ * Write log entries are processed until a 0xFFFF is reached.
+ * Each log entry updates a byte or word in the cache.
+ *
+ * During reads:
+ * EEPROM contents are given back directly from the cache in memory.
+ *
+ * During writes:
+ * The contents of the cache is updated first.
+ * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash
+ * Otherwise:
+ * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.
+ * Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
+ *
+ *
+ * *** Write Log Structure ***
+ *
+ * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
+ *
+ * === WRITE LOG ENTRY FORMATS ===
+ *
+ * ╔═══ Byte-Entry ══╗
+ * ║0XXXXXXX║YYYYYYYY║
+ * ║ └──┬──┘║└──┬───┘║
+ * ║ Address║ Value ║
+ * ╚════════╩════════╝
+ * 0 <= Address < 0x80 (128)
+ *
+ * ╔ Word-Encoded 0 ╗
+ * ║100XXXXXXXXXXXXX║
+ * ║ │└─────┬─────┘║
+ * ║ │Address >> 1 ║
+ * ║ └── Value: 0 ║
+ * ╚════════════════╝
+ * 0 <= Address <= 0x3FFE (16382)
+ *
+ * ╔ Word-Encoded 1 ╗
+ * ║101XXXXXXXXXXXXX║
+ * ║ │└─────┬─────┘║
+ * ║ │Address >> 1 ║
+ * ║ └── Value: 1 ║
+ * ╚════════════════╝
+ * 0 <= Address <= 0x3FFE (16382)
+ *
+ * ╔═══ Reserved ═══╗
+ * ║110XXXXXXXXXXXXX║
+ * ╚════════════════╝
+ *
+ * ╔═══════════ Word-Next ═══════════╗
+ * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
+ * ║ └─────┬─────┘║└───────┬──────┘║
+ * ║(Address-128)>>1║ ~Value ║
+ * ╚════════════════╩════════════════╝
+ * ( 0 <= Address < 0x0080 (128): Reserved)
+ * 0x80 <= Address <= 0x3FFE (16382)
+ *
+ * Write Log entry ranges:
+ * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
+ * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
+ * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
+ * 0xC000 ... 0xDFFF - Reserved
+ * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
+ * 0xFFC0 ... 0xFFFE - Reserved
+ * 0xFFFF - Unprogrammed
+ *
+ */
-// pico
-#include "pico/stdlib.h"
-#include "hardware/flash.h"
-#include "hardware/sync.h"
+#include "eeprom_pico_defs.h"
+#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
+# error "not implemented."
+#endif
-#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
-# ifdef VIA_EEPROM_CUSTOM_CONFIG_ADDR
-# define DYNAMIC_KEYMAP_EEPROM_ADDR \
- (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE)
-# else
-# define DYNAMIC_KEYMAP_EEPROM_ADDR (EECONFIG_SIZE + 1)
+/* These bits are used for optimizing encoding of bytes, 0 and 1 */
+#define FEE_WORD_ENCODING 0x8000
+#define FEE_VALUE_NEXT 0x6000
+#define FEE_VALUE_RESERVED 0x4000
+#define FEE_VALUE_ENCODED 0x2000
+#define FEE_BYTE_RANGE 0x80
+
+/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */
+#define FEE_ADDRESS_MAX_SIZE 0x4000
+
+/* Flash word value after erase */
+#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
+
+/* Magic number indicating the page is used for FEE */
+#define FEE_MAGIC_DWORD ((uint32_t)0x20400FEE)
+
+/* Size of combined compacted eeprom and write log pages */
+#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)
+
+#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */
+# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)
+# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024)
+# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size
# endif
#endif
-#ifndef EEPEMU_EECONFIG_SIZE
-# define EEPEMU_EECONFIG_SIZE 1024
-#endif
-_Static_assert(EEPEMU_EECONFIG_SIZE > EECONFIG_SIZE,
- "EEPEMU_EECONFIG Region is too small");
-
-#ifndef EEPEMU_KEYMAP_SIZE
-# define EEPEMU_KEYMAP_SIZE 4096
-#endif
-_Static_assert(EEPEMU_KEYMAP_SIZE > DYNAMIC_KEYMAP_EEPROM_MAX_ADDR,
- "EEPEMU_KEYMAP Region is too small");
-
-#define ALIGNED_REGION(size, align) ((size + align - 1) / align * align)
-
-#ifndef EEPEMU_KEYMAP_START_OFFSET
-# define EEPEMU_KEYMAP_START_OFFSET \
- (PICO_FLASH_SIZE_BYTES - \
- ALIGNED_REGION(EEPEMU_KEYMAP_SIZE, FLASH_SECTOR_SIZE))
+/* Size of emulated eeprom */
+#ifdef FEE_DENSITY_BYTES
+# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)
+# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
+# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE
+# endif
+# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)
+# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE)
+# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate!
+# endif
+# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE
+# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE)
+# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows
+# endif
+# if ((FEE_DENSITY_BYTES) % 2) == 1
+# error emulated eeprom: FEE_DENSITY_BYTES must be even
+# endif
+#else
+/* Default to half of allocated space used for emulated eeprom, half for write log */
+# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)
#endif
-#ifndef EEPEMU_EECONFIG_START_OFFSET
-# define EEPEMU_EECONFIG_START_OFFSET \
- (EEPEMU_KEYMAP_START_OFFSET - \
- ALIGNED_REGION(EEPEMU_EECONFIG_SIZE, FLASH_SECTOR_SIZE))
+/* Size of write log */
+#ifdef FEE_WRITE_LOG_BYTES
+# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)
+# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE)
+# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE
+# endif
+# if ((FEE_WRITE_LOG_BYTES) % 2) == 1
+# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even
+# endif
+#else
+/* Default to use all remaining space */
+# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)
#endif
-#ifndef EEPEMU_LAZY_WRITEBACK_TIMEOUT
-# define EEPEMU_LAZY_WRITEBACK_TIMEOUT \
- 30000 // write back to ROM if 30s elapsed.
+/* Start of the emulated eeprom compacted flash area */
+#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS
+/* End of the emulated eeprom compacted flash area */
+#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)
+/* Start of the emulated eeprom write log */
+#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS
+/* End of the emulated eeprom write log */
+#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)
+
+#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)
+# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available
#endif
-_Static_assert(EEPEMU_EECONFIG_START_OFFSET % FLASH_SECTOR_SIZE == 0,
- "Offset should be aligned to FLASH_SCTOR_SIZE");
-_Static_assert(EEPEMU_KEYMAP_START_OFFSET % FLASH_SECTOR_SIZE == 0,
- "Offset should be aligned to FLASH_SCTOR_SIZE");
+/* In-memory contents of emulated eeprom for faster access */
+/* *TODO: Implement page swapping */
+static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
+static uint8_t *DataBuf = (uint8_t *)WordBuf;
-static uint8_t eepemu_eeconfig[EEPEMU_EECONFIG_SIZE]; // 1kB
-static uint8_t eepemu_dynamic_keymap[EEPEMU_KEYMAP_SIZE]; // 4kB
-static int32_t eeconfig_last_edit;
-static int32_t dynamic_keymap_last_edit;
+/* Pointer to the first available slot within the write log */
+static uint16_t *empty_slot;
-// Load ROM data to RAM
-void pico_eepemu_init(void) {
- memcpy(eepemu_eeconfig, (void *)XIP_BASE + EEPEMU_EECONFIG_START_OFFSET,
- sizeof(eepemu_eeconfig));
- memcpy(eepemu_dynamic_keymap, (void *)XIP_BASE + EEPEMU_KEYMAP_START_OFFSET,
- sizeof(eepemu_dynamic_keymap));
+static void eeprom_clear(void);
- eeconfig_last_edit = -1;
- dynamic_keymap_last_edit = -1;
+// #define DEBUG_EEPROM_OUTPUT
+
+/*
+ * Debug print utils
+ */
+
+#if defined(DEBUG_EEPROM_OUTPUT)
+
+# define debug_eeprom debug_enable
+# define eeprom_println(s) dprintln(s)
+# define eeprom_printf(fmt, ...) dprintf(fmt, ##__VA_ARGS__);
+
+#else /* NO_DEBUG */
+
+# define debug_eeprom false
+# define eeprom_println(s)
+# define eeprom_printf(fmt, ...)
+
+#endif /* NO_DEBUG */
+
+void print_eeprom(void) {
+#ifndef NO_DEBUG
+ int empty_rows = 0;
+ for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
+ if (i % 16 == 0) {
+ if (i >= FEE_DENSITY_BYTES - 16) {
+ /* Make sure we display the last row */
+ empty_rows = 0;
+ }
+ /* Check if this row is uninitialized */
+ ++empty_rows;
+ for (uint16_t j = 0; j < 16; j++) {
+ if (DataBuf[i + j]) {
+ empty_rows = 0;
+ break;
+ }
+ }
+ if (empty_rows > 1) {
+ /* Repeat empty row */
+ if (empty_rows == 2) {
+ /* Only display the first repeat empty row */
+ println("*");
+ }
+ i += 15;
+ continue;
+ }
+ xprintf("%04x", i);
+ }
+ if (i % 8 == 0) print(" ");
+
+ xprintf(" %02x", DataBuf[i]);
+ if ((i + 1) % 16 == 0) {
+ println("");
+ }
+
+ watchdog_update();
+ }
+#endif
}
-void pico_eepemu_lazy_write_back(void) {
- if (eeconfig_last_edit >= 0) {
- if (timer_elapsed(eeconfig_last_edit) >=
- EEPEMU_LAZY_WRITEBACK_TIMEOUT) {
- printf("eeconfig is written to ROM\n");
- pico_eepemu_flash_eeconfig();
+uint16_t EEPROM_Init(void) {
+ /* Load emulated eeprom contents from compacted flash into memory */
+ uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
+ uint16_t *dest = (uint16_t *)DataBuf;
+ for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
+ *dest = ~*(uint16_t*)((void*)src + XIP_BASE);
+ }
+
+ if (debug_eeprom) {
+ println("EEPROM_Init Compacted Pages:");
+ print_eeprom();
+ println("EEPROM_Init Write Log:");
+ }
+
+ if (*(uint32_t*)(FEE_WRITE_LOG_BASE_ADDRESS + XIP_BASE) != FEE_MAGIC_DWORD) {
+ eeprom_clear();
+ }
+
+ /* Replay write log */
+ uint16_t *log_addr;
+ for (log_addr = (uint16_t *)(FEE_WRITE_LOG_BASE_ADDRESS + 4); log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
+
+ watchdog_update();
+
+ uint16_t address = *(uint16_t*)((void*)log_addr + XIP_BASE);
+ if (address == FEE_EMPTY_WORD) {
+ break;
+ }
+ /* Check for lowest 128-bytes optimization */
+ if (!(address & FEE_WORD_ENCODING)) {
+ uint8_t bvalue = (uint8_t)address;
+ address >>= 8;
+ DataBuf[address] = bvalue;
+ eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
+ } else {
+ uint16_t wvalue;
+ /* Check if value is in next word */
+ if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
+ /* Read value from next word */
+ if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
+ break;
+ }
+ wvalue = ~*(uint16_t *)((void *)log_addr + XIP_BASE);
+ if (!wvalue) {
+ eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr);
+ /* Possibly incomplete write. Ignore and continue */
+ continue;
+ }
+ address &= 0x1FFF;
+ address <<= 1;
+ /* Writes to addresses less than 128 are byte log entries */
+ address += FEE_BYTE_RANGE;
+ } else {
+ /* Reserved for future use */
+ if (address & FEE_VALUE_RESERVED) {
+ eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr);
+ continue;
+ }
+ /* Optimization for 0 or 1 values. */
+ wvalue = (address & FEE_VALUE_ENCODED) >> 13;
+ address &= 0x1FFF;
+ address <<= 1;
+ }
+ if (address < FEE_DENSITY_BYTES) {
+ eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
+ *(uint16_t *)(&DataBuf[address]) = wvalue;
+ } else {
+ eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
+ }
}
}
- if (dynamic_keymap_last_edit >= 0) {
- if (timer_elapsed(dynamic_keymap_last_edit) >=
- EEPEMU_LAZY_WRITEBACK_TIMEOUT) {
- printf("Dynamic keymap is written to ROM\n");
- pico_eepemu_flash_dynamic_keymap();
+ empty_slot = log_addr;
+
+ if (debug_eeprom) {
+ println("EEPROM_Init Final DataBuf:");
+ print_eeprom();
+ eeprom_printf("Write Log Usage: %d/%d bytes\n ",
+ (uint32_t)empty_slot - FEE_WRITE_LOG_BASE_ADDRESS,
+ FEE_WRITE_LOG_BYTES);
+ }
+
+ return FEE_DENSITY_BYTES;
+}
+
+/* Clear flash contents (doesn't touch in-memory DataBuf) */
+static void eeprom_clear(void) {
+ FLASH_Unlock();
+
+ for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
+ eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
+ FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
+ }
+
+ FLASH_ProgramHalfWord(FEE_WRITE_LOG_BASE_ADDRESS, FEE_MAGIC_DWORD & 0xffff);
+ FLASH_ProgramHalfWord(FEE_WRITE_LOG_BASE_ADDRESS + 2,
+ FEE_MAGIC_DWORD >> 16);
+
+ FLASH_Lock();
+
+ empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
+ eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot);
+}
+
+/* Erase emulated eeprom */
+void EEPROM_Erase(void) {
+ eeprom_println("EEPROM_Erase");
+ /* Erase compacted pages and write log */
+ eeprom_clear();
+ /* re-initialize to reset DataBuf */
+ EEPROM_Init();
+}
+
+/* Compact write log */
+static uint8_t eeprom_compact(void) {
+ /* Erase compacted pages and write log */
+ eeprom_clear();
+
+ FLASH_Unlock();
+
+ FLASH_Status final_status = FLASH_COMPLETE;
+
+ /* Write emulated eeprom contents from memory to compacted flash */
+ uint16_t *src = (uint16_t *)DataBuf;
+ uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
+ uint16_t value;
+ for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
+ value = *src;
+ if (value) {
+ eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value);
+ FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
+ if (status != FLASH_COMPLETE) final_status = status;
}
}
+
+ FLASH_Lock();
+
+ if (debug_eeprom) {
+ println("eeprom_compacted:");
+ print_eeprom();
+ }
+
+ return final_status;
}
-void pico_eepemu_flash_eeconfig(void) {
- int32_t status = save_and_disable_interrupts();
+static uint8_t eeprom_write_direct_entry(uint16_t Address) {
+ /* Check if we can just write this directly to the compacted flash area */
+ uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
+ if (*(uint16_t *)((void *)directAddress + XIP_BASE) == FEE_EMPTY_WORD) {
+ /* Write the value directly to the compacted area without a log entry */
+ uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
+ /* Early exit if a write isn't needed */
+ if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
- flash_range_erase(EEPEMU_EECONFIG_START_OFFSET, FLASH_SECTOR_SIZE);
- flash_range_program(EEPEMU_EECONFIG_START_OFFSET, eepemu_eeconfig,
- sizeof(eepemu_eeconfig));
- eeconfig_last_edit = -1;
+ FLASH_Unlock();
- restore_interrupts(status);
+ eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
+ FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
+
+ FLASH_Lock();
+ return status;
+ }
+ return 0;
}
-void pico_eepemu_flash_dynamic_keymap(void) {
- int32_t status = save_and_disable_interrupts();
+static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
+ FLASH_Status final_status = FLASH_COMPLETE;
- flash_range_erase(EEPEMU_KEYMAP_START_OFFSET, FLASH_SECTOR_SIZE);
- flash_range_program(EEPEMU_KEYMAP_START_OFFSET, eepemu_dynamic_keymap,
- sizeof(eepemu_dynamic_keymap));
- dynamic_keymap_last_edit = -1;
+ uint16_t value = *(uint16_t *)(&DataBuf[Address]);
+ eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
- restore_interrupts(status);
-}
-
-static inline bool is_eeconfig_addr(const void *eeaddr) {
- if ((uint32_t)eeaddr < DYNAMIC_KEYMAP_EEPROM_ADDR) {
- return true;
+ /* MSB signifies the lowest 128-byte optimization is not in effect */
+ uint16_t encoding = FEE_WORD_ENCODING;
+ uint8_t entry_size;
+ if (value <= 1) {
+ encoding |= value << 13;
+ entry_size = 2;
} else {
- return false;
+ encoding |= FEE_VALUE_NEXT;
+ entry_size = 4;
+ /* Writes to addresses less than 128 are byte log entries */
+ Address -= FEE_BYTE_RANGE;
}
+
+ /* if we can't find an empty spot, we must compact emulated eeprom */
+ if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
+ /* compact the write log into the compacted flash area */
+ return eeprom_compact();
+ }
+
+ /* Word log writes should be word-aligned. Take back a bit */
+ Address >>= 1;
+ Address |= encoding;
+
+ /* ok we found a place let's write our data */
+ FLASH_Unlock();
+
+ /* address */
+ eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address);
+ final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
+
+ /* value */
+ if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
+ eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value);
+ FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
+ if (status != FLASH_COMPLETE) final_status = status;
+ }
+
+ FLASH_Lock();
+
+ return final_status;
}
-static void *convert_address(const void *addr) {
- // TODO Assert address
- if (is_eeconfig_addr(addr)) {
- return (void *)(eepemu_eeconfig + (uint32_t)addr);
+static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
+ eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
+
+ /* if couldn't find an empty spot, we must compact emulated eeprom */
+ if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
+ /* compact the write log into the compacted flash area */
+ return eeprom_compact();
+ }
+
+ /* ok we found a place let's write our data */
+ FLASH_Unlock();
+
+ /* Pack address and value into the same word */
+ uint16_t value = (Address << 8) | DataBuf[Address];
+
+ /* write to flash */
+ eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value);
+ FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
+
+ FLASH_Lock();
+
+ return status;
+}
+
+uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
+ /* if the address is out-of-bounds, do nothing */
+ if (Address >= FEE_DENSITY_BYTES) {
+ eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
+ return FLASH_BAD_ADDRESS;
+ }
+
+ /* if the value is the same, don't bother writing it */
+ if (DataBuf[Address] == DataByte) {
+ eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
+ return 0;
+ }
+
+ /* keep DataBuf cache in sync */
+ DataBuf[Address] = DataByte;
+ eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
+
+ /* perform the write into flash memory */
+ /* First, attempt to write directly into the compacted flash area */
+ FLASH_Status status = eeprom_write_direct_entry(Address);
+ if (!status) {
+ /* Otherwise append to the write log */
+ if (Address < FEE_BYTE_RANGE) {
+ status = eeprom_write_log_byte_entry(Address);
+ } else {
+ status = eeprom_write_log_word_entry(Address & 0xFFFE);
+ }
+ }
+ if (status != 0 && status != FLASH_COMPLETE) {
+ eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
+ }
+ return status;
+}
+
+uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
+ /* if the address is out-of-bounds, do nothing */
+ if (Address >= FEE_DENSITY_BYTES) {
+ eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
+ return FLASH_BAD_ADDRESS;
+ }
+
+ /* Check for word alignment */
+ FLASH_Status final_status = FLASH_COMPLETE;
+ if (Address % 2) {
+ final_status = EEPROM_WriteDataByte(Address, DataWord);
+ FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
+ if (status != FLASH_COMPLETE) final_status = status;
+ if (final_status != 0 && final_status != FLASH_COMPLETE) {
+ eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
+ }
+ return final_status;
+ }
+
+ /* if the value is the same, don't bother writing it */
+ uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
+ if (oldValue == DataWord) {
+ eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
+ return 0;
+ }
+
+ /* keep DataBuf cache in sync */
+ *(uint16_t *)(&DataBuf[Address]) = DataWord;
+ eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
+
+ /* perform the write into flash memory */
+ /* First, attempt to write directly into the compacted flash area */
+ final_status = eeprom_write_direct_entry(Address);
+ if (!final_status) {
+ /* Otherwise append to the write log */
+ /* Check if we need to fall back to byte write */
+ if (Address < FEE_BYTE_RANGE) {
+ final_status = FLASH_COMPLETE;
+ /* Only write a byte if it has changed */
+ if ((uint8_t)oldValue != (uint8_t)DataWord) {
+ final_status = eeprom_write_log_byte_entry(Address);
+ }
+ FLASH_Status status = FLASH_COMPLETE;
+ /* Only write a byte if it has changed */
+ if ((oldValue >> 8) != (DataWord >> 8)) {
+ status = eeprom_write_log_byte_entry(Address + 1);
+ }
+ if (status != FLASH_COMPLETE) final_status = status;
+ } else {
+ final_status = eeprom_write_log_word_entry(Address);
+ }
+ }
+ if (final_status != 0 && final_status != FLASH_COMPLETE) {
+ eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
+ }
+ return final_status;
+}
+
+uint8_t EEPROM_ReadDataByte(uint16_t Address) {
+ uint8_t DataByte = 0xFF;
+
+ if (Address < FEE_DENSITY_BYTES) {
+ DataByte = DataBuf[Address];
+ }
+
+ eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
+
+ return DataByte;
+}
+
+uint16_t EEPROM_ReadDataWord(uint16_t Address) {
+ uint16_t DataWord = 0xFFFF;
+
+ if (Address < FEE_DENSITY_BYTES - 1) {
+ /* Check word alignment */
+ if (Address % 2) {
+ DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
+ } else {
+ DataWord = *(uint16_t *)(&DataBuf[Address]);
+ }
+ }
+
+ eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
+
+ return DataWord;
+}
+
+/*****************************************************************************
+ * Wrap library in AVR style functions.
+ *******************************************************************************/
+uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); }
+
+void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
+
+void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); }
+
+uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); }
+
+void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
+
+void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); }
+
+uint32_t eeprom_read_dword(const uint32_t *Address) {
+ const uint16_t p = (const uintptr_t)Address;
+ /* Check word alignment */
+ if (p % 2) {
+ /* Not aligned */
+ return (uint32_t)EEPROM_ReadDataByte(p) | (uint32_t)(EEPROM_ReadDataWord(p + 1) << 8) | (uint32_t)(EEPROM_ReadDataByte(p + 3) << 24);
} else {
- return (void *)(eepemu_dynamic_keymap + (uint32_t)addr -
- DYNAMIC_KEYMAP_EEPROM_ADDR);
+ /* Aligned */
+ return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16);
}
}
-uint8_t eeprom_read_byte(const uint8_t *__p) {
- uint8_t *addr = convert_address(__p);
- if (addr == NULL) {
- return 0xff;
- }
-
- return *addr;
-}
-
-uint16_t eeprom_read_word(const uint16_t *__p) {
- uint16_t *addr = convert_address(__p);
- if (addr == NULL) {
- return 0xffff;
- }
-
- if ((uint32_t)addr % 2 == 0) {
- return *addr;
+void eeprom_write_dword(uint32_t *Address, uint32_t Value) {
+ uint16_t p = (const uintptr_t)Address;
+ /* Check word alignment */
+ if (p % 2) {
+ /* Not aligned */
+ EEPROM_WriteDataByte(p, (uint8_t)Value);
+ EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8));
+ EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24));
} else {
- uint16_t ret;
- memcpy(&ret, addr, sizeof(ret));
- return ret;
+ /* Aligned */
+ EEPROM_WriteDataWord(p, (uint16_t)Value);
+ EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16));
}
}
-uint32_t eeprom_read_dword(const uint32_t *__p) {
- uint32_t *addr = convert_address(__p);
- if (addr == NULL) {
- return 0xffffffff;
+void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, Value); }
+
+void eeprom_read_block(void *buf, const void *addr, size_t len) {
+ const uint8_t *src = (const uint8_t *)addr;
+ uint8_t * dest = (uint8_t *)buf;
+
+ /* Check word alignment */
+ if (len && (uintptr_t)src % 2) {
+ /* Read the unaligned first byte */
+ *dest++ = eeprom_read_byte(src++);
+ --len;
}
- if ((uint32_t)addr % 4 == 0) {
- return *addr;
- } else {
- uint32_t ret;
- memcpy(&ret, addr, sizeof(ret));
- return ret;
+ uint16_t value;
+ bool aligned = ((uintptr_t)dest % 2 == 0);
+ while (len > 1) {
+ value = eeprom_read_word((uint16_t *)src);
+ if (aligned) {
+ *(uint16_t *)dest = value;
+ dest += 2;
+ } else {
+ *dest++ = value;
+ *dest++ = value >> 8;
+ }
+ src += 2;
+ len -= 2;
+ }
+ if (len) {
+ *dest = eeprom_read_byte(src);
}
}
-void eeprom_read_block(void *__dst, const void *__src, size_t __n) {
- uint32_t *addr = convert_address(__src);
- if (addr == NULL) {
- return;
+void eeprom_write_block(const void *buf, void *addr, size_t len) {
+ uint8_t * dest = (uint8_t *)addr;
+ const uint8_t *src = (const uint8_t *)buf;
+
+ /* Check word alignment */
+ if (len && (uintptr_t)dest % 2) {
+ /* Write the unaligned first byte */
+ eeprom_write_byte(dest++, *src++);
+ --len;
}
- memcpy(__dst, addr, __n);
-}
+ uint16_t value;
+ bool aligned = ((uintptr_t)src % 2 == 0);
+ while (len > 1) {
+ if (aligned) {
+ value = *(uint16_t *)src;
+ } else {
+ value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
+ }
+ eeprom_write_word((uint16_t *)dest, value);
+ dest += 2;
+ src += 2;
+ len -= 2;
+ }
-static void update_last_edit_time(const void *eeaddr) {
- if (is_eeconfig_addr(eeaddr)) {
- eeconfig_last_edit = timer_read();
- } else {
- dynamic_keymap_last_edit = timer_read();
+ if (len) {
+ eeprom_write_byte(dest, *src);
}
}
-void eeprom_write_byte(uint8_t *__p, uint8_t __value) {
- uint8_t *addr = convert_address(__p);
- if (addr == NULL) {
- return;
- }
-
- *addr = __value;
-
- update_last_edit_time(__p);
-}
-
-void eeprom_write_word(uint16_t *__p, uint16_t __value) {
- uint16_t *addr = convert_address(__p);
- if (addr == NULL) {
- return;
- }
-
- if ((uint32_t)addr % 2 == 0) {
- *addr = __value;
- } else {
- memcpy(addr, &__value, sizeof(__value));
- }
-
- update_last_edit_time(__p);
-}
-void eeprom_write_dword(uint32_t *__p, uint32_t __value) {
- uint32_t *addr = convert_address(__p);
- if (addr == NULL) {
- return;
- }
-
- if ((uint32_t)addr % 4 == 0) {
- *addr = __value;
- } else {
- memcpy(addr, &__value, sizeof(__value));
- }
-
- update_last_edit_time(__p);
-}
-
-void eeprom_write_block(const void *__src, void *__dst, size_t __n) {
- uint32_t *addr = convert_address(__dst);
- if (addr == NULL) {
- return;
- }
-
- memcpy(addr, __src, __n);
-
- update_last_edit_time(__dst);
-}
-
-void eeprom_update_byte(uint8_t *__p, uint8_t __value) {
- if (eeprom_read_byte(__p) != __value) {
- eeprom_write_byte(__p, __value);
- }
-}
-
-void eeprom_update_word(uint16_t *__p, uint16_t __value) {
- if (eeprom_read_word(__p) != __value) {
- eeprom_write_word(__p, __value);
- }
-}
-
-void eeprom_update_dword(uint32_t *__p, uint32_t __value) {
- if (eeprom_read_dword(__p) != __value) {
- eeprom_write_dword(__p, __value);
- }
-}
-
-void eeprom_update_block(const void *__src, void *__dst, size_t __n) {
- uint32_t *addr = convert_address(__dst);
- if (addr == NULL) {
- return;
- }
-
- if (memcmp(__src, addr, __n) != 0) {
- eeprom_write_block(__src, __dst, __n);
- }
-}
+void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); }
diff --git a/platforms/pico/eeprom_pico.h b/platforms/pico/eeprom_pico.h
new file mode 100644
index 0000000000..8fcfb556b8
--- /dev/null
+++ b/platforms/pico/eeprom_pico.h
@@ -0,0 +1,33 @@
+/*
+ * This software is experimental and a work in progress.
+ * Under no circumstances should these files be used in relation to any critical system(s).
+ * Use of these files is at your own risk.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
+ * Artur F.
+ *
+ * Modifications for QMK and STM32F303 by Yiancar
+ *
+ * This library assumes 8-bit data locations. To add a new MCU, please provide the flash
+ * page size and the total flash size in Kb. The number of available pages must be a multiple
+ * of 2. Only half of the pages account for the total EEPROM size.
+ * This library also assumes that the pages are not used by the firmware.
+ */
+
+#pragma once
+
+uint16_t EEPROM_Init(void);
+void EEPROM_Erase(void);
+uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);
+uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);
+uint8_t EEPROM_ReadDataByte(uint16_t Address);
+uint16_t EEPROM_ReadDataWord(uint16_t Address);
+
+void print_eeprom(void);
diff --git a/platforms/pico/eeprom_pico_defs.h b/platforms/pico/eeprom_pico_defs.h
new file mode 100644
index 0000000000..edf0a7ff2c
--- /dev/null
+++ b/platforms/pico/eeprom_pico_defs.h
@@ -0,0 +1,12 @@
+
+#pragma once
+
+#include "hardware/flash.h"
+#include "hardware/structs/ssi.h"
+
+#define FEE_PAGE_SIZE FLASH_SECTOR_SIZE
+#define FEE_PAGE_COUNT 2
+#define FEE_MCU_FLASH_SIZE (PICO_FLASH_SIZE_BYTES / 1024)
+#define FEE_PAGE_BASE_ADDRESS \
+ (PICO_FLASH_SIZE_BYTES - FEE_PAGE_SIZE * FEE_PAGE_COUNT)
+
diff --git a/platforms/pico/flash_pico.c b/platforms/pico/flash_pico.c
new file mode 100644
index 0000000000..186109e1e9
--- /dev/null
+++ b/platforms/pico/flash_pico.c
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+
+#include "flash_pico.h"
+
+#include "pico/bootrom.h"
+#include "hardware/flash.h"
+#include "hardware/sync.h"
+#include "hardware/structs/ssi.h"
+#include "hardware/structs/ioqspi.h"
+
+#define BOOT2_SIZE_WORDS 64
+#define FLASHCMD_PAGE_PROGRAM 0x02
+#define FLASHCMD_READ_STATUS 0x05
+#define FLASHCMD_WRITE_ENABLE 0x06
+
+static ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE;
+
+// Sanity check
+#undef static_assert
+#define static_assert(cond, x) extern int static_assert[(cond) ? 1 : -1]
+check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET);
+check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET);
+
+static uint32_t boot2_copyout[BOOT2_SIZE_WORDS];
+static bool boot2_copyout_valid = false;
+
+static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(void) {
+ if (boot2_copyout_valid) return;
+ for (int i = 0; i < BOOT2_SIZE_WORDS; ++i)
+ boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i];
+ __compiler_memory_barrier();
+ boot2_copyout_valid = true;
+}
+
+static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {
+ ((void (*)(void))boot2_copyout + 1)();
+}
+
+// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs
+// are still running, and the FIFO bottoms out. (the bootrom does the same)
+static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) {
+ uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH
+ : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
+ hw_write_masked(&ioqspi_hw->io[1].ctrl,
+ field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
+ IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS);
+}
+
+// Also allow any unbounded loops to check whether the above abort condition
+// was asserted, and terminate early
+static int __no_inline_not_in_flash_func(flash_was_aborted)(void) {
+ return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) &
+ IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS;
+}
+
+// Put bytes from one buffer, and get bytes into another buffer.
+// These can be the same buffer.
+// If tx is NULL then send zeroes.
+// If rx is NULL then all read data will be dropped.
+//
+// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO,
+// before reading a further count bytes into *rx.
+// E.g. if you have written a command+address just before calling this function.
+static void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx,
+ uint8_t * rx,
+ size_t count,
+ size_t rx_skip) {
+ // Make sure there is never more data in flight than the depth of the RX
+ // FIFO. Otherwise, when we are interrupted for long periods, hardware
+ // will overflow the RX FIFO.
+ const uint max_in_flight = 16 - 2; // account for data internal to SSI
+ size_t tx_count = count;
+ size_t rx_count = count;
+ while (tx_count || rx_skip || rx_count) {
+ // NB order of reads, for pessimism rather than optimism
+ uint32_t tx_level = ssi_hw->txflr;
+ uint32_t rx_level = ssi_hw->rxflr;
+ bool did_something =
+ false; // Expect this to be folded into control flow, not register
+ if (tx_count && tx_level + rx_level < max_in_flight) {
+ ssi->dr0 = (uint32_t)(tx ? *tx++ : 0);
+ --tx_count;
+ did_something = true;
+ }
+ if (rx_level) {
+ uint8_t rxbyte = ssi->dr0;
+ did_something = true;
+ if (rx_skip) {
+ --rx_skip;
+ } else {
+ if (rx) *rx++ = rxbyte;
+ --rx_count;
+ }
+ }
+ // APB load costs 4 cycles, so only do it on idle loops (our budget is
+ // 48 cyc/byte)
+ if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break;
+ }
+ flash_cs_force(1);
+}
+
+// Convenience wrapper for above
+// (And it's hard for the debug host to get the tight timing between
+// cmd DR0 write and the remaining data)
+static void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd,
+ const uint8_t *tx,
+ uint8_t * rx,
+ size_t count) {
+ flash_cs_force(0);
+ ssi->dr0 = cmd;
+ flash_put_get(tx, rx, count, 1);
+}
+
+// Timing of this one is critical, so do not expose the symbol to debugger etc
+static void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd,
+ uint32_t addr) {
+ flash_cs_force(0);
+ addr |= cmd << 24;
+ for (int i = 0; i < 4; ++i) {
+ ssi->dr0 = addr >> 24;
+ addr <<= 8;
+ }
+}
+
+// Poll the flash status register until the busy bit (LSB) clears
+static void __no_inline_not_in_flash_func(flash_wait_ready)(void) {
+ uint8_t stat;
+ do {
+ _flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1);
+ } while (stat & 0x1 && !flash_was_aborted());
+}
+
+// Set the WEL bit (needed before any program/erase operation)
+static void __no_inline_not_in_flash_func(flash_enable_write)(void) {
+ _flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0);
+}
+
+static void __no_inline_not_in_flash_func(pico_program_half_word)(
+ uint32_t flash_offs, uint16_t data) {
+ rom_connect_internal_flash_fn connect_internal_flash =
+ (rom_connect_internal_flash_fn)rom_func_lookup_inline(
+ ROM_FUNC_CONNECT_INTERNAL_FLASH);
+ rom_flash_exit_xip_fn flash_exit_xip =
+ (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP);
+ rom_flash_range_program_fn flash_range_program =
+ (rom_flash_range_program_fn)rom_func_lookup_inline(
+ ROM_FUNC_FLASH_RANGE_PROGRAM);
+ rom_flash_flush_cache_fn flash_flush_cache =
+ (rom_flash_flush_cache_fn)rom_func_lookup_inline(
+ ROM_FUNC_FLASH_FLUSH_CACHE);
+ assert(connect_internal_flash && flash_exit_xip && flash_range_program &&
+ flash_flush_cache);
+ flash_init_boot2_copyout();
+
+ __compiler_memory_barrier();
+
+ connect_internal_flash();
+ flash_exit_xip();
+
+ flash_enable_write();
+ flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_offs);
+ flash_put_get((uint8_t *)&data, NULL, 2, 4);
+ flash_wait_ready();
+
+ flash_flush_cache(); // Note this is needed to remove CSn IO force as well
+ // as cache flushing
+ flash_enable_xip_via_boot2();
+}
+
+FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {
+ return FLASH_COMPLETE;
+}
+
+FLASH_Status FLASH_ErasePage(uint32_t Page_Address) {
+ flash_range_erase(Page_Address, FLASH_SECTOR_SIZE);
+ return FLASH_COMPLETE;
+}
+
+FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
+ pico_program_half_word(Address, Data);
+ return FLASH_COMPLETE;
+}
+
+__attribute__((weak)) void pico_before_flash_operation(void) {}
+__attribute__((weak)) void pico_after_flash_operation(void) {}
+
+static int intr_stat;
+
+void FLASH_Lock(void) {
+ restore_interrupts(intr_stat);
+ pico_after_flash_operation();
+}
+void FLASH_Unlock(void) {
+ intr_stat = save_and_disable_interrupts();
+ pico_before_flash_operation();
+}
diff --git a/platforms/pico/flash_pico.h b/platforms/pico/flash_pico.h
new file mode 100644
index 0000000000..6c66642ec5
--- /dev/null
+++ b/platforms/pico/flash_pico.h
@@ -0,0 +1,44 @@
+/*
+ * This software is experimental and a work in progress.
+ * Under no circumstances should these files be used in relation to any critical system(s).
+ * Use of these files is at your own risk.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and
+ * https://github.com/leaflabs/libmaple
+ *
+ * Modifications for QMK and STM32F303 by Yiancar
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#ifdef FLASH_STM32_MOCKED
+extern uint8_t FlashBuf[MOCK_FLASH_SIZE];
+#endif
+
+typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status;
+
+#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF))
+
+FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
+FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
+FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
+
+void FLASH_Unlock(void);
+void FLASH_Lock(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tmk_core/protocol/pico/pico.c b/tmk_core/protocol/pico/pico.c
index ca225154a2..4152a50b46 100644
--- a/tmk_core/protocol/pico/pico.c
+++ b/tmk_core/protocol/pico/pico.c
@@ -31,7 +31,7 @@
#include "bootloader.h"
#include "debug.h"
-#include "pico_eeprom.h"
+#include "eeprom_pico.h"
#include "usb_descriptors.h"
#include "pico/stdio/driver.h"
@@ -135,7 +135,7 @@ void protocol_pre_init(void) {
watchdog_enable(8000, 1);
watchdog_hw->scratch[0] = 0x2040dead;
pico_cdc_enable_printf();
- pico_eepemu_init();
+ EEPROM_Init();
}
void protocol_post_init(void) { host_set_driver(&driver); }
@@ -145,7 +145,6 @@ void protocol_pre_task(void) {
}
void protocol_post_task(void) {
- pico_eepemu_lazy_write_back();
#ifdef CONSOLE_ENABLE
console_task();
#endif
@@ -207,16 +206,15 @@ __attribute__((weak)) void pico_cdc_receive_cb(uint8_t const* buf, uint32_t cnt)
tud_cdc_write(buf, cnt);
tud_cdc_write_flush();
- if (buf[0] == 's') {
- printf("save keymap to eeprom\n");
- pico_eepemu_flash_dynamic_keymap();
- printf("complete\n");
- printf("save eeconfig to eeprom\n");
- pico_eepemu_flash_eeconfig();
- printf("complete\n");
+ if (buf[0] == 'b') {
+ bootloader_jump();
} else if (buf[0] == 'l') {
printf("init eeprom emulation\n");
- pico_eepemu_init();
+ EEPROM_Init();
+ printf("complete\n");
+ } else if (buf[0] == 'c') {
+ printf("clear eeprom emulation\n");
+ EEPROM_Erase();
printf("complete\n");
} else if (buf[0] == 'd') {
printf("debug print ");
diff --git a/tmk_core/protocol/pico/pico_eeprom.h b/tmk_core/protocol/pico/pico_eeprom.h
deleted file mode 100644
index 729181e274..0000000000
--- a/tmk_core/protocol/pico/pico_eeprom.h
+++ /dev/null
@@ -1,7 +0,0 @@
-
-#pragma once
-
-void pico_eepemu_init(void);
-void pico_eepemu_flash_eeconfig(void);
-void pico_eepemu_flash_dynamic_keymap(void);
-void pico_eepemu_lazy_write_back(void);