From 9210e665754dba0412271ab1c6bf406d85c370c2 Mon Sep 17 00:00:00 2001 From: Ilya Zhuravlev Date: Sat, 26 Dec 2020 06:18:20 -0500 Subject: [PATCH 1/5] eeprom_stm32: implement wear leveling Update EECONFIG_MAGIC_NUMBER eeprom_stm32: check emulated eeprom size is large enough --- quantum/eeconfig.h | 2 +- tmk_core/common/chibios/eeprom_stm32.c | 157 ++++++++++++++----------- tmk_core/common/chibios/eeprom_stm32.h | 23 ++-- 3 files changed, 103 insertions(+), 79 deletions(-) diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index a88071729da6..bd39971b2c50 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -21,7 +21,7 @@ along with this program. If not, see . #include #ifndef EECONFIG_MAGIC_NUMBER -# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues +# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE9 // When changing, decrement this value to avoid future re-init issues #endif #define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c index ea519897280d..e63a5c6f5780 100644 --- a/tmk_core/common/chibios/eeprom_stm32.c +++ b/tmk_core/common/chibios/eeprom_stm32.c @@ -14,102 +14,119 @@ * Artur F. * * Modifications for QMK and STM32F303 by Yiancar + * Modifications to add flash wear leveling by Ilya Zhuravlev */ #include #include #include "eeprom_stm32.h" -/***************************************************************************** - * Allows to use the internal flash to store non volatile data. To initialize - * the functionality use the EEPROM_Init() function. Be sure that by reprogramming - * of the controller just affected pages will be deleted. In other case the non - * volatile data will be lost. - ******************************************************************************/ -/* Private macro -------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Functions -----------------------------------------------------------------*/ +/* In-memory contents of emulated eeprom for faster access */ +static uint8_t DataBuf[FEE_DENSITY_BYTES]; + +/* Pointer to the first available slot within flash area */ +static uint8_t *empty_slot; + +void EEPROM_Init(void) { + memset(DataBuf, 0, sizeof(DataBuf)); + + /* Load emulated eeprom contents from flash into memory */ + uint8_t *addr; + for (addr = (uint8_t*)FEE_PAGE_BASE_ADDRESS; addr < (uint8_t*)FEE_LAST_PAGE_ADDRESS; addr += 4) { + uint16_t address; + uint8_t value; + memcpy(&address, addr, sizeof(address)); + memcpy(&value, addr+2, sizeof(value)); + if (address == FEE_EMPTY_WORD) + break; + if (address < FEE_DENSITY_BYTES) + DataBuf[address] = value; + } -uint8_t DataBuf[FEE_PAGE_SIZE]; -/***************************************************************************** - * Delete Flash Space used for user Data, deletes the whole space between - * RW_PAGE_BASE_ADDRESS and the last uC Flash Page - ******************************************************************************/ -uint16_t EEPROM_Init(void) { - // unlock flash + empty_slot = addr; +} + +/* Clear flash contents (doesn't touch in-memory DataBuf) */ +static void eeprom_clear(void) { FLASH_Unlock(); - // Clear Flags - // FLASH_ClearFlag(FLASH_SR_EOP|FLASH_SR_PGERR|FLASH_SR_WRPERR); + for (uint32_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) + FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); + + FLASH_Lock(); - return FEE_DENSITY_BYTES; + empty_slot = (void*)FEE_PAGE_BASE_ADDRESS; } -/***************************************************************************** - * Erase the whole reserved Flash Space used for user Data - ******************************************************************************/ + +/* Erase emulated eeprom */ void EEPROM_Erase(void) { - int page_num = 0; + eeprom_clear(); + /* re-initialize to clear DataBuf */ + EEPROM_Init(); +} - // delete all pages from specified start page to the last page - do { - FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); - page_num++; - } while (page_num < FEE_DENSITY_PAGES); +static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte); + +/* Dump in-memory contents into flash */ +static void eeprom_restore(void) { + for (uint32_t i = 0; i < FEE_DENSITY_BYTES; ++i) { + /* don't bother writing zeroes */ + if (DataBuf[i]) { + eeprom_writedatabyte(i, DataBuf[i]); + } + } } -/***************************************************************************** - * Writes once data byte to flash on specified address. If a byte is already - * written, the whole page must be copied to a buffer, the byte changed and - * the manipulated buffer written after PageErase. - *******************************************************************************/ -uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { - FLASH_Status FlashStatus = FLASH_COMPLETE; - uint32_t page; - int i; +static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte) { + /* if couldn't find an empty spot, we must re-initialize emulated eeprom */ + if (empty_slot >= (uint8_t*)FEE_LAST_PAGE_ADDRESS) { + /* ensure that the following call to eeprom_restore will write our desired byte value */ + DataBuf[Address] = DataByte; + + /* fully erase emulated eeprom */ + eeprom_clear(); + + /* and then write DataBuf contents back into flash */ + eeprom_restore(); - // exit if desired address is above the limit (e.G. under 2048 Bytes for 4 pages) - if (Address > FEE_DENSITY_BYTES) { - return 0; + /* don't need to do anything else as eeprom_restore already wrote our value */ + return; } - // calculate which page is affected (Pagenum1/Pagenum2...PagenumN) - page = FEE_ADDR_OFFSET(Address) / FEE_PAGE_SIZE; + /* ok we found a place let's write our data */ + FLASH_Unlock(); - // if current data is 0xFF, the byte is empty, just overwrite with the new one - if ((*(__IO uint16_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) == FEE_EMPTY_WORD) { - FlashStatus = FLASH_ProgramHalfWord(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address), (uint16_t)(0x00FF & DataByte)); - } else { - // Copy Page to a buffer - memcpy(DataBuf, (uint8_t *)FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE), FEE_PAGE_SIZE); // !!! Calculate base address for the desired page + /* address */ + FLASH_ProgramHalfWord((uint32_t)empty_slot, Address); + /* value */ + FLASH_ProgramHalfWord((uint32_t)empty_slot + 2, DataByte | 0xFF00); - // check if new data is differ to current data, return if not, proceed if yes - if (DataByte == *(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) { - return 0; - } + FLASH_Lock(); + + empty_slot += 4; +} - // manipulate desired data byte in temp data array if new byte is differ to the current - DataBuf[FEE_ADDR_OFFSET(Address) % FEE_PAGE_SIZE] = DataByte; +void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) + return; - // Erase Page - FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)); + /* if the value is the same, don't bother writing it */ + if (DataBuf[Address] == DataByte) + return; - // Write new data (whole page) to flash if data has been changed - for (i = 0; i < (FEE_PAGE_SIZE / 2); i++) { - if ((__IO uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]) != 0xFFFF) { - FlashStatus = FLASH_ProgramHalfWord((FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)) + (i * 2), (uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)])); - } - } - } - return FlashStatus; + /* perform the write into flash memory */ + eeprom_writedatabyte(Address, DataByte); + + /* keep DataBuf cache in sync */ + DataBuf[Address] = DataByte; } -/***************************************************************************** - * Read once data byte from a specified address. - *******************************************************************************/ + uint8_t EEPROM_ReadDataByte(uint16_t Address) { - uint8_t DataByte = 0xFF; + uint8_t DataByte = 0x00; - // Get Byte from specified address - DataByte = (*(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))); + if (Address < FEE_DENSITY_BYTES) + DataByte = DataBuf[Address]; return DataByte; } diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h index 4dac7c1b595f..25d46cc0d8fe 100644 --- a/tmk_core/common/chibios/eeprom_stm32.h +++ b/tmk_core/common/chibios/eeprom_stm32.h @@ -44,7 +44,7 @@ #ifndef EEPROM_PAGE_SIZE # if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6) # define FEE_PAGE_SIZE (uint16_t)0x400 // Page size = 1KByte -# define FEE_DENSITY_PAGES 2 // How many pages are used +# define FEE_DENSITY_PAGES 8 // How many pages are used # elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB) # define FEE_PAGE_SIZE (uint16_t)0x800 // Page size = 2KByte # define FEE_DENSITY_PAGES 4 // How many pages are used @@ -69,16 +69,23 @@ # endif #endif -// DONT CHANGE -// Choose location for the first EEPROM Page address on the top of flash +/* Start of the emulated eeprom flash area */ #define FEE_PAGE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE)) -#define FEE_DENSITY_BYTES ((FEE_PAGE_SIZE / 2) * FEE_DENSITY_PAGES - 1) +/* End of the emulated eeprom flash area */ #define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES)) +/* Size of emulated eeprom */ +#define FEE_DENSITY_BYTES 1024 +/* Flash word value after erase */ #define FEE_EMPTY_WORD ((uint16_t)0xFFFF) -#define FEE_ADDR_OFFSET(Address) (Address * 2) // 1Byte per Word will be saved to preserve Flash -// Use this function to initialize the functionality -uint16_t EEPROM_Init(void); +_Static_assert(FEE_DENSITY_PAGES * FEE_PAGE_SIZE >= FEE_DENSITY_BYTES * 8, + "flash memory for emulated eeprom is too small; for correct functionality ensure it is at least 8x FEE_DENSITY_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 + +void EEPROM_Init(void); void EEPROM_Erase(void); -uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); uint8_t EEPROM_ReadDataByte(uint16_t Address); From a6e82337e272ed9a082aa23d2fbe769c026f8b11 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Wed, 14 Jul 2021 16:56:03 -0700 Subject: [PATCH 2/5] eeprom_stm32: Increasing simulated EEPROM density on stm32 * Adding utility script to decode emulated eeprom * Adding unit tests * Applying qmk cformat changes --- build_test.mk | 1 + testlist.mk | 1 + tmk_core/common/chibios/eeprom_stm32.c | 805 +++++++++++++++++--- tmk_core/common/chibios/eeprom_stm32.h | 73 +- tmk_core/common/chibios/flash_stm32.h | 3 +- tmk_core/common/test/eeprom_stm32_tests.cpp | 439 +++++++++++ tmk_core/common/test/flash_stm32_mock.c | 51 ++ tmk_core/common/test/flash_stm32_mock.h | 34 + tmk_core/common/test/rules.mk | 12 + tmk_core/common/test/testlist.mk | 1 + util/stm32eeprom_parser.py | 318 ++++++++ 11 files changed, 1568 insertions(+), 170 deletions(-) create mode 100644 tmk_core/common/test/eeprom_stm32_tests.cpp create mode 100644 tmk_core/common/test/flash_stm32_mock.c create mode 100644 tmk_core/common/test/flash_stm32_mock.h create mode 100644 tmk_core/common/test/rules.mk create mode 100644 tmk_core/common/test/testlist.mk create mode 100755 util/stm32eeprom_parser.py diff --git a/build_test.mk b/build_test.mk index b6b8782174d3..037577bf9041 100644 --- a/build_test.mk +++ b/build_test.mk @@ -56,6 +56,7 @@ include $(TMK_PATH)/common.mk include $(QUANTUM_PATH)/debounce/tests/rules.mk include $(QUANTUM_PATH)/sequencer/tests/rules.mk include $(QUANTUM_PATH)/serial_link/tests/rules.mk +include $(TMK_PATH)/common/test/rules.mk ifneq ($(filter $(FULL_TESTS),$(TEST)),) include build_full_test.mk endif diff --git a/testlist.mk b/testlist.mk index d256f4c815d9..b66b93d29567 100644 --- a/testlist.mk +++ b/testlist.mk @@ -4,6 +4,7 @@ FULL_TESTS := $(TEST_LIST) include $(ROOT_DIR)/quantum/debounce/tests/testlist.mk include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk include $(ROOT_DIR)/quantum/serial_link/tests/testlist.mk +include $(ROOT_DIR)/tmk_core/common/test/testlist.mk define VALIDATE_TEST_LIST ifneq ($1,) diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c index e63a5c6f5780..e2138debd500 100644 --- a/tmk_core/common/chibios/eeprom_stm32.c +++ b/tmk_core/common/chibios/eeprom_stm32.c @@ -15,201 +15,792 @@ * * Modifications for QMK and STM32F303 by Yiancar * Modifications to add flash wear leveling by Ilya Zhuravlev + * Modifications to increase flash density by Don Kjer */ #include -#include +#include #include "eeprom_stm32.h" +#include "flash_stm32.h" +#ifdef FLASH_STM32_MOCKED +# include "flash_stm32_mock.h" +#endif +#ifdef DEBUG_EEPROM +# ifdef FLASH_STM32_MOCKED +# define dprintf printf +# else +# include "debug.h" +# endif +#endif + +/* + * 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_DENSITY_PAGES # 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_DENSITY_PAGES) + * 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_DENSITY_PAGES * 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 + * + */ + +/* 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 + +// HACK ALERT. This definition may not match your processor +// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc +#if defined(EEPROM_EMU_STM32F303xC) +# define MCU_STM32F303CC +#elif defined(EEPROM_EMU_STM32F103xB) +# define MCU_STM32F103RB +#elif defined(EEPROM_EMU_STM32F072xB) +# define MCU_STM32F072CB +#elif defined(EEPROM_EMU_STM32F042x6) +# define MCU_STM32F042K6 +#elif !defined(FEE_PAGE_SIZE) || !defined(FEE_DENSITY_PAGES) || !defined(FEE_MCU_FLASH_SIZE) +# error "not implemented." +#endif + +#if !defined(FEE_PAGE_SIZE) || !defined(FEE_DENSITY_PAGES) +# if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte +# endif +# ifndef FEE_DENSITY_PAGES +# define FEE_DENSITY_PAGES 2 // How many pages are used +# endif +# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte +# endif +# ifndef FEE_DENSITY_PAGES +# define FEE_DENSITY_PAGES 4 // How many pages are used +# endif +# else +# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." +# endif +#endif + +#ifndef FEE_MCU_FLASH_SIZE +# if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB) +# define FEE_MCU_FLASH_SIZE 128 // Size in Kb +# elif defined(MCU_STM32F042K6) +# define FEE_MCU_FLASH_SIZE 32 // Size in Kb +# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) +# define FEE_MCU_FLASH_SIZE 512 // Size in Kb +# elif defined(MCU_STM32F103RD) +# define FEE_MCU_FLASH_SIZE 384 // Size in Kb +# elif defined(MCU_STM32F303CC) +# define FEE_MCU_FLASH_SIZE 256 // Size in Kb +# else +# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." +# endif +#endif + +#define FEE_XSTR(x) FEE_STR(x) +#define FEE_STR(x) #x + +/* Size of combined compacted eeprom and write log pages */ +#define FEE_DENSITY_MAX_SIZE (FEE_DENSITY_PAGES * FEE_PAGE_SIZE) +/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */ +#define FEE_ADDRESS_MAX_SIZE 0x4000 + +#ifndef EEPROM_START_ADDRESS /* *TODO: Get rid of this check */ +# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024) +# pragma message FEE_XSTR(FEE_DENSITY_MAX_SIZE) " > " FEE_XSTR(FEE_MCU_FLASH_SIZE * 1024) +# error emulated eeprom: FEE_DENSITY_PAGES is greater than available flash size +# endif +#endif + +/* Size of emulated eeprom */ +#ifdef FEE_DENSITY_BYTES +# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE) +# pragma message FEE_XSTR(FEE_DENSITY_BYTES) " > " FEE_XSTR(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 FEE_XSTR(FEE_DENSITY_BYTES) " == " FEE_XSTR(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 FEE_XSTR(FEE_DENSITY_BYTES) " > " FEE_XSTR(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_DENSITY_PAGES * FEE_PAGE_SIZE / 2) +#endif + +/* Size of write log */ +#define FEE_WRITE_LOG_BYTES (FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_DENSITY_BYTES) + +/* Start of the emulated eeprom compacted flash area */ +#ifndef FEE_FLASH_BASE +# define FEE_FLASH_BASE 0x8000000 +#endif +#define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - FEE_WRITE_LOG_BYTES - FEE_DENSITY_BYTES) +/* End of the emulated eeprom compacted flash area */ +#define FEE_PAGE_LAST_ADDRESS (FEE_PAGE_BASE_ADDRESS + FEE_DENSITY_BYTES) +/* Start of the emulated eeprom write log */ +#define FEE_WRITE_LOG_BASE_ADDRESS FEE_PAGE_LAST_ADDRESS +/* End of the emulated eeprom write log */ +#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES) + +/* Flash word value after erase */ +#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) + +#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 /* In-memory contents of emulated eeprom for faster access */ -static uint8_t DataBuf[FEE_DENSITY_BYTES]; - -/* Pointer to the first available slot within flash area */ -static uint8_t *empty_slot; - -void EEPROM_Init(void) { - memset(DataBuf, 0, sizeof(DataBuf)); - - /* Load emulated eeprom contents from flash into memory */ - uint8_t *addr; - for (addr = (uint8_t*)FEE_PAGE_BASE_ADDRESS; addr < (uint8_t*)FEE_LAST_PAGE_ADDRESS; addr += 4) { - uint16_t address; - uint8_t value; - memcpy(&address, addr, sizeof(address)); - memcpy(&value, addr+2, sizeof(value)); - if (address == FEE_EMPTY_WORD) +/* *TODO: Implement page swapping */ +static uint16_t WordBuf[FEE_DENSITY_BYTES / 2]; +static uint8_t *DataBuf = (uint8_t *)WordBuf; + +/* Pointer to the first available slot within the write log */ +static uint16_t *empty_slot; + +#ifdef DEBUG_EEPROM +void dumpEepromDataBuf(void) { + 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 */ + dprintf("*\n"); + } + i += 15; + continue; + } + dprintf("%04x", i); + } + if (i % 8 == 0) dprintf(" "); + + dprintf(" %02x", DataBuf[i]); + if ((i + 1) % 16 == 0) { + dprintf("\n"); + } + } +} +#endif + +uint16_t EEPROM_Init(void) { + /* Load emulated eeprom contents from compacted flash into memory */ + uint16_t *src = (uint16_t *)FEE_PAGE_BASE_ADDRESS; + uint16_t *dest = (uint16_t *)DataBuf; + for (; src < (uint16_t *)FEE_PAGE_LAST_ADDRESS; ++src, ++dest) { + *dest = ~*src; + } + +#ifdef DEBUG_EEPROM + dprintf("EEPROM_Init Compacted Pages:\n"); + dumpEepromDataBuf(); + dprintf("EEPROM_Init Write Log:\n"); +#endif + + /* Replay write log */ + uint16_t *log_addr; + for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) { + uint16_t address = *log_addr; + if (address == FEE_EMPTY_WORD) { break; - if (address < FEE_DENSITY_BYTES) - DataBuf[address] = value; + } + /* Check for lowest 128-bytes optimization */ + if (!(address & FEE_WORD_ENCODING)) { + uint8_t bvalue = (uint8_t)address; + address >>= 8; + DataBuf[address] = bvalue; +#ifdef DEBUG_EEPROM + dprintf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); +#endif + } 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 = ~*log_addr; + if (!wvalue) { +#ifdef DEBUG_EEPROM + dprintf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); +#endif + /* 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) { +#ifdef DEBUG_EEPROM + dprintf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); +#endif + continue; + } + /* Optimization for 0 or 1 values. */ + wvalue = (address & FEE_VALUE_ENCODED) >> 13; + address &= 0x1FFF; + address <<= 1; + } + if (address < FEE_DENSITY_BYTES) { +#ifdef DEBUG_EEPROM + dprintf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); +#endif + *(uint16_t *)(&DataBuf[address]) = wvalue; + } +#ifdef DEBUG_EEPROM + else { + dprintf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); + } +#endif + } } - empty_slot = addr; + empty_slot = log_addr; + +#ifdef DEBUG_EEPROM + dprintf("EEPROM_Init Final DataBuf:\n"); + dumpEepromDataBuf(); +#endif + + return FEE_DENSITY_BYTES; } /* Clear flash contents (doesn't touch in-memory DataBuf) */ static void eeprom_clear(void) { FLASH_Unlock(); - for (uint32_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) + for (uint16_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) { +#ifdef DEBUG_EEPROM + dprintf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); +#endif FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); + } FLASH_Lock(); - empty_slot = (void*)FEE_PAGE_BASE_ADDRESS; + empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; +#ifdef DEBUG_EEPROM + dprintf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); +#endif } /* Erase emulated eeprom */ void EEPROM_Erase(void) { +#ifdef DEBUG_EEPROM + dprintf("EEPROM_Erase\n"); +#endif + /* Erase compacted pages and write log */ eeprom_clear(); - /* re-initialize to clear DataBuf */ + /* re-initialize to reset DataBuf */ EEPROM_Init(); } -static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte); +/* Compact write log */ +static uint8_t eeprom_compact(void) { + /* Erase compacted pages and write log */ + eeprom_clear(); + + FLASH_Unlock(); -/* Dump in-memory contents into flash */ -static void eeprom_restore(void) { - for (uint32_t i = 0; i < FEE_DENSITY_BYTES; ++i) { - /* don't bother writing zeroes */ - if (DataBuf[i]) { - eeprom_writedatabyte(i, DataBuf[i]); + 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_PAGE_BASE_ADDRESS; + uint16_t value; + for (; dest < FEE_PAGE_LAST_ADDRESS; ++src, dest += 2) { + value = *src; + if (value) { +#ifdef DEBUG_EEPROM + dprintf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value); +#endif + FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value); + if (status != FLASH_COMPLETE) final_status = status; } } + + FLASH_Lock(); + +#ifdef DEBUG_EEPROM + dprintf("eeprom_compacted:\n"); + dumpEepromDataBuf(); +#endif + + return final_status; } -static void eeprom_writedatabyte(uint16_t Address, uint8_t DataByte) { - /* if couldn't find an empty spot, we must re-initialize emulated eeprom */ - if (empty_slot >= (uint8_t*)FEE_LAST_PAGE_ADDRESS) { - /* ensure that the following call to eeprom_restore will write our desired byte value */ - DataBuf[Address] = DataByte; +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_PAGE_BASE_ADDRESS + (Address & 0xFFFE); + if (*(uint16_t *)directAddress == 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_Unlock(); - /* fully erase emulated eeprom */ - eeprom_clear(); +#ifdef DEBUG_EEPROM + dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); +#endif + FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); + + FLASH_Lock(); + return status; + } + return 0; +} - /* and then write DataBuf contents back into flash */ - eeprom_restore(); +static uint8_t eeprom_write_log_word_entry(uint16_t Address) { + FLASH_Status final_status = FLASH_COMPLETE; + + uint16_t value = *(uint16_t *)(&DataBuf[Address]); +#ifdef DEBUG_EEPROM + dprintf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); +#endif + + /* 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 { + encoding |= FEE_VALUE_NEXT; + entry_size = 4; + /* Writes to addresses less than 128 are byte log entries */ + Address -= FEE_BYTE_RANGE; + } - /* don't need to do anything else as eeprom_restore already wrote our value */ - return; + /* 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 */ - FLASH_ProgramHalfWord((uint32_t)empty_slot, Address); +#ifdef DEBUG_EEPROM + dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address); +#endif + final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address); + /* value */ - FLASH_ProgramHalfWord((uint32_t)empty_slot + 2, DataByte | 0xFF00); + if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) { +#ifdef DEBUG_EEPROM + dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value); +#endif + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } FLASH_Lock(); - empty_slot += 4; + return final_status; } -void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { +static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { +#ifdef DEBUG_EEPROM + dprintf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); +#endif + + /* 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 */ +#ifdef DEBUG_EEPROM + dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value); +#endif + 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) - return; + if (Address >= FEE_DENSITY_BYTES) { +#ifdef DEBUG_EEPROM + dprintf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); +#endif + return FLASH_BAD_ADDRESS; + } /* if the value is the same, don't bother writing it */ - if (DataBuf[Address] == DataByte) - return; + if (DataBuf[Address] == DataByte) { +#ifdef DEBUG_EEPROM + dprintf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); +#endif + return 0; + } + + /* keep DataBuf cache in sync */ + DataBuf[Address] = DataByte; +#ifdef DEBUG_EEPROM + dprintf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); +#endif /* perform the write into flash memory */ - eeprom_writedatabyte(Address, DataByte); + /* 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); + } + } +#ifdef DEBUG_EEPROM + if (status != 0 && status != FLASH_COMPLETE) { + dprintf("EEPROM_WriteDataByte [STATUS == %d]\n", status); + } +#endif + 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) { +#ifdef DEBUG_EEPROM + dprintf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); +#endif + 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; +#ifdef DEBUG_EEPROM + if (final_status != 0 && final_status != FLASH_COMPLETE) { + dprintf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } +#endif + return final_status; + } + + /* if the value is the same, don't bother writing it */ + uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); + if (oldValue == DataWord) { +#ifdef DEBUG_EEPROM + dprintf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); +#endif + return 0; + } /* keep DataBuf cache in sync */ - DataBuf[Address] = DataByte; + *(uint16_t *)(&DataBuf[Address]) = DataWord; +#ifdef DEBUG_EEPROM + dprintf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); +#endif + + /* 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); + } + } +#ifdef DEBUG_EEPROM + if (final_status != 0 && final_status != FLASH_COMPLETE) { + dprintf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } +#endif + return final_status; } uint8_t EEPROM_ReadDataByte(uint16_t Address) { - uint8_t DataByte = 0x00; + uint8_t DataByte = 0xFF; - if (Address < FEE_DENSITY_BYTES) + if (Address < FEE_DENSITY_BYTES) { DataByte = DataBuf[Address]; + } + +#ifdef DEBUG_EEPROM + dprintf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); +#endif 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]); + } + } + +#ifdef DEBUG_EEPROM + dprintf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); +#endif + + return DataWord; +} + /***************************************************************************** * Wrap library in AVR style functions. *******************************************************************************/ -uint8_t eeprom_read_byte(const uint8_t *Address) { - const uint16_t p = (const uint32_t)Address; - return EEPROM_ReadDataByte(p); -} +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) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, Value); -} +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) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, 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) { - const uint16_t p = (const uint32_t)Address; - return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8); -} +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) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); -} +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) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); -} +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 uint32_t)Address; - return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24); + 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 { + /* Aligned */ + return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16); + } } void eeprom_write_dword(uint32_t *Address, uint32_t Value) { - uint16_t p = (const uint32_t)Address; - EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); - EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16)); - EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24)); -} - -void eeprom_update_dword(uint32_t *Address, uint32_t Value) { - uint16_t p = (const uint32_t)Address; - uint32_t existingValue = EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24); - if (Value != existingValue) { + uint16_t p = (const uintptr_t)Address; + /* Check word alignment */ + if (p % 2) { + /* Not aligned */ EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); - EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16)); + EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8)); EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24)); + } else { + /* Aligned */ + EEPROM_WriteDataWord(p, (uint16_t)Value); + EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16)); } } +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 *p = (const uint8_t *)addr; + const uint8_t *src = (const uint8_t *)addr; uint8_t * dest = (uint8_t *)buf; - while (len--) { - *dest++ = eeprom_read_byte(p++); + + /* Check word alignment */ + if (len && (uintptr_t)src % 2) { + /* Read the unaligned first byte */ + *dest++ = eeprom_read_byte(src++); + --len; + } + + 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_write_block(const void *buf, void *addr, size_t len) { - uint8_t * p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); + 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; } -} -void eeprom_update_block(const void *buf, void *addr, size_t len) { - uint8_t * p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); + 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; + } + + if (len) { + eeprom_write_byte(dest, *src); } } + +void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); } diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h index 25d46cc0d8fe..d943ba0eb632 100644 --- a/tmk_core/common/chibios/eeprom_stm32.h +++ b/tmk_core/common/chibios/eeprom_stm32.h @@ -23,69 +23,20 @@ #pragma once -#include -#include -#include "flash_stm32.h" - -// HACK ALERT. This definition may not match your processor -// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc -#if defined(EEPROM_EMU_STM32F303xC) -# define MCU_STM32F303CC -#elif defined(EEPROM_EMU_STM32F103xB) -# define MCU_STM32F103RB -#elif defined(EEPROM_EMU_STM32F072xB) -# define MCU_STM32F072CB -#elif defined(EEPROM_EMU_STM32F042x6) -# define MCU_STM32F042K6 +#ifdef FLASH_STM32_MOCKED +# include #else -# error "not implemented." -#endif - -#ifndef EEPROM_PAGE_SIZE -# if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6) -# define FEE_PAGE_SIZE (uint16_t)0x400 // Page size = 1KByte -# define FEE_DENSITY_PAGES 8 // How many pages are used -# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB) -# define FEE_PAGE_SIZE (uint16_t)0x800 // Page size = 2KByte -# define FEE_DENSITY_PAGES 4 // How many pages are used -# else -# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." -# endif +# include +# include #endif -#ifndef EEPROM_START_ADDRESS -# if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB) -# define FEE_MCU_FLASH_SIZE 128 // Size in Kb -# elif defined(MCU_STM32F042K6) -# define FEE_MCU_FLASH_SIZE 32 // Size in Kb -# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) -# define FEE_MCU_FLASH_SIZE 512 // Size in Kb -# elif defined(MCU_STM32F103RD) -# define FEE_MCU_FLASH_SIZE 384 // Size in Kb -# elif defined(MCU_STM32F303CC) -# define FEE_MCU_FLASH_SIZE 256 // Size in Kb -# else -# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." -# endif -#endif - -/* Start of the emulated eeprom flash area */ -#define FEE_PAGE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE)) -/* End of the emulated eeprom flash area */ -#define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES)) -/* Size of emulated eeprom */ -#define FEE_DENSITY_BYTES 1024 -/* Flash word value after erase */ -#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) - -_Static_assert(FEE_DENSITY_PAGES * FEE_PAGE_SIZE >= FEE_DENSITY_BYTES * 8, - "flash memory for emulated eeprom is too small; for correct functionality ensure it is at least 8x FEE_DENSITY_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 - -void EEPROM_Init(void); +uint16_t EEPROM_Init(void); void EEPROM_Erase(void); -void EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +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); + +#ifdef DEBUG_EEPROM +void dumpEepromDataBuf(void); +#endif diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h index 90d5bff47e33..75cd27d120a0 100644 --- a/tmk_core/common/chibios/flash_stm32.h +++ b/tmk_core/common/chibios/flash_stm32.h @@ -22,8 +22,7 @@ extern "C" { #endif -#include -#include +#include typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status; diff --git a/tmk_core/common/test/eeprom_stm32_tests.cpp b/tmk_core/common/test/eeprom_stm32_tests.cpp new file mode 100644 index 000000000000..fb29d2858c69 --- /dev/null +++ b/tmk_core/common/test/eeprom_stm32_tests.cpp @@ -0,0 +1,439 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +extern "C" { +#include "flash_stm32.h" +#include "flash_stm32_mock.h" +#include "eeprom_stm32.h" +#include "eeprom.h" +} + +/* Mock Flash Parameters: + * + * === Large Layout === + * flash size: 65536 + * page size: 2048 + * density pages: 16 + * Simulated EEPROM size: 16384 + * + * FlashBuf Layout: + * [Unused | Compact | Write Log ] + * [0......|32768......|49152......65535] + * + * === Tiny Layout === + * flash size: 1024 + * page size: 512 + * density pages: 1 + * Simulated EEPROM size: 256 + * + * FlashBuf Layout: + * [Unused | Compact | Write Log ] + * [0......|512......|768......1023] + * + */ + +#define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_DENSITY_PAGES / 2) +#define LOG_SIZE EEPROM_SIZE +#define LOG_BASE (FLASH_SIZE - LOG_SIZE) +#define EEPROM_BASE (LOG_BASE - EEPROM_SIZE) + +/* Log encoding helpers */ +#define BYTE_VALUE(addr, value) (((addr) << 8) | (value)) +#define WORD_ZERO(addr) (0x8000 | ((addr) >> 1)) +#define WORD_ONE(addr) (0xA000 | ((addr) >> 1)) +#define WORD_NEXT(addr) (0xE000 | (((addr)-0x80) >> 1)) + +class EepromStm32Test : public testing::Test { + public: + EepromStm32Test() {} + ~EepromStm32Test() {} + + protected: + void SetUp() override { EEPROM_Erase(); } + + void TearDown() override { +#ifdef EEPROM_DEBUG + dumpEepromDataBuf(); +#endif + } +}; + +TEST_F(EepromStm32Test, TestErase) { + EEPROM_WriteDataByte(0, 0x42); + EEPROM_Erase(); + EXPECT_EQ(EEPROM_ReadDataByte(0), 0); + EXPECT_EQ(EEPROM_ReadDataByte(1), 0); +} + +TEST_F(EepromStm32Test, TestReadGarbage) { + uint8_t garbage = 0x3c; + for (int i = 0; i < FLASH_SIZE; ++i) { + garbage ^= 0xa3; + garbage += i; + FlashBuf[i] = garbage; + } + EEPROM_Init(); // Just verify we don't crash +} + +TEST_F(EepromStm32Test, TestWriteBadAddress) { + EXPECT_EQ(EEPROM_WriteDataByte(EEPROM_SIZE, 0x42), FLASH_BAD_ADDRESS); + EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE - 1, 0xbeef), FLASH_BAD_ADDRESS); + EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE, 0xbeef), FLASH_BAD_ADDRESS); +} + +TEST_F(EepromStm32Test, TestReadBadAddress) { + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE), 0xFF); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 1), 0xFFFF); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE), 0xFFFF); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 3)), 0xFF000000); + EXPECT_EQ(eeprom_read_dword((uint32_t*)EEPROM_SIZE), 0xFFFFFFFF); +} + +TEST_F(EepromStm32Test, TestReadByte) { + /* Direct compacted-area baseline: Address < 0x80 */ + FlashBuf[EEPROM_BASE + 2] = ~0xef; + FlashBuf[EEPROM_BASE + 3] = ~0xbe; + /* Direct compacted-area baseline: Address >= 0x80 */ + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); + /* Write Log byte value */ + FlashBuf[LOG_BASE] = 0x65; + FlashBuf[LOG_BASE + 1] = 3; + /* Write Log word value */ + *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_NEXT(EEPROM_SIZE - 2); + *(uint16_t*)&FlashBuf[LOG_BASE + 4] = ~0x9abc; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0x65); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0xbc); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x9a); +} + +TEST_F(EepromStm32Test, TestWriteByte) { + /* Direct compacted-area baseline: Address < 0x80 */ + EEPROM_WriteDataByte(2, 0xef); + EEPROM_WriteDataByte(3, 0xbe); + /* Direct compacted-area baseline: Address >= 0x80 */ + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78); + EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56); + /* Check values */ + /* First write in each aligned word should have been direct */ + EXPECT_EQ(FlashBuf[EEPROM_BASE + 2], (uint8_t)~0xef); + EXPECT_EQ(FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint8_t)~0x78); + + /* Second write per aligned word requires a log entry */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(3, 0xbe)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(EEPROM_SIZE - 1)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0x5678); +} + +TEST_F(EepromStm32Test, TestByteRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); + EEPROM_WriteDataByte(2, 0xef); + EEPROM_WriteDataByte(3, 0xbe); + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78); + EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(0), 0xad); + EXPECT_EQ(EEPROM_ReadDataByte(1), 0xde); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); + /* Write log entries */ + EEPROM_WriteDataByte(2, 0x80); + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x3c); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0x80); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x3c); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); +} + +TEST_F(EepromStm32Test, TestReadWord) { + /* Direct compacted-area baseline: Address < 0x80 */ + FlashBuf[EEPROM_BASE + 0] = ~0xad; + FlashBuf[EEPROM_BASE + 1] = ~0xde; + /* Direct compacted-area baseline: Address >= 0x80 */ + FlashBuf[EEPROM_BASE + 200] = ~0xcd; + FlashBuf[EEPROM_BASE + 201] = ~0xab; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4] = ~0x34; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 3] = ~0x12; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678); + /* Write Log word zero-encoded */ + *(uint16_t*)&FlashBuf[LOG_BASE] = WORD_ZERO(200); + /* Write Log word one-encoded */ + *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_ONE(EEPROM_SIZE - 4); + /* Write Log word value */ + *(uint16_t*)&FlashBuf[LOG_BASE + 4] = WORD_NEXT(EEPROM_SIZE - 2); + *(uint16_t*)&FlashBuf[LOG_BASE + 6] = ~0x9abc; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 1); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x9abc); +} + +TEST_F(EepromStm32Test, TestWriteWord) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); // Aligned + EEPROM_WriteDataWord(3, 0xbeef); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataWord(200, 0xabcd); // Aligned + EEPROM_WriteDataWord(203, 0x9876); // Unaligned + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234); + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678); + /* Write Log word zero-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0); + /* Write Log word one-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1); + /* Write Log word value aligned */ + EEPROM_WriteDataWord(200, 0x4321); // Aligned + /* Write Log word value unaligned */ + EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte + EEPROM_WriteDataWord(203, 0xcdef); // Unaligned + /* Check values */ + /* Direct compacted-area */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE], (uint16_t)~0xdead); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 3], (uint16_t)~0xbeef); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 200], (uint16_t)~0xabcd); + EXPECT_EQ(FlashBuf[EEPROM_BASE + 203], (uint8_t)~0x76); + EXPECT_EQ(FlashBuf[EEPROM_BASE + 204], (uint8_t)~0x98); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4], (uint16_t)~0x1234); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint16_t)~0x5678); + /* Write Log word zero-encoded */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], WORD_ZERO(EEPROM_SIZE - 4)); + /* Write Log word one-encoded */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_ONE(EEPROM_SIZE - 2)); + /* Write Log word value aligned */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], WORD_NEXT(200)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], (uint16_t)~0x4321); + /* Write Log word value unaligned */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], WORD_NEXT(202)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], (uint16_t)~0x763c); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(202)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xef3c); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(204)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0x00cd); +} + +TEST_F(EepromStm32Test, TestWordRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); // Aligned + EEPROM_WriteDataWord(3, 0xbeef); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataWord(200, 0xabcd); // Aligned + EEPROM_WriteDataWord(203, 0x9876); // Unaligned + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234); + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead); + EXPECT_EQ(EEPROM_ReadDataWord(3), 0xbeef); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd); + EXPECT_EQ(EEPROM_ReadDataWord(203), 0x9876); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678); + + /* Write Log word zero-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0); + /* Write Log word one-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1); + /* Write Log word value aligned */ + EEPROM_WriteDataWord(200, 0x4321); // Aligned + /* Write Log word value unaligned */ + EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte + EEPROM_WriteDataWord(203, 0xcdef); // Unaligned + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0x4321); + EXPECT_EQ(EEPROM_ReadDataByte(202), 0x3c); + EXPECT_EQ(EEPROM_ReadDataWord(203), 0xcdef); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 1); +} + +TEST_F(EepromStm32Test, TestByteWordBoundary) { + /* Direct compacted-area write */ + EEPROM_WriteDataWord(0x7e, 0xdead); + EEPROM_WriteDataWord(0x80, 0xbeef); + /* Byte log entry */ + EEPROM_WriteDataByte(0x7f, 0x3c); + /* Word log entry */ + EEPROM_WriteDataByte(0x80, 0x18); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0x3cad); + EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xbe18); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(0x7f, 0x3c)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0xbe18); + /* Byte log entries */ + EEPROM_WriteDataWord(0x7e, 0xcafe); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0xcafe); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], BYTE_VALUE(0x7e, 0xfe)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], BYTE_VALUE(0x7f, 0xca)); + /* Byte and Word log entries */ + EEPROM_WriteDataWord(0x7f, 0xba5e); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7f), 0xba5e); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], BYTE_VALUE(0x7f, 0x5e)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xbeba); + /* Word log entry */ + EEPROM_WriteDataWord(0x80, 0xf00d); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xf00d); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0xf00d); +} + +TEST_F(EepromStm32Test, TestDWordRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + eeprom_write_dword((uint32_t*)0, 0xdeadbeef); // Aligned + eeprom_write_dword((uint32_t*)9, 0x12345678); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + eeprom_write_dword((uint32_t*)200, 0xfacef00d); + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xba5eba11); // Aligned + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0xcafed00d); // Unaligned + /* Check direct values */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x12345678); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 0xfacef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xba5eba11); // Aligned + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0xcafed00d); // Unaligned + /* Write Log byte encoded */ + eeprom_write_dword((uint32_t*)0, 0xdecafbad); + eeprom_write_dword((uint32_t*)9, 0x87654321); + /* Write Log word encoded */ + eeprom_write_dword((uint32_t*)200, 1); + /* Write Log word value aligned */ + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xdeadc0de); // Aligned + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0x6789abcd); // Unaligned + /* Check log values */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdecafbad); + EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x87654321); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 1); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xdeadc0de); // Aligned + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0x6789abcd); // Unaligned +} + +TEST_F(EepromStm32Test, TestBlockRoundTrip) { + char src0[] = "0123456789abcdef"; + void* src1 = (void*)&src0[1]; + /* Various alignments of src & dst, Address < 0x80 */ + eeprom_write_block(src0, (void*)0, sizeof(src0)); + eeprom_write_block(src0, (void*)21, sizeof(src0)); + eeprom_write_block(src1, (void*)40, sizeof(src0) - 1); + eeprom_write_block(src1, (void*)61, sizeof(src0) - 1); + /* Various alignments of src & dst, Address >= 0x80 */ + eeprom_write_block(src0, (void*)140, sizeof(src0)); + eeprom_write_block(src0, (void*)161, sizeof(src0)); + eeprom_write_block(src1, (void*)180, sizeof(src0) - 1); + eeprom_write_block(src1, (void*)201, sizeof(src0) - 1); + + /* Check values */ + EEPROM_Init(); + + char dstBuf[256] = {0}; + char* dst0a = (char*)dstBuf; + char* dst0b = (char*)&dstBuf[20]; + char* dst1a = (char*)&dstBuf[41]; + char* dst1b = (char*)&dstBuf[61]; + char* dst0c = (char*)&dstBuf[80]; + char* dst0d = (char*)&dstBuf[100]; + char* dst1c = (char*)&dstBuf[121]; + char* dst1d = (char*)&dstBuf[141]; + eeprom_read_block((void*)dst0a, (void*)0, sizeof(src0)); + eeprom_read_block((void*)dst0b, (void*)21, sizeof(src0)); + eeprom_read_block((void*)dst1a, (void*)40, sizeof(src0) - 1); + eeprom_read_block((void*)dst1b, (void*)61, sizeof(src0) - 1); + eeprom_read_block((void*)dst0c, (void*)140, sizeof(src0)); + eeprom_read_block((void*)dst0d, (void*)161, sizeof(src0)); + eeprom_read_block((void*)dst1c, (void*)180, sizeof(src0) - 1); + eeprom_read_block((void*)dst1d, (void*)201, sizeof(src0) - 1); + EXPECT_EQ(strcmp((char*)src0, dst0a), 0); + EXPECT_EQ(strcmp((char*)src0, dst0b), 0); + EXPECT_EQ(strcmp((char*)src0, dst0c), 0); + EXPECT_EQ(strcmp((char*)src0, dst0d), 0); + EXPECT_EQ(strcmp((char*)src1, dst1a), 0); + EXPECT_EQ(strcmp((char*)src1, dst1b), 0); + EXPECT_EQ(strcmp((char*)src1, dst1c), 0); + EXPECT_EQ(strcmp((char*)src1, dst1d), 0); +} + +TEST_F(EepromStm32Test, TestCompaction) { + /* Direct writes */ + eeprom_write_dword((uint32_t*)0, 0xdeadbeef); + eeprom_write_byte((uint8_t*)4, 0x3c); + eeprom_write_word((uint16_t*)6, 0xd00d); + eeprom_write_dword((uint32_t*)150, 0xcafef00d); + eeprom_write_dword((uint32_t*)200, 0x12345678); + /* Fill write log entries */ + uint32_t i; + uint32_t val = 0xd8453c6b; + for (i = 0; i < (LOG_SIZE / (sizeof(uint32_t) * 2)); i++) { + val ^= 0x593ca5b3; + val += i; + eeprom_write_dword((uint32_t*)200, val); + } + /* Check values pre-compaction */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x3c); + EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val); + EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF); + EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF); + /* Run compaction */ + eeprom_write_byte((uint8_t*)4, 0x1f); + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x1f); + EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF); +} diff --git a/tmk_core/common/test/flash_stm32_mock.c b/tmk_core/common/test/flash_stm32_mock.c new file mode 100644 index 000000000000..1c5c45e6eb63 --- /dev/null +++ b/tmk_core/common/test/flash_stm32_mock.c @@ -0,0 +1,51 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "flash_stm32.h" +#include "flash_stm32_mock.h" + +uint8_t FlashBuf[FLASH_SIZE] = {0}; + +static bool flash_locked = true; + +FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { + if (flash_locked) return FLASH_ERROR_WRP; + Page_Address -= (uintptr_t)FlashBuf; + Page_Address -= (Page_Address % FEE_PAGE_SIZE); + if (Page_Address >= FLASH_SIZE) return FLASH_BAD_ADDRESS; + memset(&FlashBuf[Page_Address], '\xff', FEE_PAGE_SIZE); + return FLASH_COMPLETE; +} + +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { + if (flash_locked) return FLASH_ERROR_WRP; + Address -= (uintptr_t)FlashBuf; + if (Address >= FLASH_SIZE) return FLASH_BAD_ADDRESS; + uint16_t oldData = *(uint16_t*)&FlashBuf[Address]; + if (oldData == 0xFFFF || Data == 0) { + *(uint16_t*)&FlashBuf[Address] = Data; + return FLASH_COMPLETE; + } else { + return FLASH_ERROR_PG; + } +} + +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { return FLASH_COMPLETE; } +void FLASH_Unlock(void) { flash_locked = false; } +void FLASH_Lock(void) { flash_locked = true; } +void FLASH_ClearFlag(uint32_t FLASH_FLAG) {} diff --git a/tmk_core/common/test/flash_stm32_mock.h b/tmk_core/common/test/flash_stm32_mock.h new file mode 100644 index 000000000000..b49af7086229 --- /dev/null +++ b/tmk_core/common/test/flash_stm32_mock.h @@ -0,0 +1,34 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +//#define DEBUG_EEPROM + +#define FEE_FLASH_BASE FlashBuf + +#ifdef FLASH_STM32_MOCK_LARGE +# define FEE_MCU_FLASH_SIZE 64 +# define FEE_PAGE_SIZE 2048 +# define FEE_DENSITY_PAGES 16 +#else +# define FEE_MCU_FLASH_SIZE 1 +# define FEE_PAGE_SIZE 512 +# define FEE_DENSITY_PAGES 1 +#endif + +#define FLASH_SIZE (FEE_MCU_FLASH_SIZE * 1024) +extern uint8_t FlashBuf[FLASH_SIZE]; diff --git a/tmk_core/common/test/rules.mk b/tmk_core/common/test/rules.mk new file mode 100644 index 000000000000..e914c3555bf5 --- /dev/null +++ b/tmk_core/common/test/rules.mk @@ -0,0 +1,12 @@ +eeprom_stm32_DEFS := -DFLASH_STM32_MOCKED +eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) -DFLASH_STM32_MOCK_LARGE + +eeprom_stm32_INC := \ + $(TMK_PATH)/common/chibios/ +eeprom_stm32_large_INC := $(eeprom_stm32_INC) + +eeprom_stm32_SRC := \ + $(TMK_PATH)/common/test/eeprom_stm32_tests.cpp \ + $(TMK_PATH)/common/test/flash_stm32_mock.c \ + $(TMK_PATH)/common/chibios/eeprom_stm32.c +eeprom_stm32_large_SRC := $(eeprom_stm32_SRC) diff --git a/tmk_core/common/test/testlist.mk b/tmk_core/common/test/testlist.mk new file mode 100644 index 000000000000..e461da7efa0e --- /dev/null +++ b/tmk_core/common/test/testlist.mk @@ -0,0 +1 @@ +TEST_LIST += eeprom_stm32 eeprom_stm32_large diff --git a/util/stm32eeprom_parser.py b/util/stm32eeprom_parser.py new file mode 100755 index 000000000000..a38db17df004 --- /dev/null +++ b/util/stm32eeprom_parser.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python +# +# Copyright 2021 Don Kjer +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from __future__ import print_function + +import argparse +from struct import pack, unpack +import os, sys + +MAGIC_FEEA = '\xea\xff\xfe\xff' + +MAGIC_FEE9 = '\x16\x01' +EMPTY_WORD = '\xff\xff' +WORD_ENCODING = 0x8000 +VALUE_NEXT = 0x6000 +VALUE_RESERVED = 0x4000 +VALUE_ENCODED = 0x2000 +BYTE_RANGE = 0x80 + +CHUNK_SIZE = 1024 + +STRUCT_FMTS = { + 1: 'B', + 2: 'H', + 4: 'I' +} +PRINTABLE='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' + +EECONFIG_V1 = [ + ("MAGIC", 0, 2), + ("DEBUG", 2, 1), + ("DEFAULT_LAYER", 3, 1), + ("KEYMAP", 4, 1), + ("MOUSEKEY_ACCEL", 5, 1), + ("BACKLIGHT", 6, 1), + ("AUDIO", 7, 1), + ("RGBLIGHT", 8, 4), + ("UNICODEMODE", 12, 1), + ("STENOMODE", 13, 1), + ("HANDEDNESS", 14, 1), + ("KEYBOARD", 15, 4), + ("USER", 19, 4), + ("VELOCIKEY", 23, 1), + ("HAPTIC", 24, 4), + ("MATRIX", 28, 4), + ("MATRIX_EXTENDED", 32, 2), + ("KEYMAP_UPPER_BYTE", 34, 1), +] +VIABASE_V1 = 35 + +VERBOSE = False + +def parseArgs(): + parser = argparse.ArgumentParser(description='Decode an STM32 emulated eeprom dump') + parser.add_argument('-s', '--size', type=int, + help='Size of the emulated eeprom (default: input_size / 2)') + parser.add_argument('-o', '--output', help='File to write decoded eeprom to') + parser.add_argument('-y', '--layout-options-size', type=int, + help='VIA layout options size (default: 1)', default=1) + parser.add_argument('-t', '--custom-config-size', type=int, + help='VIA custom config size (default: 0)', default=0) + parser.add_argument('-l', '--layers', type=int, + help='VIA keyboard layers (default: 4)', default=4) + parser.add_argument('-r', '--rows', type=int, help='VIA matrix rows') + parser.add_argument('-c', '--cols', type=int, help='VIA matrix columns') + parser.add_argument('-m', '--macros', type=int, + help='VIA macro count (default: 16)', default=16) + parser.add_argument('-C', '--canonical', action='store_true', + help='Canonical hex+ASCII display.') + parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') + parser.add_argument('input', help='Raw contents of the STM32 flash area used to emulate eeprom') + return parser.parse_args() + + +def decodeEepromFEEA(in_file, size): + decoded=size*[None] + pos = 0 + while True: + chunk = in_file.read(CHUNK_SIZE) + for i in range(0, len(chunk), 2): + decoded[pos] = unpack('B', chunk[i])[0] + pos += 1 + if pos >= size: + break + + if len(chunk) < CHUNK_SIZE or pos >= size: + break + return decoded + +def decodeEepromFEE9(in_file, size): + decoded=size*[None] + pos = 0 + # Read compacted flash + while True: + read_size = min(size - pos, CHUNK_SIZE) + chunk = in_file.read(read_size) + for i in range(len(chunk)): + decoded[pos] = unpack('B', chunk[i])[0] ^ 0xFF + pos += 1 + if pos >= size: + break + + if len(chunk) < read_size or pos >= size: + break + if VERBOSE: + print("COMPACTED EEPROM:") + dumpBinary(decoded, True) + print("WRITE LOG:") + # Read write log + while True: + entry = in_file.read(2) + if len(entry) < 2: + print("Partial log address at position 0x%04x" % pos, file=sys.stderr) + break + pos += 2 + + if entry == EMPTY_WORD: + break + + be_entry = unpack('>H', entry)[0] + entry = unpack('H', entry)[0] + if not (entry & WORD_ENCODING): + address = entry >> 8 + decoded[address] = entry & 0xFF + if VERBOSE: + print("[0x%04x]: BYTE 0x%02x = 0x%02x" % (be_entry, address, decoded[address])) + else: + if (entry & VALUE_NEXT) == VALUE_NEXT: + # Read next word as value + value = in_file.read(2) + if len(value) < 2: + print("Partial log value at position 0x%04x" % pos, file=sys.stderr) + break + pos += 2 + address = entry & 0x1FFF + address <<= 1 + address += BYTE_RANGE + decoded[address] = unpack('B', value[0])[0] ^ 0xFF + decoded[address+1] = unpack('B', value[1])[0] ^ 0xFF + be_value = unpack('>H', value)[0] + if VERBOSE: + print("[0x%04x 0x%04x]: WORD 0x%04x = 0x%02x%02x" % (be_entry, be_value, address, decoded[address+1], decoded[address])) + else: + # Reserved for future use + if entry & VALUE_RESERVED: + if VERBOSE: + print("[0x%04x]: RESERVED 0x%04x" % (be_entry, address)) + continue + address = entry & 0x1FFF + address <<= 1 + decoded[address] = (entry & VALUE_ENCODED) >> 13 + decoded[address+1] = 0 + if VERBOSE: + print("[0x%04x]: ENCODED 0x%04x = 0x%02x%02x" % (be_entry, address, decoded[address+1], decoded[address])) + + return decoded + +def dumpBinary(data, canonical): + def display(pos, row): + print("%04x" % pos, end='') + for i in range(len(row)): + if i % 8 == 0: + print(" ", end='') + char = row[i] + if char is None: + print(" ", end='') + else: + print(" %02x" % row[i], end='') + if canonical: + print(" |", end='') + for i in range(len(row)): + char = row[i] + if char is None: + char = " " + else: + char = chr(char) + if char not in PRINTABLE: + char = "." + print(char, end='') + print("|", end='') + + print("") + + size = len(data) + empty_rows = 0 + prev_row = '' + first_repeat = True + for pos in range(0, size, 16): + row=data[pos:pos+16] + row[len(row):16] = (16-len(row))*[None] + if row == prev_row: + if first_repeat: + print("*") + first_repeat = False + else: + first_repeat = True + display(pos, row) + prev_row = row + print("%04x" % (pos+16)) + +def dumpEeconfig(data, eeconfig): + print("EECONFIG:") + for (name, pos, length) in eeconfig: + fmt = STRUCT_FMTS[length] + value = unpack(fmt, ''.join([chr(x) for x in data[pos:pos+length]]))[0] + print(("%%04x %%s = 0x%%0%dx" % (length * 2)) % (pos, name, value)) + +def dumpVia(data, base, layers, cols, rows, macros, + layout_options_size, custom_config_size): + magicYear = data[base + 0] + magicMonth = data[base + 1] + magicDay = data[base + 2] + # Sanity check + if not 10 <= magicYear <= 100: + return + if not 0 <= magicMonth <= 12: + return + if not 0 <= magicDay <= 31: + return + if cols is None or rows is None: + print("ERROR: VIA dump requires specifying --rows and --cols", file=sys.stderr) + return 2 + print("VIA:") + # Decode magic + print("%04x MAGIC = 20%02x-%02x-%02x" % (base, magicYear, magicMonth, magicDay)) + # Decode layout options + options = 0 + pos = base + 3 + for i in range(base+3, base+3+layout_options_size): + options = options << 8 + options |= data[i] + print(("%%04x LAYOUT_OPTIONS = 0x%%0%dx" % (layout_options_size * 2)) % (pos, options)) + pos += layout_options_size + custom_config_size + # Decode keycodes + keymap_size = layers * rows * cols * 2 + if (pos + keymap_size) >= (len(data) - 1): + print("ERROR: VIA keymap requires %d bytes, but only %d available" % (keymap_size, len(data) - pos)) + return 3 + for layer in range(layers): + print("%s LAYER %d %s" % ('-'*int(cols*2.5), layer, '-'*int(cols*2.5))) + for row in range(rows): + print("%04x | " % pos, end='') + for col in range(cols): + keycode = (data[pos] << 8) | (data[pos+1]) + print(" %04x" % keycode, end='') + pos += 2 + print("") + # Decode macros + for macro_num in range(macros): + macro = "" + macro_pos = pos + while pos < len(data): + char = chr(data[pos]) + pos += 1 + if char == '\x00': + print("%04x MACRO[%d] = '%s'" % (macro_pos, macro_num, macro)) + break + else: + macro += char + return 0 + + +def decodeSTM32Eeprom(input, canonical, size=None, output=None, **kwargs): + input_size = os.path.getsize(input) + if size is None: + size = input_size >> 1 + + # Read the first few bytes to check magic signature + with open(input, 'rb') as in_file: + magic=in_file.read(4) + in_file.seek(0) + + if magic == MAGIC_FEEA: + decoded = decodeEepromFEEA(in_file, size) + eeconfig = EECONFIG_V1 + via_base = VIABASE_V1 + elif magic[:2] == MAGIC_FEE9: + decoded = decodeEepromFEE9(in_file, size) + eeconfig = EECONFIG_V1 + via_base = VIABASE_V1 + else: + print("Unknown magic signature: %s" % " ".join(["0x%02x" % ord(x) for x in magic]), file=sys.stderr) + return 1 + + if output is not None: + with open(output, 'wb') as out_file: + out_file.write(pack('%dB' % len(decoded), *decoded)) + print("DECODED EEPROM:") + dumpBinary(decoded, canonical) + dumpEeconfig(decoded, eeconfig) + if kwargs['rows'] is not None and kwargs['cols'] is not None: + return dumpVia(decoded, via_base, **kwargs) + + return 0 + +def main(): + global VERBOSE + kwargs = vars(parseArgs()) + VERBOSE = kwargs.pop('verbose') + return decodeSTM32Eeprom(**kwargs) + +if __name__ == '__main__': + sys.exit(main()) From af90e1fd9ce10fc8d164c1e0b535b465f43ea148 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Thu, 15 Jul 2021 22:05:32 -0700 Subject: [PATCH 3/5] Added eeprom debugging; cleaned up flash mocking The following options were added: * debug_config.eeprom flag added (debug_eeprom) * BOOTMAGIC 'E' key will toggle debug_eeprom * Command key 'R' will toggle debug_eeprom * Command key 'P' will print EEPROM contents (stm32 only) Fix for stm32eeprom_parser.py checking via signature with wrong base --- quantum/command.c | 31 ++++ quantum/command.h | 10 ++ quantum/eeconfig.h | 1 + quantum/logging/debug.c | 1 + quantum/logging/debug.h | 4 +- tmk_core/common/chibios/eeprom_stm32.c | 182 ++++++++------------ tmk_core/common/chibios/eeprom_stm32.h | 11 +- tmk_core/common/chibios/flash_stm32.h | 4 + tmk_core/common/test/eeprom_stm32_tests.cpp | 5 +- tmk_core/common/test/flash_stm32_mock.c | 7 +- tmk_core/common/test/flash_stm32_mock.h | 34 ---- tmk_core/common/test/rules.mk | 15 +- tmk_core/common/test/testlist.mk | 2 +- util/stm32eeprom_parser.py | 9 +- 14 files changed, 145 insertions(+), 171 deletions(-) delete mode 100644 tmk_core/common/test/flash_stm32_mock.h diff --git a/quantum/command.c b/quantum/command.c index 2ff640a7bcd6..bdc681dedf4f 100644 --- a/quantum/command.c +++ b/quantum/command.c @@ -35,6 +35,10 @@ along with this program. If not, see . #include "quantum.h" #include "version.h" +#ifdef STM32_EEPROM_ENABLE +# include "eeprom_stm32.h" +#endif + #ifdef BACKLIGHT_ENABLE # include "backlight.h" #endif @@ -113,6 +117,10 @@ static void command_common_help(void) { STR(MAGIC_KEY_DEBUG_KBD) ": Keyboard Debug Toggle" " - Show keypress report\n" STR(MAGIC_KEY_DEBUG_MOUSE) ": Debug Mouse Toggle\n" + STR(MAGIC_KEY_DEBUG_EEPROM) ": Debug EEPROM Toggle\n" +#ifdef STM32_EEPROM_ENABLE + STR(MAGIC_KEY_PRINT_EEPROM) ": Print EEPROM Contents\n" +#endif STR(MAGIC_KEY_VERSION) ": Version\n" STR(MAGIC_KEY_STATUS) ": Status\n" STR(MAGIC_KEY_CONSOLE) ": Activate Console Mode\n" @@ -259,12 +267,14 @@ static void print_eeconfig(void) { ".matrix: %u\n" ".keyboard: %u\n" ".mouse: %u\n" + ".eeprom: %u\n" , dc.raw , dc.enable , dc.matrix , dc.keyboard , dc.mouse + , dc.eeprom ); /* clang-format on */ keymap_config_t kc; @@ -373,6 +383,7 @@ static bool command_common(uint8_t code) { debug_matrix = false; debug_keyboard = false; debug_mouse = false; + debug_eeprom = false; debug_enable = false; command_console_help(); print("C> "); @@ -396,6 +407,7 @@ static bool command_common(uint8_t code) { debug_matrix = false; debug_keyboard = false; debug_mouse = false; + debug_eeprom = false; } break; @@ -432,6 +444,25 @@ static bool command_common(uint8_t code) { } break; + // debug eeprom toggle + case MAGIC_KC(MAGIC_KEY_DEBUG_EEPROM): + debug_eeprom = !debug_eeprom; + if (debug_eeprom) { + print("\neeprom: on\n"); + debug_enable = true; + } else { + print("\neeprom: off\n"); + } + break; + +#ifdef STM32_EEPROM_ENABLE + // print eeprom contents + case MAGIC_KC(MAGIC_KEY_PRINT_EEPROM): + print("EEPROM Contents:\n"); + print_eeprom(); + break; +#endif + // print version case MAGIC_KC(MAGIC_KEY_VERSION): print_version(); diff --git a/quantum/command.h b/quantum/command.h index 676507d3bd39..9d0eb9b4e390 100644 --- a/quantum/command.h +++ b/quantum/command.h @@ -73,6 +73,16 @@ bool command_proc(uint8_t code); # define MAGIC_KEY_DEBUG_MOUSE M #endif +#ifdef STM32_EEPROM_ENABLE +# ifndef MAGIC_KEY_PRINT_EEPROM +# define MAGIC_KEY_PRINT_EEPROM P +# endif +#endif + +#ifndef MAGIC_KEY_DEBUG_EEPROM +# define MAGIC_KEY_DEBUG_EEPROM R +#endif + #ifndef MAGIC_KEY_VERSION # define MAGIC_KEY_VERSION V #endif diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index bd39971b2c50..1b3df82edfac 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -60,6 +60,7 @@ along with this program. If not, see . #define EECONFIG_DEBUG_MATRIX (1 << 1) #define EECONFIG_DEBUG_KEYBOARD (1 << 2) #define EECONFIG_DEBUG_MOUSE (1 << 3) +#define EECONFIG_DEBUG_EEPROM (1 << 4) /* keyconf bit */ #define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0) diff --git a/quantum/logging/debug.c b/quantum/logging/debug.c index ea62deaa8ccc..098bc9294e28 100644 --- a/quantum/logging/debug.c +++ b/quantum/logging/debug.c @@ -21,5 +21,6 @@ debug_config_t debug_config = { .matrix = false, // .keyboard = false, // .mouse = false, // + .eeprom = false, // .reserved = 0 // }; diff --git a/quantum/logging/debug.h b/quantum/logging/debug.h index 841531035691..14f95c6827c1 100644 --- a/quantum/logging/debug.h +++ b/quantum/logging/debug.h @@ -33,7 +33,8 @@ typedef union { bool matrix : 1; bool keyboard : 1; bool mouse : 1; - uint8_t reserved : 4; + bool eeprom : 1; + uint8_t reserved : 3; }; uint8_t raw; } debug_config_t; @@ -49,6 +50,7 @@ extern debug_config_t debug_config; #define debug_matrix (debug_config.matrix) #define debug_keyboard (debug_config.keyboard) #define debug_mouse (debug_config.mouse) +#define debug_eeprom (debug_config.eeprom) /* * Debug print utils diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c index e2138debd500..7199895cafeb 100644 --- a/tmk_core/common/chibios/eeprom_stm32.c +++ b/tmk_core/common/chibios/eeprom_stm32.c @@ -20,18 +20,9 @@ #include #include +#include "debug.h" #include "eeprom_stm32.h" #include "flash_stm32.h" -#ifdef FLASH_STM32_MOCKED -# include "flash_stm32_mock.h" -#endif -#ifdef DEBUG_EEPROM -# ifdef FLASH_STM32_MOCKED -# define dprintf printf -# else -# include "debug.h" -# endif -#endif /* * We emulate eeprom by writing a snapshot compacted view of eeprom contents, @@ -264,8 +255,30 @@ static uint8_t *DataBuf = (uint8_t *)WordBuf; /* Pointer to the first available slot within the write log */ static uint16_t *empty_slot; -#ifdef DEBUG_EEPROM -void dumpEepromDataBuf(void) { +/* + * Debug print utils + */ +#ifndef NO_DEBUG + +# define eeprom_println(s) \ + do { \ + if (debug_eeprom) println(s); \ + } while (0) + +# define eeprom_printf(fmt, ...) \ + do { \ + if (debug_eeprom) xprintf(fmt, ##__VA_ARGS__); \ + } while (0) + +#else /* NO_DEBUG */ + +# 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) { @@ -285,22 +298,22 @@ void dumpEepromDataBuf(void) { /* Repeat empty row */ if (empty_rows == 2) { /* Only display the first repeat empty row */ - dprintf("*\n"); + println("*"); } i += 15; continue; } - dprintf("%04x", i); + xprintf("%04x", i); } - if (i % 8 == 0) dprintf(" "); + if (i % 8 == 0) print(" "); - dprintf(" %02x", DataBuf[i]); + xprintf(" %02x", DataBuf[i]); if ((i + 1) % 16 == 0) { - dprintf("\n"); + println(""); } } -} #endif +} uint16_t EEPROM_Init(void) { /* Load emulated eeprom contents from compacted flash into memory */ @@ -310,11 +323,11 @@ uint16_t EEPROM_Init(void) { *dest = ~*src; } -#ifdef DEBUG_EEPROM - dprintf("EEPROM_Init Compacted Pages:\n"); - dumpEepromDataBuf(); - dprintf("EEPROM_Init Write Log:\n"); -#endif + if (debug_eeprom) { + println("EEPROM_Init Compacted Pages:"); + print_eeprom(); + println("EEPROM_Init Write Log:"); + } /* Replay write log */ uint16_t *log_addr; @@ -328,9 +341,7 @@ uint16_t EEPROM_Init(void) { uint8_t bvalue = (uint8_t)address; address >>= 8; DataBuf[address] = bvalue; -#ifdef DEBUG_EEPROM - dprintf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); -#endif + eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); } else { uint16_t wvalue; /* Check if value is in next word */ @@ -341,9 +352,7 @@ uint16_t EEPROM_Init(void) { } wvalue = ~*log_addr; if (!wvalue) { -#ifdef DEBUG_EEPROM - dprintf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); -#endif + eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); /* Possibly incomplete write. Ignore and continue */ continue; } @@ -354,9 +363,7 @@ uint16_t EEPROM_Init(void) { } else { /* Reserved for future use */ if (address & FEE_VALUE_RESERVED) { -#ifdef DEBUG_EEPROM - dprintf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); -#endif + eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); continue; } /* Optimization for 0 or 1 values. */ @@ -365,25 +372,20 @@ uint16_t EEPROM_Init(void) { address <<= 1; } if (address < FEE_DENSITY_BYTES) { -#ifdef DEBUG_EEPROM - dprintf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); -#endif + 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); } -#ifdef DEBUG_EEPROM - else { - dprintf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); - } -#endif } } empty_slot = log_addr; -#ifdef DEBUG_EEPROM - dprintf("EEPROM_Init Final DataBuf:\n"); - dumpEepromDataBuf(); -#endif + if (debug_eeprom) { + println("EEPROM_Init Final DataBuf:"); + print_eeprom(); + } return FEE_DENSITY_BYTES; } @@ -393,25 +395,19 @@ static void eeprom_clear(void) { FLASH_Unlock(); for (uint16_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) { -#ifdef DEBUG_EEPROM - dprintf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); -#endif + 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_Lock(); empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; -#ifdef DEBUG_EEPROM - dprintf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); -#endif + eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); } /* Erase emulated eeprom */ void EEPROM_Erase(void) { -#ifdef DEBUG_EEPROM - dprintf("EEPROM_Erase\n"); -#endif + eeprom_println("EEPROM_Erase"); /* Erase compacted pages and write log */ eeprom_clear(); /* re-initialize to reset DataBuf */ @@ -434,9 +430,7 @@ static uint8_t eeprom_compact(void) { for (; dest < FEE_PAGE_LAST_ADDRESS; ++src, dest += 2) { value = *src; if (value) { -#ifdef DEBUG_EEPROM - dprintf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value); -#endif + 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; } @@ -444,10 +438,10 @@ static uint8_t eeprom_compact(void) { FLASH_Lock(); -#ifdef DEBUG_EEPROM - dprintf("eeprom_compacted:\n"); - dumpEepromDataBuf(); -#endif + if (debug_eeprom) { + println("eeprom_compacted:"); + print_eeprom(); + } return final_status; } @@ -463,9 +457,7 @@ static uint8_t eeprom_write_direct_entry(uint16_t Address) { FLASH_Unlock(); -#ifdef DEBUG_EEPROM - dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); -#endif + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); FLASH_Lock(); @@ -478,9 +470,7 @@ static uint8_t eeprom_write_log_word_entry(uint16_t Address) { FLASH_Status final_status = FLASH_COMPLETE; uint16_t value = *(uint16_t *)(&DataBuf[Address]); -#ifdef DEBUG_EEPROM - dprintf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); -#endif + eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); /* MSB signifies the lowest 128-byte optimization is not in effect */ uint16_t encoding = FEE_WORD_ENCODING; @@ -509,16 +499,12 @@ static uint8_t eeprom_write_log_word_entry(uint16_t Address) { FLASH_Unlock(); /* address */ -#ifdef DEBUG_EEPROM - dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address); -#endif + 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)) { -#ifdef DEBUG_EEPROM - dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value); -#endif + 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; } @@ -529,9 +515,7 @@ static uint8_t eeprom_write_log_word_entry(uint16_t Address) { } static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { -#ifdef DEBUG_EEPROM - dprintf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); -#endif + 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) { @@ -546,9 +530,7 @@ static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { uint16_t value = (Address << 8) | DataBuf[Address]; /* write to flash */ -#ifdef DEBUG_EEPROM - dprintf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value); -#endif + 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(); @@ -559,25 +541,19 @@ static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { /* if the address is out-of-bounds, do nothing */ if (Address >= FEE_DENSITY_BYTES) { -#ifdef DEBUG_EEPROM - dprintf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); -#endif + 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) { -#ifdef DEBUG_EEPROM - dprintf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); -#endif + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); return 0; } /* keep DataBuf cache in sync */ DataBuf[Address] = DataByte; -#ifdef DEBUG_EEPROM - dprintf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); -#endif + 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 */ @@ -590,20 +566,16 @@ uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { status = eeprom_write_log_word_entry(Address & 0xFFFE); } } -#ifdef DEBUG_EEPROM if (status != 0 && status != FLASH_COMPLETE) { - dprintf("EEPROM_WriteDataByte [STATUS == %d]\n", status); + eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status); } -#endif 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) { -#ifdef DEBUG_EEPROM - dprintf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); -#endif + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); return FLASH_BAD_ADDRESS; } @@ -613,28 +585,22 @@ uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { final_status = EEPROM_WriteDataByte(Address, DataWord); FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8); if (status != FLASH_COMPLETE) final_status = status; -#ifdef DEBUG_EEPROM if (final_status != 0 && final_status != FLASH_COMPLETE) { - dprintf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); } -#endif return final_status; } /* if the value is the same, don't bother writing it */ uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); if (oldValue == DataWord) { -#ifdef DEBUG_EEPROM - dprintf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); -#endif + 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; -#ifdef DEBUG_EEPROM - dprintf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); -#endif + 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 */ @@ -658,11 +624,9 @@ uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { final_status = eeprom_write_log_word_entry(Address); } } -#ifdef DEBUG_EEPROM if (final_status != 0 && final_status != FLASH_COMPLETE) { - dprintf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); } -#endif return final_status; } @@ -673,9 +637,7 @@ uint8_t EEPROM_ReadDataByte(uint16_t Address) { DataByte = DataBuf[Address]; } -#ifdef DEBUG_EEPROM - dprintf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); -#endif + eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); return DataByte; } @@ -692,9 +654,7 @@ uint16_t EEPROM_ReadDataWord(uint16_t Address) { } } -#ifdef DEBUG_EEPROM - dprintf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); -#endif + eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); return DataWord; } diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h index d943ba0eb632..8fcfb556b809 100644 --- a/tmk_core/common/chibios/eeprom_stm32.h +++ b/tmk_core/common/chibios/eeprom_stm32.h @@ -23,13 +23,6 @@ #pragma once -#ifdef FLASH_STM32_MOCKED -# include -#else -# include -# include -#endif - uint16_t EEPROM_Init(void); void EEPROM_Erase(void); uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); @@ -37,6 +30,4 @@ uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord); uint8_t EEPROM_ReadDataByte(uint16_t Address); uint16_t EEPROM_ReadDataWord(uint16_t Address); -#ifdef DEBUG_EEPROM -void dumpEepromDataBuf(void); -#endif +void print_eeprom(void); diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h index 75cd27d120a0..9c6a7cc50f80 100644 --- a/tmk_core/common/chibios/flash_stm32.h +++ b/tmk_core/common/chibios/flash_stm32.h @@ -24,6 +24,10 @@ extern "C" { #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)) diff --git a/tmk_core/common/test/eeprom_stm32_tests.cpp b/tmk_core/common/test/eeprom_stm32_tests.cpp index fb29d2858c69..aa84492b8796 100644 --- a/tmk_core/common/test/eeprom_stm32_tests.cpp +++ b/tmk_core/common/test/eeprom_stm32_tests.cpp @@ -18,7 +18,6 @@ extern "C" { #include "flash_stm32.h" -#include "flash_stm32_mock.h" #include "eeprom_stm32.h" #include "eeprom.h" } @@ -49,7 +48,7 @@ extern "C" { #define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_DENSITY_PAGES / 2) #define LOG_SIZE EEPROM_SIZE -#define LOG_BASE (FLASH_SIZE - LOG_SIZE) +#define LOG_BASE (MOCK_FLASH_SIZE - LOG_SIZE) #define EEPROM_BASE (LOG_BASE - EEPROM_SIZE) /* Log encoding helpers */ @@ -82,7 +81,7 @@ TEST_F(EepromStm32Test, TestErase) { TEST_F(EepromStm32Test, TestReadGarbage) { uint8_t garbage = 0x3c; - for (int i = 0; i < FLASH_SIZE; ++i) { + for (int i = 0; i < MOCK_FLASH_SIZE; ++i) { garbage ^= 0xa3; garbage += i; FlashBuf[i] = garbage; diff --git a/tmk_core/common/test/flash_stm32_mock.c b/tmk_core/common/test/flash_stm32_mock.c index 1c5c45e6eb63..1b81d81f9ae3 100644 --- a/tmk_core/common/test/flash_stm32_mock.c +++ b/tmk_core/common/test/flash_stm32_mock.c @@ -17,9 +17,8 @@ #include #include #include "flash_stm32.h" -#include "flash_stm32_mock.h" -uint8_t FlashBuf[FLASH_SIZE] = {0}; +uint8_t FlashBuf[MOCK_FLASH_SIZE] = {0}; static bool flash_locked = true; @@ -27,7 +26,7 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { if (flash_locked) return FLASH_ERROR_WRP; Page_Address -= (uintptr_t)FlashBuf; Page_Address -= (Page_Address % FEE_PAGE_SIZE); - if (Page_Address >= FLASH_SIZE) return FLASH_BAD_ADDRESS; + if (Page_Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS; memset(&FlashBuf[Page_Address], '\xff', FEE_PAGE_SIZE); return FLASH_COMPLETE; } @@ -35,7 +34,7 @@ FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { if (flash_locked) return FLASH_ERROR_WRP; Address -= (uintptr_t)FlashBuf; - if (Address >= FLASH_SIZE) return FLASH_BAD_ADDRESS; + if (Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS; uint16_t oldData = *(uint16_t*)&FlashBuf[Address]; if (oldData == 0xFFFF || Data == 0) { *(uint16_t*)&FlashBuf[Address] = Data; diff --git a/tmk_core/common/test/flash_stm32_mock.h b/tmk_core/common/test/flash_stm32_mock.h deleted file mode 100644 index b49af7086229..000000000000 --- a/tmk_core/common/test/flash_stm32_mock.h +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright 2021 by Don Kjer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -//#define DEBUG_EEPROM - -#define FEE_FLASH_BASE FlashBuf - -#ifdef FLASH_STM32_MOCK_LARGE -# define FEE_MCU_FLASH_SIZE 64 -# define FEE_PAGE_SIZE 2048 -# define FEE_DENSITY_PAGES 16 -#else -# define FEE_MCU_FLASH_SIZE 1 -# define FEE_PAGE_SIZE 512 -# define FEE_DENSITY_PAGES 1 -#endif - -#define FLASH_SIZE (FEE_MCU_FLASH_SIZE * 1024) -extern uint8_t FlashBuf[FLASH_SIZE]; diff --git a/tmk_core/common/test/rules.mk b/tmk_core/common/test/rules.mk index e914c3555bf5..e47e5880c521 100644 --- a/tmk_core/common/test/rules.mk +++ b/tmk_core/common/test/rules.mk @@ -1,12 +1,23 @@ -eeprom_stm32_DEFS := -DFLASH_STM32_MOCKED -eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) -DFLASH_STM32_MOCK_LARGE +eeprom_stm32_DEFS := -DFLASH_STM32_MOCKED -DNO_PRINT -DFEE_FLASH_BASE=FlashBuf +eeprom_stm32_tiny_DEFS := $(eeprom_stm32_DEFS) \ + -DFEE_MCU_FLASH_SIZE=1 \ + -DMOCK_FLASH_SIZE=1024 \ + -DFEE_PAGE_SIZE=512 \ + -DFEE_DENSITY_PAGES=1 +eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) \ + -DFEE_MCU_FLASH_SIZE=64 \ + -DMOCK_FLASH_SIZE=65536 \ + -DFEE_PAGE_SIZE=2048 \ + -DFEE_DENSITY_PAGES=16 eeprom_stm32_INC := \ $(TMK_PATH)/common/chibios/ +eeprom_stm32_tiny_INC := $(eeprom_stm32_INC) eeprom_stm32_large_INC := $(eeprom_stm32_INC) eeprom_stm32_SRC := \ $(TMK_PATH)/common/test/eeprom_stm32_tests.cpp \ $(TMK_PATH)/common/test/flash_stm32_mock.c \ $(TMK_PATH)/common/chibios/eeprom_stm32.c +eeprom_stm32_tiny_SRC := $(eeprom_stm32_SRC) eeprom_stm32_large_SRC := $(eeprom_stm32_SRC) diff --git a/tmk_core/common/test/testlist.mk b/tmk_core/common/test/testlist.mk index e461da7efa0e..51a9638bb968 100644 --- a/tmk_core/common/test/testlist.mk +++ b/tmk_core/common/test/testlist.mk @@ -1 +1 @@ -TEST_LIST += eeprom_stm32 eeprom_stm32_large +TEST_LIST += eeprom_stm32_tiny eeprom_stm32_large diff --git a/util/stm32eeprom_parser.py b/util/stm32eeprom_parser.py index a38db17df004..b124f713d543 100755 --- a/util/stm32eeprom_parser.py +++ b/util/stm32eeprom_parser.py @@ -226,11 +226,10 @@ def dumpVia(data, base, layers, cols, rows, macros, magicMonth = data[base + 1] magicDay = data[base + 2] # Sanity check - if not 10 <= magicYear <= 100: - return - if not 0 <= magicMonth <= 12: - return - if not 0 <= magicDay <= 31: + if not 10 <= magicYear <= 0x99 or \ + not 0 <= magicMonth <= 0x12 or \ + not 0 <= magicDay <= 0x31: + print("ERROR: VIA Signature is not valid; Year:%x, Month:%x, Day:%x" % (magicYear, magicMonth, magicDay)) return if cols is None or rows is None: print("ERROR: VIA dump requires specifying --rows and --cols", file=sys.stderr) From 6cd8f27229e8f0759b22a015a480e2fcc1fe0192 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Thu, 12 Aug 2021 16:16:34 -0700 Subject: [PATCH 4/5] Fix for nk65 keyboard --- keyboards/nk65/config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/keyboards/nk65/config.h b/keyboards/nk65/config.h index fa14e9ffbfdd..a178307cc03d 100755 --- a/keyboards/nk65/config.h +++ b/keyboards/nk65/config.h @@ -148,6 +148,9 @@ along with this program. If not, see . * both 128kb and 256kb versions of F303. * Register 0x1FFFF7CC holds the size of the flash memory. */ +#ifndef FLASHSIZE_BASE +# define FLASHSIZE_BASE ((uint32_t)0x1FFFF7CCU) /*!< FLASH Size register base address */ +#endif #define EEPROM_START_ADDRESS #define FEE_MCU_FLASH_SIZE \ ({ \ From 578ddc9b7455f364ecf7fb47a6f67b546c90e5a5 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 23 Aug 2021 23:10:55 +0100 Subject: [PATCH 5/5] Remove debug_eeprom for now --- quantum/command.c | 31 -------------------------- quantum/command.h | 10 --------- quantum/eeconfig.h | 1 - quantum/logging/debug.c | 1 - quantum/logging/debug.h | 4 +--- tmk_core/common/chibios/eeprom_stm32.c | 16 ++++++------- 6 files changed, 8 insertions(+), 55 deletions(-) diff --git a/quantum/command.c b/quantum/command.c index bdc681dedf4f..2ff640a7bcd6 100644 --- a/quantum/command.c +++ b/quantum/command.c @@ -35,10 +35,6 @@ along with this program. If not, see . #include "quantum.h" #include "version.h" -#ifdef STM32_EEPROM_ENABLE -# include "eeprom_stm32.h" -#endif - #ifdef BACKLIGHT_ENABLE # include "backlight.h" #endif @@ -117,10 +113,6 @@ static void command_common_help(void) { STR(MAGIC_KEY_DEBUG_KBD) ": Keyboard Debug Toggle" " - Show keypress report\n" STR(MAGIC_KEY_DEBUG_MOUSE) ": Debug Mouse Toggle\n" - STR(MAGIC_KEY_DEBUG_EEPROM) ": Debug EEPROM Toggle\n" -#ifdef STM32_EEPROM_ENABLE - STR(MAGIC_KEY_PRINT_EEPROM) ": Print EEPROM Contents\n" -#endif STR(MAGIC_KEY_VERSION) ": Version\n" STR(MAGIC_KEY_STATUS) ": Status\n" STR(MAGIC_KEY_CONSOLE) ": Activate Console Mode\n" @@ -267,14 +259,12 @@ static void print_eeconfig(void) { ".matrix: %u\n" ".keyboard: %u\n" ".mouse: %u\n" - ".eeprom: %u\n" , dc.raw , dc.enable , dc.matrix , dc.keyboard , dc.mouse - , dc.eeprom ); /* clang-format on */ keymap_config_t kc; @@ -383,7 +373,6 @@ static bool command_common(uint8_t code) { debug_matrix = false; debug_keyboard = false; debug_mouse = false; - debug_eeprom = false; debug_enable = false; command_console_help(); print("C> "); @@ -407,7 +396,6 @@ static bool command_common(uint8_t code) { debug_matrix = false; debug_keyboard = false; debug_mouse = false; - debug_eeprom = false; } break; @@ -444,25 +432,6 @@ static bool command_common(uint8_t code) { } break; - // debug eeprom toggle - case MAGIC_KC(MAGIC_KEY_DEBUG_EEPROM): - debug_eeprom = !debug_eeprom; - if (debug_eeprom) { - print("\neeprom: on\n"); - debug_enable = true; - } else { - print("\neeprom: off\n"); - } - break; - -#ifdef STM32_EEPROM_ENABLE - // print eeprom contents - case MAGIC_KC(MAGIC_KEY_PRINT_EEPROM): - print("EEPROM Contents:\n"); - print_eeprom(); - break; -#endif - // print version case MAGIC_KC(MAGIC_KEY_VERSION): print_version(); diff --git a/quantum/command.h b/quantum/command.h index 9d0eb9b4e390..676507d3bd39 100644 --- a/quantum/command.h +++ b/quantum/command.h @@ -73,16 +73,6 @@ bool command_proc(uint8_t code); # define MAGIC_KEY_DEBUG_MOUSE M #endif -#ifdef STM32_EEPROM_ENABLE -# ifndef MAGIC_KEY_PRINT_EEPROM -# define MAGIC_KEY_PRINT_EEPROM P -# endif -#endif - -#ifndef MAGIC_KEY_DEBUG_EEPROM -# define MAGIC_KEY_DEBUG_EEPROM R -#endif - #ifndef MAGIC_KEY_VERSION # define MAGIC_KEY_VERSION V #endif diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index 1b3df82edfac..bd39971b2c50 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -60,7 +60,6 @@ along with this program. If not, see . #define EECONFIG_DEBUG_MATRIX (1 << 1) #define EECONFIG_DEBUG_KEYBOARD (1 << 2) #define EECONFIG_DEBUG_MOUSE (1 << 3) -#define EECONFIG_DEBUG_EEPROM (1 << 4) /* keyconf bit */ #define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0) diff --git a/quantum/logging/debug.c b/quantum/logging/debug.c index 098bc9294e28..ea62deaa8ccc 100644 --- a/quantum/logging/debug.c +++ b/quantum/logging/debug.c @@ -21,6 +21,5 @@ debug_config_t debug_config = { .matrix = false, // .keyboard = false, // .mouse = false, // - .eeprom = false, // .reserved = 0 // }; diff --git a/quantum/logging/debug.h b/quantum/logging/debug.h index 14f95c6827c1..841531035691 100644 --- a/quantum/logging/debug.h +++ b/quantum/logging/debug.h @@ -33,8 +33,7 @@ typedef union { bool matrix : 1; bool keyboard : 1; bool mouse : 1; - bool eeprom : 1; - uint8_t reserved : 3; + uint8_t reserved : 4; }; uint8_t raw; } debug_config_t; @@ -50,7 +49,6 @@ extern debug_config_t debug_config; #define debug_matrix (debug_config.matrix) #define debug_keyboard (debug_config.keyboard) #define debug_mouse (debug_config.mouse) -#define debug_eeprom (debug_config.eeprom) /* * Debug print utils diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c index 7199895cafeb..5bf852fde1b8 100644 --- a/tmk_core/common/chibios/eeprom_stm32.c +++ b/tmk_core/common/chibios/eeprom_stm32.c @@ -255,23 +255,21 @@ static uint8_t *DataBuf = (uint8_t *)WordBuf; /* Pointer to the first available slot within the write log */ static uint16_t *empty_slot; +// #define DEBUG_EEPROM_OUTPUT + /* * Debug print utils */ -#ifndef NO_DEBUG -# define eeprom_println(s) \ - do { \ - if (debug_eeprom) println(s); \ - } while (0) +#if defined(DEBUG_EEPROM_OUTPUT) -# define eeprom_printf(fmt, ...) \ - do { \ - if (debug_eeprom) xprintf(fmt, ##__VA_ARGS__); \ - } while (0) +# define debug_eeprom debug_enable +# define eeprom_println(s) println(s) +# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__); #else /* NO_DEBUG */ +# define debug_eeprom false # define eeprom_println(s) # define eeprom_printf(fmt, ...)