mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-31 13:22:31 +01:00 
			
		
		
		
	eeprom_stm32: implement high density wear leveling (#12567)
* eeprom_stm32: implement wear leveling Update EECONFIG_MAGIC_NUMBER eeprom_stm32: check emulated eeprom size is large enough * eeprom_stm32: Increasing simulated EEPROM density on stm32 * Adding utility script to decode emulated eeprom * Adding unit tests * Applying qmk cformat changes * cleaned up flash mocking * Fix for stm32eeprom_parser.py checking via signature with wrong base * Fix for nk65 keyboard Co-authored-by: Ilya Zhuravlev <whatever@xyz.is> Co-authored-by: zvecr <git@zvecr.com>
This commit is contained in:
		
							parent
							
								
									2481e109a0
								
							
						
					
					
						commit
						e756a21636
					
				| @ -56,6 +56,7 @@ include $(TMK_PATH)/common.mk | |||||||
| include $(QUANTUM_PATH)/debounce/tests/rules.mk | include $(QUANTUM_PATH)/debounce/tests/rules.mk | ||||||
| include $(QUANTUM_PATH)/sequencer/tests/rules.mk | include $(QUANTUM_PATH)/sequencer/tests/rules.mk | ||||||
| include $(QUANTUM_PATH)/serial_link/tests/rules.mk | include $(QUANTUM_PATH)/serial_link/tests/rules.mk | ||||||
|  | include $(TMK_PATH)/common/test/rules.mk | ||||||
| ifneq ($(filter $(FULL_TESTS),$(TEST)),) | ifneq ($(filter $(FULL_TESTS),$(TEST)),) | ||||||
| include build_full_test.mk | include build_full_test.mk | ||||||
| endif | endif | ||||||
|  | |||||||
| @ -148,6 +148,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. | |||||||
|  * both 128kb and 256kb versions of F303. |  * both 128kb and 256kb versions of F303. | ||||||
|  * Register 0x1FFFF7CC holds the size of the flash memory. |  * 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 EEPROM_START_ADDRESS | ||||||
| #define FEE_MCU_FLASH_SIZE                              \ | #define FEE_MCU_FLASH_SIZE                              \ | ||||||
| ({                                                      \ | ({                                                      \ | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| #ifndef EECONFIG_MAGIC_NUMBER | #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 | #endif | ||||||
| #define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF | #define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ FULL_TESTS := $(TEST_LIST) | |||||||
| include $(ROOT_DIR)/quantum/debounce/tests/testlist.mk | include $(ROOT_DIR)/quantum/debounce/tests/testlist.mk | ||||||
| include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk | include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk | ||||||
| include $(ROOT_DIR)/quantum/serial_link/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 | define VALIDATE_TEST_LIST | ||||||
|     ifneq ($1,) |     ifneq ($1,) | ||||||
|  | |||||||
| @ -14,185 +14,751 @@ | |||||||
|  * Artur F. |  * Artur F. | ||||||
|  * |  * | ||||||
|  * Modifications for QMK and STM32F303 by Yiancar |  * 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 <stdio.h> | #include <stdio.h> | ||||||
| #include <string.h> | #include <stdbool.h> | ||||||
|  | #include "debug.h" | ||||||
| #include "eeprom_stm32.h" | #include "eeprom_stm32.h" | ||||||
| /*****************************************************************************
 | #include "flash_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 ---------------------------------------------------------*/ |  * We emulate eeprom by writing a snapshot compacted view of eeprom contents, | ||||||
| /* Functions -----------------------------------------------------------------*/ |  * 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 */ | ||||||
|  | /* *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; | ||||||
|  | 
 | ||||||
|  | // #define DEBUG_EEPROM_OUTPUT
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Debug print utils | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #if defined(DEBUG_EEPROM_OUTPUT) | ||||||
|  | 
 | ||||||
|  | #    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, ...) | ||||||
|  | 
 | ||||||
|  | #endif /* NO_DEBUG */ | ||||||
|  | 
 | ||||||
|  | void print_eeprom(void) { | ||||||
|  | #ifndef NO_DEBUG | ||||||
|  |     int empty_rows = 0; | ||||||
|  |     for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) { | ||||||
|  |         if (i % 16 == 0) { | ||||||
|  |             if (i >= FEE_DENSITY_BYTES - 16) { | ||||||
|  |                 /* Make sure we display the last row */ | ||||||
|  |                 empty_rows = 0; | ||||||
|  |             } | ||||||
|  |             /* Check if this row is uninitialized */ | ||||||
|  |             ++empty_rows; | ||||||
|  |             for (uint16_t j = 0; j < 16; j++) { | ||||||
|  |                 if (DataBuf[i + j]) { | ||||||
|  |                     empty_rows = 0; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (empty_rows > 1) { | ||||||
|  |                 /* Repeat empty row */ | ||||||
|  |                 if (empty_rows == 2) { | ||||||
|  |                     /* Only display the first repeat empty row */ | ||||||
|  |                     println("*"); | ||||||
|  |                 } | ||||||
|  |                 i += 15; | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             xprintf("%04x", i); | ||||||
|  |         } | ||||||
|  |         if (i % 8 == 0) print(" "); | ||||||
|  | 
 | ||||||
|  |         xprintf(" %02x", DataBuf[i]); | ||||||
|  |         if ((i + 1) % 16 == 0) { | ||||||
|  |             println(""); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 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) { | uint16_t EEPROM_Init(void) { | ||||||
|     // unlock flash
 |     /* Load emulated eeprom contents from compacted flash into memory */ | ||||||
|     FLASH_Unlock(); |     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; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Clear Flags
 |     if (debug_eeprom) { | ||||||
|     // FLASH_ClearFlag(FLASH_SR_EOP|FLASH_SR_PGERR|FLASH_SR_WRPERR);
 |         println("EEPROM_Init Compacted Pages:"); | ||||||
|  |         print_eeprom(); | ||||||
|  |         println("EEPROM_Init Write Log:"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* 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; | ||||||
|  |         } | ||||||
|  |         /* Check for lowest 128-bytes optimization */ | ||||||
|  |         if (!(address & FEE_WORD_ENCODING)) { | ||||||
|  |             uint8_t bvalue = (uint8_t)address; | ||||||
|  |             address >>= 8; | ||||||
|  |             DataBuf[address] = bvalue; | ||||||
|  |             eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); | ||||||
|  |         } else { | ||||||
|  |             uint16_t wvalue; | ||||||
|  |             /* Check if value is in next word */ | ||||||
|  |             if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) { | ||||||
|  |                 /* Read value from next word */ | ||||||
|  |                 if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 wvalue = ~*log_addr; | ||||||
|  |                 if (!wvalue) { | ||||||
|  |                     eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); | ||||||
|  |                     /* Possibly incomplete write.  Ignore and continue */ | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 address &= 0x1FFF; | ||||||
|  |                 address <<= 1; | ||||||
|  |                 /* Writes to addresses less than 128 are byte log entries */ | ||||||
|  |                 address += FEE_BYTE_RANGE; | ||||||
|  |             } else { | ||||||
|  |                 /* Reserved for future use */ | ||||||
|  |                 if (address & FEE_VALUE_RESERVED) { | ||||||
|  |                     eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 /* Optimization for 0 or 1 values. */ | ||||||
|  |                 wvalue = (address & FEE_VALUE_ENCODED) >> 13; | ||||||
|  |                 address &= 0x1FFF; | ||||||
|  |                 address <<= 1; | ||||||
|  |             } | ||||||
|  |             if (address < FEE_DENSITY_BYTES) { | ||||||
|  |                 eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); | ||||||
|  |                 *(uint16_t *)(&DataBuf[address]) = wvalue; | ||||||
|  |             } else { | ||||||
|  |                 eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     empty_slot = log_addr; | ||||||
|  | 
 | ||||||
|  |     if (debug_eeprom) { | ||||||
|  |         println("EEPROM_Init Final DataBuf:"); | ||||||
|  |         print_eeprom(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return FEE_DENSITY_BYTES; |     return FEE_DENSITY_BYTES; | ||||||
| } | } | ||||||
| /*****************************************************************************
 |  | ||||||
|  *  Erase the whole reserved Flash Space used for user Data |  | ||||||
|  ******************************************************************************/ |  | ||||||
| void EEPROM_Erase(void) { |  | ||||||
|     int page_num = 0; |  | ||||||
| 
 | 
 | ||||||
|     // delete all pages from specified start page to the last page
 | /* Clear flash contents (doesn't touch in-memory DataBuf) */ | ||||||
|     do { | static void eeprom_clear(void) { | ||||||
|  |     FLASH_Unlock(); | ||||||
|  | 
 | ||||||
|  |     for (uint16_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) { | ||||||
|  |         eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); | ||||||
|         FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); |         FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); | ||||||
|         page_num++; |  | ||||||
|     } while (page_num < FEE_DENSITY_PAGES); |  | ||||||
|     } |     } | ||||||
| /*****************************************************************************
 |  | ||||||
|  *  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; |     FLASH_Lock(); | ||||||
|     int      i; |  | ||||||
| 
 | 
 | ||||||
|     // exit if desired address is above the limit (e.G. under 2048 Bytes for 4 pages)
 |     empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; | ||||||
|     if (Address > FEE_DENSITY_BYTES) { |     eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Erase emulated eeprom */ | ||||||
|  | void EEPROM_Erase(void) { | ||||||
|  |     eeprom_println("EEPROM_Erase"); | ||||||
|  |     /* Erase compacted pages and write log */ | ||||||
|  |     eeprom_clear(); | ||||||
|  |     /* re-initialize to reset DataBuf */ | ||||||
|  |     EEPROM_Init(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Compact write log */ | ||||||
|  | static uint8_t eeprom_compact(void) { | ||||||
|  |     /* Erase compacted pages and write log */ | ||||||
|  |     eeprom_clear(); | ||||||
|  | 
 | ||||||
|  |     FLASH_Unlock(); | ||||||
|  | 
 | ||||||
|  |     FLASH_Status final_status = FLASH_COMPLETE; | ||||||
|  | 
 | ||||||
|  |     /* Write emulated eeprom contents from memory to compacted flash */ | ||||||
|  |     uint16_t *src  = (uint16_t *)DataBuf; | ||||||
|  |     uintptr_t dest = FEE_PAGE_BASE_ADDRESS; | ||||||
|  |     uint16_t  value; | ||||||
|  |     for (; dest < FEE_PAGE_LAST_ADDRESS; ++src, dest += 2) { | ||||||
|  |         value = *src; | ||||||
|  |         if (value) { | ||||||
|  |             eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value); | ||||||
|  |             FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value); | ||||||
|  |             if (status != FLASH_COMPLETE) final_status = status; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FLASH_Lock(); | ||||||
|  | 
 | ||||||
|  |     if (debug_eeprom) { | ||||||
|  |         println("eeprom_compacted:"); | ||||||
|  |         print_eeprom(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return final_status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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(); | ||||||
|  | 
 | ||||||
|  |         eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); | ||||||
|  |         FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); | ||||||
|  | 
 | ||||||
|  |         FLASH_Lock(); | ||||||
|  |         return status; | ||||||
|  |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     // calculate which page is affected (Pagenum1/Pagenum2...PagenumN)
 | static uint8_t eeprom_write_log_word_entry(uint16_t Address) { | ||||||
|     page = FEE_ADDR_OFFSET(Address) / FEE_PAGE_SIZE; |     FLASH_Status final_status = FLASH_COMPLETE; | ||||||
| 
 | 
 | ||||||
|     // if current data is 0xFF, the byte is empty, just overwrite with the new one
 |     uint16_t value = *(uint16_t *)(&DataBuf[Address]); | ||||||
|     if ((*(__IO uint16_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) == FEE_EMPTY_WORD) { |     eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); | ||||||
|         FlashStatus = FLASH_ProgramHalfWord(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address), (uint16_t)(0x00FF & DataByte)); | 
 | ||||||
|  |     /* 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 { |     } else { | ||||||
|         // Copy Page to a buffer
 |         encoding |= FEE_VALUE_NEXT; | ||||||
|         memcpy(DataBuf, (uint8_t *)FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE), FEE_PAGE_SIZE);  // !!! Calculate base address for the desired page
 |         entry_size = 4; | ||||||
|  |         /* Writes to addresses less than 128 are byte log entries */ | ||||||
|  |         Address -= FEE_BYTE_RANGE; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         // check if new data is differ to current data, return if not, proceed if yes
 |     /* if we can't find an empty spot, we must compact emulated eeprom */ | ||||||
|         if (DataByte == *(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) { |     if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) { | ||||||
|  |         /* compact the write log into the compacted flash area */ | ||||||
|  |         return eeprom_compact(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Word log writes should be word-aligned.  Take back a bit */ | ||||||
|  |     Address >>= 1; | ||||||
|  |     Address |= encoding; | ||||||
|  | 
 | ||||||
|  |     /* ok we found a place let's write our data */ | ||||||
|  |     FLASH_Unlock(); | ||||||
|  | 
 | ||||||
|  |     /* address */ | ||||||
|  |     eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address); | ||||||
|  |     final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address); | ||||||
|  | 
 | ||||||
|  |     /* value */ | ||||||
|  |     if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) { | ||||||
|  |         eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value); | ||||||
|  |         FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value); | ||||||
|  |         if (status != FLASH_COMPLETE) final_status = status; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FLASH_Lock(); | ||||||
|  | 
 | ||||||
|  |     return final_status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { | ||||||
|  |     eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); | ||||||
|  | 
 | ||||||
|  |     /* if couldn't find an empty spot, we must compact emulated eeprom */ | ||||||
|  |     if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { | ||||||
|  |         /* compact the write log into the compacted flash area */ | ||||||
|  |         return eeprom_compact(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* ok we found a place let's write our data */ | ||||||
|  |     FLASH_Unlock(); | ||||||
|  | 
 | ||||||
|  |     /* Pack address and value into the same word */ | ||||||
|  |     uint16_t value = (Address << 8) | DataBuf[Address]; | ||||||
|  | 
 | ||||||
|  |     /* write to flash */ | ||||||
|  |     eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value); | ||||||
|  |     FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value); | ||||||
|  | 
 | ||||||
|  |     FLASH_Lock(); | ||||||
|  | 
 | ||||||
|  |     return status; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { | ||||||
|  |     /* if the address is out-of-bounds, do nothing */ | ||||||
|  |     if (Address >= FEE_DENSITY_BYTES) { | ||||||
|  |         eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); | ||||||
|  |         return FLASH_BAD_ADDRESS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* if the value is the same, don't bother writing it */ | ||||||
|  |     if (DataBuf[Address] == DataByte) { | ||||||
|  |         eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         // manipulate desired data byte in temp data array if new byte is differ to the current
 |     /* keep DataBuf cache in sync */ | ||||||
|         DataBuf[FEE_ADDR_OFFSET(Address) % FEE_PAGE_SIZE] = DataByte; |     DataBuf[Address] = DataByte; | ||||||
|  |     eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); | ||||||
| 
 | 
 | ||||||
|         // Erase Page
 |     /* perform the write into flash memory */ | ||||||
|         FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)); |     /* First, attempt to write directly into the compacted flash area */ | ||||||
|  |     FLASH_Status status = eeprom_write_direct_entry(Address); | ||||||
|  |     if (!status) { | ||||||
|  |         /* Otherwise append to the write log */ | ||||||
|  |         if (Address < FEE_BYTE_RANGE) { | ||||||
|  |             status = eeprom_write_log_byte_entry(Address); | ||||||
|  |         } else { | ||||||
|  |             status = eeprom_write_log_word_entry(Address & 0xFFFE); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (status != 0 && status != FLASH_COMPLETE) { | ||||||
|  |         eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status); | ||||||
|  |     } | ||||||
|  |     return status; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|         // Write new data (whole page) to flash if data has been changed
 | uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { | ||||||
|         for (i = 0; i < (FEE_PAGE_SIZE / 2); i++) { |     /* if the address is out-of-bounds, do nothing */ | ||||||
|             if ((__IO uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]) != 0xFFFF) { |     if (Address >= FEE_DENSITY_BYTES) { | ||||||
|                 FlashStatus = FLASH_ProgramHalfWord((FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)) + (i * 2), (uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)])); |         eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); | ||||||
|  |         return FLASH_BAD_ADDRESS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Check for word alignment */ | ||||||
|  |     FLASH_Status final_status = FLASH_COMPLETE; | ||||||
|  |     if (Address % 2) { | ||||||
|  |         final_status        = EEPROM_WriteDataByte(Address, DataWord); | ||||||
|  |         FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8); | ||||||
|  |         if (status != FLASH_COMPLETE) final_status = status; | ||||||
|  |         if (final_status != 0 && final_status != FLASH_COMPLETE) { | ||||||
|  |             eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); | ||||||
|  |         } | ||||||
|  |         return final_status; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* if the value is the same, don't bother writing it */ | ||||||
|  |     uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); | ||||||
|  |     if (oldValue == DataWord) { | ||||||
|  |         eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* keep DataBuf cache in sync */ | ||||||
|  |     *(uint16_t *)(&DataBuf[Address]) = DataWord; | ||||||
|  |     eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); | ||||||
|  | 
 | ||||||
|  |     /* perform the write into flash memory */ | ||||||
|  |     /* First, attempt to write directly into the compacted flash area */ | ||||||
|  |     final_status = eeprom_write_direct_entry(Address); | ||||||
|  |     if (!final_status) { | ||||||
|  |         /* Otherwise append to the write log */ | ||||||
|  |         /* Check if we need to fall back to byte write */ | ||||||
|  |         if (Address < FEE_BYTE_RANGE) { | ||||||
|  |             final_status = FLASH_COMPLETE; | ||||||
|  |             /* Only write a byte if it has changed */ | ||||||
|  |             if ((uint8_t)oldValue != (uint8_t)DataWord) { | ||||||
|  |                 final_status = eeprom_write_log_byte_entry(Address); | ||||||
|  |             } | ||||||
|  |             FLASH_Status status = FLASH_COMPLETE; | ||||||
|  |             /* Only write a byte if it has changed */ | ||||||
|  |             if ((oldValue >> 8) != (DataWord >> 8)) { | ||||||
|  |                 status = eeprom_write_log_byte_entry(Address + 1); | ||||||
|  |             } | ||||||
|  |             if (status != FLASH_COMPLETE) final_status = status; | ||||||
|  |         } else { | ||||||
|  |             final_status = eeprom_write_log_word_entry(Address); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     if (final_status != 0 && final_status != FLASH_COMPLETE) { | ||||||
|  |         eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); | ||||||
|     } |     } | ||||||
|     return FlashStatus; |     return final_status; | ||||||
| } | } | ||||||
| /*****************************************************************************
 | 
 | ||||||
|  *  Read once data byte from a specified address. |  | ||||||
|  *******************************************************************************/ |  | ||||||
| uint8_t EEPROM_ReadDataByte(uint16_t Address) { | uint8_t EEPROM_ReadDataByte(uint16_t Address) { | ||||||
|     uint8_t DataByte = 0xFF; |     uint8_t DataByte = 0xFF; | ||||||
| 
 | 
 | ||||||
|     // Get Byte from specified address
 |     if (Address < FEE_DENSITY_BYTES) { | ||||||
|     DataByte = (*(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))); |         DataByte = DataBuf[Address]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); | ||||||
| 
 | 
 | ||||||
|     return DataByte; |     return DataByte; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint16_t EEPROM_ReadDataWord(uint16_t Address) { | ||||||
|  |     uint16_t DataWord = 0xFFFF; | ||||||
|  | 
 | ||||||
|  |     if (Address < FEE_DENSITY_BYTES - 1) { | ||||||
|  |         /* Check word alignment */ | ||||||
|  |         if (Address % 2) { | ||||||
|  |             DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8); | ||||||
|  |         } else { | ||||||
|  |             DataWord = *(uint16_t *)(&DataBuf[Address]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); | ||||||
|  | 
 | ||||||
|  |     return DataWord; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*****************************************************************************
 | /*****************************************************************************
 | ||||||
|  *  Wrap library in AVR style functions. |  *  Wrap library in AVR style functions. | ||||||
|  *******************************************************************************/ |  *******************************************************************************/ | ||||||
| uint8_t eeprom_read_byte(const uint8_t *Address) { | uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); } | ||||||
|     const uint16_t p = (const uint32_t)Address; |  | ||||||
|     return EEPROM_ReadDataByte(p); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void eeprom_write_byte(uint8_t *Address, uint8_t Value) { | void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); } | ||||||
|     uint16_t p = (uint32_t)Address; |  | ||||||
|     EEPROM_WriteDataByte(p, Value); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void eeprom_update_byte(uint8_t *Address, uint8_t Value) { | void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); } | ||||||
|     uint16_t p = (uint32_t)Address; |  | ||||||
|     EEPROM_WriteDataByte(p, Value); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| uint16_t eeprom_read_word(const uint16_t *Address) { | uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); } | ||||||
|     const uint16_t p = (const uint32_t)Address; |  | ||||||
|     return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void eeprom_write_word(uint16_t *Address, uint16_t Value) { | void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, 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) { | void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); } | ||||||
|     uint16_t p = (uint32_t)Address; |  | ||||||
|     EEPROM_WriteDataByte(p, (uint8_t)Value); |  | ||||||
|     EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| uint32_t eeprom_read_dword(const uint32_t *Address) { | uint32_t eeprom_read_dword(const uint32_t *Address) { | ||||||
|     const uint16_t p = (const uint32_t)Address; |     const uint16_t p = (const uintptr_t)Address; | ||||||
|     return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24); |     /* 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) { | void eeprom_write_dword(uint32_t *Address, uint32_t Value) { | ||||||
|     uint16_t p = (const uint32_t)Address; |     uint16_t p = (const uintptr_t)Address; | ||||||
|  |     /* Check word alignment */ | ||||||
|  |     if (p % 2) { | ||||||
|  |         /* Not aligned */ | ||||||
|         EEPROM_WriteDataByte(p, (uint8_t)Value); |         EEPROM_WriteDataByte(p, (uint8_t)Value); | ||||||
|     EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); |         EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8)); | ||||||
|     EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16)); |  | ||||||
|         EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24)); |         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) { | void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, 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) { |  | ||||||
|         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_read_block(void *buf, const void *addr, size_t len) { | 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; |     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) { | void eeprom_write_block(const void *buf, void *addr, size_t len) { | ||||||
|     uint8_t *      p   = (uint8_t *)addr; |     uint8_t *      dest = (uint8_t *)addr; | ||||||
|     const uint8_t *src  = (const uint8_t *)buf; |     const uint8_t *src  = (const uint8_t *)buf; | ||||||
|     while (len--) { | 
 | ||||||
|         eeprom_write_byte(p++, *src++); |     /* Check word alignment */ | ||||||
|  |     if (len && (uintptr_t)dest % 2) { | ||||||
|  |         /* Write the unaligned first byte */ | ||||||
|  |         eeprom_write_byte(dest++, *src++); | ||||||
|  |         --len; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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) { | void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); } | ||||||
|     uint8_t *      p   = (uint8_t *)addr; |  | ||||||
|     const uint8_t *src = (const uint8_t *)buf; |  | ||||||
|     while (len--) { |  | ||||||
|         eeprom_write_byte(p++, *src++); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -23,62 +23,11 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <ch.h> |  | ||||||
| #include <hal.h> |  | ||||||
| #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 |  | ||||||
| #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 2            // 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 |  | ||||||
| #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 |  | ||||||
| 
 |  | ||||||
| // DONT CHANGE
 |  | ||||||
| // Choose location for the first EEPROM Page address on the top of flash
 |  | ||||||
| #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) |  | ||||||
| #define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES)) |  | ||||||
| #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); | uint16_t EEPROM_Init(void); | ||||||
| void     EEPROM_Erase(void); | void     EEPROM_Erase(void); | ||||||
| uint16_t 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); | uint8_t  EEPROM_ReadDataByte(uint16_t Address); | ||||||
|  | uint16_t EEPROM_ReadDataWord(uint16_t Address); | ||||||
|  | 
 | ||||||
|  | void print_eeprom(void); | ||||||
|  | |||||||
| @ -22,8 +22,11 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <ch.h> | #include <stdint.h> | ||||||
| #include <hal.h> | 
 | ||||||
|  | #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; | typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										438
									
								
								tmk_core/common/test/eeprom_stm32_tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										438
									
								
								tmk_core/common/test/eeprom_stm32_tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,438 @@ | |||||||
|  | /* 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 <http://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "gtest/gtest.h" | ||||||
|  | 
 | ||||||
|  | extern "C" { | ||||||
|  | #include "flash_stm32.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 (MOCK_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 < MOCK_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); | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								tmk_core/common/test/flash_stm32_mock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								tmk_core/common/test/flash_stm32_mock.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | /* 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 <http://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include "flash_stm32.h" | ||||||
|  | 
 | ||||||
|  | uint8_t FlashBuf[MOCK_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 >= MOCK_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 >= MOCK_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) {} | ||||||
							
								
								
									
										23
									
								
								tmk_core/common/test/rules.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tmk_core/common/test/rules.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | 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) | ||||||
							
								
								
									
										1
									
								
								tmk_core/common/test/testlist.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tmk_core/common/test/testlist.mk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | TEST_LIST += eeprom_stm32_tiny eeprom_stm32_large | ||||||
							
								
								
									
										317
									
								
								util/stm32eeprom_parser.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										317
									
								
								util/stm32eeprom_parser.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,317 @@ | |||||||
|  | #!/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 <http://www.gnu.org/licenses/>. | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | 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 <= 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) | ||||||
|  |         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()) | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Donald Kjer
						Donald Kjer