Maintain FNV checksums per PG to only write if config updated

This commit is contained in:
Steve Evans 2022-05-09 20:53:53 +01:00
parent 9e894586b7
commit fdd7990a29
6 changed files with 85 additions and 31 deletions

View File

@ -20,6 +20,8 @@
#include <stdint.h>
#include "common/crc.h"
#include "platform.h"
#include "streambuf.h"
@ -114,3 +116,17 @@ void crc8_xor_sbuf_append(sbuf_t *dst, uint8_t *start)
sbufWriteU8(dst, crc);
}
// FowlerNollVo hash function; see https://en.wikipedia.org/wiki/FowlerNollVo_hash_function
uint32_t fnv_update(uint32_t hash, const void *data, uint32_t length)
{
const uint8_t *p = (const uint8_t *)data;
const uint8_t *pend = p + length;
for (; p != pend; p++) {
hash *= FNV_PRIME;
hash ^= *p;
}
return hash;
}

View File

@ -38,3 +38,8 @@ void crc8_sbuf_append(struct sbuf_s *dst, uint8_t *start, uint8_t poly);
uint8_t crc8_xor_update(uint8_t crc, const void *data, uint32_t length);
void crc8_xor_sbuf_append(struct sbuf_s *dst, uint8_t *start);
#define FNV_PRIME 16777619
#define FNV_OFFSET_BASIS 2166136261
uint32_t fnv_update(uint32_t hash, const void *data, uint32_t length);

View File

@ -388,6 +388,7 @@ bool loadEEPROM(void)
success = false;
}
*reg->fnv_hash = fnv_update(FNV_OFFSET_BASIS, reg->address, pgSize(reg));
}
return success;
@ -395,51 +396,63 @@ bool loadEEPROM(void)
static bool writeSettingsToEEPROM(void)
{
config_streamer_t streamer;
config_streamer_init(&streamer);
config_streamer_start(&streamer, (uintptr_t)&__config_start, &__config_end - &__config_start);
bool dirtyConfig = !isEEPROMVersionValid() || !isEEPROMStructureValid();
configHeader_t header = {
.eepromConfigVersion = EEPROM_CONF_VERSION,
.magic_be = 0xBE,
};
config_streamer_write(&streamer, (uint8_t *)&header, sizeof(header));
uint16_t crc = CRC_START_VALUE;
crc = crc16_ccitt_update(crc, (uint8_t *)&header, sizeof(header));
PG_FOREACH(reg) {
const uint16_t regSize = pgSize(reg);
configRecord_t record = {
.size = sizeof(configRecord_t) + regSize,
.pgn = pgN(reg),
.version = pgVersion(reg),
.flags = 0
};
record.flags |= CR_CLASSICATION_SYSTEM;
config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record));
crc = crc16_ccitt_update(crc, (uint8_t *)&record, sizeof(record));
config_streamer_write(&streamer, reg->address, regSize);
crc = crc16_ccitt_update(crc, reg->address, regSize);
if (*reg->fnv_hash != fnv_update(FNV_OFFSET_BASIS, reg->address, pgSize(reg))) {
dirtyConfig = true;
}
}
configFooter_t footer = {
.terminator = 0,
};
// Only write the config if it has changed
if (dirtyConfig) {
config_streamer_t streamer;
config_streamer_init(&streamer);
config_streamer_write(&streamer, (uint8_t *)&footer, sizeof(footer));
crc = crc16_ccitt_update(crc, (uint8_t *)&footer, sizeof(footer));
config_streamer_start(&streamer, (uintptr_t)&__config_start, &__config_end - &__config_start);
// include inverted CRC in big endian format in the CRC
const uint16_t invertedBigEndianCrc = ~(((crc & 0xFF) << 8) | (crc >> 8));
config_streamer_write(&streamer, (uint8_t *)&invertedBigEndianCrc, sizeof(crc));
config_streamer_write(&streamer, (uint8_t *)&header, sizeof(header));
uint16_t crc = CRC_START_VALUE;
crc = crc16_ccitt_update(crc, (uint8_t *)&header, sizeof(header));
PG_FOREACH(reg) {
const uint16_t regSize = pgSize(reg);
configRecord_t record = {
.size = sizeof(configRecord_t) + regSize,
.pgn = pgN(reg),
.version = pgVersion(reg),
.flags = 0,
};
config_streamer_flush(&streamer);
const bool success = config_streamer_finish(&streamer) == 0;
record.flags |= CR_CLASSICATION_SYSTEM;
config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record));
crc = crc16_ccitt_update(crc, (uint8_t *)&record, sizeof(record));
config_streamer_write(&streamer, reg->address, regSize);
crc = crc16_ccitt_update(crc, reg->address, regSize);
}
return success;
configFooter_t footer = {
.terminator = 0,
};
config_streamer_write(&streamer, (uint8_t *)&footer, sizeof(footer));
crc = crc16_ccitt_update(crc, (uint8_t *)&footer, sizeof(footer));
// include inverted CRC in big endian format in the CRC
const uint16_t invertedBigEndianCrc = ~(((crc & 0xFF) << 8) | (crc >> 8));
config_streamer_write(&streamer, (uint8_t *)&invertedBigEndianCrc, sizeof(crc));
config_streamer_flush(&streamer);
return (config_streamer_finish(&streamer) == 0);
} else {
return true;
}
}
void writeConfigToEEPROM(void)

