Files
gps/soc/software/gps_system/tests/test_gps_flash_journal.c
T
onur 30179df8e6 feat(gps): add timer-based assisted reacquisition
Remove the fixed pseudorange start offset, preserve per-PRN navigation assistance across RF loss and power cycles, and seed coherent navigation measurement phase on reacquisition. Add full LNAV almanac visibility scheduling, power-safe QSPI journaling, RF-domain reset isolation, and PPS Gray CDC hardening with Linux, sanitizer, RTL, and end-to-end coverage.
2026-08-04 02:58:11 +03:00

260 lines
7.4 KiB
C

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "gps/gps_flash_journal.h"
#define FAKE_FLASH_SIZE 8192u
#define REGION_SIZE 2048u
#define REGION0 0u
#define REGION1 4096u
typedef struct {
unsigned char bytes[FAKE_FLASH_SIZE];
unsigned int program_calls;
unsigned int erase_calls;
unsigned int fail_program_call;
unsigned int fail_program_after_bytes;
bool fail_erase;
} fake_flash_t;
static void fake_flash_reset(fake_flash_t* flash)
{
memset(flash, 0, sizeof(*flash));
memset(flash->bytes, 0xff, sizeof(flash->bytes));
}
static bool fake_read(
void* context, uint32_t address, void* destination, uint32_t length)
{
fake_flash_t* flash = context;
if((address > FAKE_FLASH_SIZE) ||
(length > (FAKE_FLASH_SIZE - address)))
{
return false;
}
memcpy(destination, &flash->bytes[address], length);
return true;
}
static bool fake_program(
void* context, uint32_t address, const void* source, uint32_t length)
{
fake_flash_t* flash = context;
flash->program_calls++;
if((length == 0u) || (length > GPS_FLASH_JOURNAL_PAGE_SIZE) ||
((address / GPS_FLASH_JOURNAL_PAGE_SIZE) !=
((address + length - 1u) / GPS_FLASH_JOURNAL_PAGE_SIZE)) ||
(address > FAKE_FLASH_SIZE) ||
(length > (FAKE_FLASH_SIZE - address)))
{
return false;
}
const unsigned char* data = source;
uint32_t programmed = length;
bool fail = flash->fail_program_call != 0u &&
flash->program_calls == flash->fail_program_call;
if(fail && (flash->fail_program_after_bytes < programmed))
{
programmed = flash->fail_program_after_bytes;
}
for(uint32_t i = 0u ; i < programmed ; i++)
{
/* NOR page programming can only clear bits. */
flash->bytes[address + i] &= data[i];
}
return !fail;
}
static bool fake_erase(void* context, uint32_t region_address)
{
fake_flash_t* flash = context;
flash->erase_calls++;
if(flash->fail_erase ||
((region_address != REGION0) && (region_address != REGION1)))
{
return false;
}
memset(&flash->bytes[region_address], 0xff, REGION_SIZE);
return true;
}
static gps_flash_journal_io_t fake_io(fake_flash_t* flash)
{
const gps_flash_journal_io_t io = {
.context = flash,
.read = fake_read,
.program = fake_program,
.erase_region = fake_erase
};
return io;
}
static void init_journal(
gps_flash_journal_t* journal,
fake_flash_t* flash,
uint32_t maximum_payload)
{
gps_flash_journal_io_t io = fake_io(flash);
assert(gps_flash_journal_init(
journal, &io, REGION0, REGION1, REGION_SIZE,
maximum_payload));
}
static void test_crc_vector(void)
{
static const char vector[] = "123456789";
assert(gps_flash_journal_crc32(vector, 9u) == UINT32_C(0xcbf43926));
}
static void test_empty_write_load_and_deduplicate(void)
{
fake_flash_t flash;
fake_flash_reset(&flash);
gps_flash_journal_t journal;
init_journal(&journal, &flash, 800u);
unsigned char output[800];
assert(!gps_flash_journal_load_latest(
&journal, output, sizeof(output), NULL, NULL));
unsigned char payload[700];
for(unsigned int i = 0u ; i < sizeof(payload) ; i++)
{
payload[i] = (unsigned char)(i * 17u);
}
assert(gps_flash_journal_write(
&journal, payload, sizeof(payload)) ==
GPS_FLASH_JOURNAL_WRITE_COMMITTED);
unsigned int programs_after_commit = flash.program_calls;
assert(gps_flash_journal_write(
&journal, payload, sizeof(payload)) ==
GPS_FLASH_JOURNAL_WRITE_UNCHANGED);
assert(flash.program_calls == programs_after_commit);
uint32_t size = 0u;
uint64_t sequence = 0u;
memset(output, 0, sizeof(output));
assert(gps_flash_journal_load_latest(
&journal, output, sizeof(output), &size, &sequence));
assert(size == sizeof(payload));
assert(sequence == 1u);
assert(memcmp(output, payload, sizeof(payload)) == 0);
/* A CPU reset reconstructs journal state only from flash. */
gps_flash_journal_t rebooted;
init_journal(&rebooted, &flash, 800u);
memset(output, 0, sizeof(output));
assert(gps_flash_journal_load_latest(
&rebooted, output, sizeof(output), &size, &sequence));
assert(sequence == 1u);
assert(memcmp(output, payload, sizeof(payload)) == 0);
}
static void test_rotation_keeps_latest_and_old_bank_until_commit(void)
{
fake_flash_t flash;
fake_flash_reset(&flash);
gps_flash_journal_t journal;
init_journal(&journal, &flash, 700u);
unsigned char payload[700];
for(unsigned int version = 1u ; version <= 4u ; version++)
{
memset(payload, (int)version, sizeof(payload));
assert(gps_flash_journal_write(
&journal, payload, sizeof(payload)) ==
GPS_FLASH_JOURNAL_WRITE_COMMITTED);
}
assert(journal.latest_sequence == 4u);
assert(flash.erase_calls >= 2u);
gps_flash_journal_t rebooted;
init_journal(&rebooted, &flash, 700u);
unsigned char output[700];
uint64_t sequence = 0u;
assert(gps_flash_journal_load_latest(
&rebooted, output, sizeof(output), NULL, &sequence));
assert(sequence == 4u);
assert(output[0] == 4u && output[699] == 4u);
}
static void test_every_torn_program_keeps_previous_record(void)
{
/* Header, three payload pages, and final commit are separate calls. */
for(unsigned int failed_call_in_write = 1u ;
failed_call_in_write <= 5u ; failed_call_in_write++)
{
fake_flash_t flash;
fake_flash_reset(&flash);
gps_flash_journal_t journal;
init_journal(&journal, &flash, 700u);
unsigned char old_payload[700];
unsigned char new_payload[700];
memset(old_payload, 0x31, sizeof(old_payload));
memset(new_payload, 0x72, sizeof(new_payload));
assert(gps_flash_journal_write(
&journal, old_payload, sizeof(old_payload)) ==
GPS_FLASH_JOURNAL_WRITE_COMMITTED);
flash.fail_program_call =
flash.program_calls + failed_call_in_write;
flash.fail_program_after_bytes = 1u;
assert(gps_flash_journal_write(
&journal, new_payload, sizeof(new_payload)) ==
GPS_FLASH_JOURNAL_WRITE_FAILED);
flash.fail_program_call = 0u;
gps_flash_journal_t rebooted;
init_journal(&rebooted, &flash, 700u);
unsigned char output[700];
uint64_t sequence = 0u;
assert(gps_flash_journal_load_latest(
&rebooted, output, sizeof(output), NULL, &sequence));
assert(sequence == 1u);
assert(memcmp(output, old_payload, sizeof(output)) == 0);
}
}
static void test_corrupt_latest_falls_back_to_other_bank(void)
{
fake_flash_t flash;
fake_flash_reset(&flash);
gps_flash_journal_t journal;
init_journal(&journal, &flash, 700u);
unsigned char payload[700];
memset(payload, 0x11, sizeof(payload));
assert(gps_flash_journal_write(&journal, payload, sizeof(payload)) ==
GPS_FLASH_JOURNAL_WRITE_COMMITTED);
memset(payload, 0x22, sizeof(payload));
assert(gps_flash_journal_write(&journal, payload, sizeof(payload)) ==
GPS_FLASH_JOURNAL_WRITE_COMMITTED);
memset(payload, 0x33, sizeof(payload));
assert(gps_flash_journal_write(&journal, payload, sizeof(payload)) ==
GPS_FLASH_JOURNAL_WRITE_COMMITTED);
/* Three 700-byte records force sequence 3 into the second bank. */
assert(journal.active_region == 1u);
flash.bytes[REGION1 + GPS_FLASH_JOURNAL_PAGE_SIZE + 100u] ^= 0x01u;
gps_flash_journal_t rebooted;
init_journal(&rebooted, &flash, 700u);
unsigned char output[700];
uint64_t sequence = 0u;
assert(gps_flash_journal_load_latest(
&rebooted, output, sizeof(output), NULL, &sequence));
assert(sequence == 2u);
assert(output[0] == 0x22u);
}
int main(void)
{
test_crc_vector();
test_empty_write_load_and_deduplicate();
test_rotation_keeps_latest_and_old_bank_until_commit();
test_every_torn_program_keeps_previous_record();
test_corrupt_latest_falls_back_to_other_bank();
puts("gps flash journal fault-injection tests passed");
return 0;
}