View File

@ -24,6 +24,7 @@
#include "platform.h"
#include "common/crc.h"
#include "common/maths.h"
#include "pg.h"
@ -80,6 +81,8 @@ bool pgLoad(const pgRegistry_t* reg, const void *from, int size, int version)
const int take = MIN(size, pgSize(reg));
memcpy(pgOffset(reg), from, take);
*reg->fnv_hash = fnv_update(FNV_OFFSET_BASIS, from, take);
return true;
}

View File

@ -54,6 +54,7 @@ typedef struct pgRegistry_s {
void *ptr; // Pointer to init template
pgResetFunc *fn; // Popinter to pgResetFunc
} reset;
uint32_t *fnv_hash; // Used to detect if config has changed prior to write
} pgRegistry_t;
static inline uint16_t pgN(const pgRegistry_t* reg) {return reg->pgn & PGR_PGN_MASK;}
@ -118,6 +119,7 @@ extern const uint8_t __pg_resetdata_end[];
#define PG_REGISTER_I(_type, _name, _pgn, _version, _reset) \
_type _name ## _System; \
_type _name ## _Copy; \
uint32_t _name ## _fnv_hash; \
/* Force external linkage for g++. Catch multi registration */ \
extern const pgRegistry_t _name ## _Registry; \
const pgRegistry_t _name ##_Registry PG_REGISTER_ATTRIBUTES = { \
@ -125,6 +127,7 @@ extern const uint8_t __pg_resetdata_end[];
.length = 1, \
.size = sizeof(_type) | PGR_SIZE_SYSTEM_FLAG, \
.address = (uint8_t*)&_name ## _System, \
.fnv_hash = &_name ## _fnv_hash, \
.copy = (uint8_t*)&_name ## _Copy, \
.ptr = 0, \
_reset, \
@ -149,12 +152,14 @@ extern const uint8_t __pg_resetdata_end[];
#define PG_REGISTER_ARRAY_I(_type, _length, _name, _pgn, _version, _reset) \
_type _name ## _SystemArray[_length]; \
_type _name ## _CopyArray[_length]; \
uint32_t _name ## _fnv_hash; \
extern const pgRegistry_t _name ##_Registry; \
const pgRegistry_t _name ## _Registry PG_REGISTER_ATTRIBUTES = { \
.pgn = _pgn | (_version << 12), \
.length = _length, \
.size = (sizeof(_type) * _length) | PGR_SIZE_SYSTEM_FLAG, \
.address = (uint8_t*)&_name ## _SystemArray, \
.fnv_hash = &_name ## _fnv_hash, \
.copy = (uint8_t*)&_name ## _CopyArray, \
.ptr = 0, \
_reset, \

View File

@ -106,7 +106,9 @@ blackbox_encoding_unittest_SRC := \
cli_unittest_SRC := \
$(USER_DIR)/cli/cli.c \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/printf.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/config/feature.c \
$(USER_DIR)/pg/pg.c \
$(USER_DIR)/common/typeconversion.c
@ -227,6 +229,8 @@ link_quality_unittest_DEFINES := \
USE_RX_LINK_QUALITY_INFO=
pg_unittest_SRC := \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/pg/pg.c
@ -234,7 +238,9 @@ rc_controls_unittest_SRC := \
$(USER_DIR)/fc/rc_controls.c \
$(USER_DIR)/pg/pg.c \
$(USER_DIR)/common/bitarray.c \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/maths.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/fc/rc_adjustments.c \
$(USER_DIR)/fc/rc_modes.c
@ -254,7 +260,9 @@ rx_ibus_unittest_SRC := \
rx_ranges_unittest_SRC := \
$(USER_DIR)/common/bitarray.c \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/maths.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/fc/rc_modes.c \
$(USER_DIR)/rx/rx.c \
$(USER_DIR)/pg/pg.c \
@ -287,7 +295,9 @@ sensor_gyro_unittest_SRC := \
$(USER_DIR)/sensors/gyro_init.c \
$(USER_DIR)/sensors/boardalignment.c \
$(USER_DIR)/common/filter.c \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/maths.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/common/sensor_alignment.c \
$(USER_DIR)/drivers/accgyro/accgyro_fake.c \
$(USER_DIR)/drivers/accgyro/gyro_sync.c \
@ -378,8 +388,10 @@ rcdevice_unittest_SRC := \
$(USER_DIR)/pg/pg.c \
pid_unittest_SRC := \
$(USER_DIR)/common/crc.c \
$(USER_DIR)/common/filter.c \
$(USER_DIR)/common/maths.c \
$(USER_DIR)/common/streambuf.c \
$(USER_DIR)/drivers/accgyro/gyro_sync.c \
$(USER_DIR)/fc/controlrate_profile.c \
$(USER_DIR)/fc/runtime_config.c \