diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 1d9bcbc5..8b844f46 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -1440,20 +1440,23 @@ unit-ecc-raw-der: unit-ecc-raw-der.c # destination flash is a pre-filled host array, so the over-read is # observable through a canary after the source buffer. stm32l5_write_extract.h: ../../hal/stm32l5.c - sed -n '/^void RAMFUNCTION hal_flash_wait_complete/,/^}/p' $< > $@ - sed -n '/^void RAMFUNCTION hal_flash_clear_errors/,/^}/p' $< >> $@ + # hal_flash_wait_complete is mocked in the test (program-window + # check), so only the real clear_errors + write are extracted. + sed -n '/^void RAMFUNCTION hal_flash_clear_errors/,/^}/p' $< > $@ sed -n '/^int RAMFUNCTION hal_flash_write/,/^}/p' $< >> $@ unit-stm32l5-write: unit-stm32l5-write.c stm32l5_write_extract.h gcc -o $@ unit-stm32l5-write.c $(CFLAGS) $(LDFLAGS) # unit-stm32u5-write is the 16-byte-unit twin of the STM32L5 test: -# the real hal_flash_write() and its wait/clear helpers from -# hal/stm32u5.c, FLASH_NS_SR/CR on a host register file, destination -# flash at a 32-bit host address, canary after the source buffer. +# the real hal_flash_write() and clear_errors from hal/stm32u5.c +# (hal_flash_wait_complete is mocked in the test for the +# program-window check), FLASH_NS_SR/CR on a host register file, +# destination flash at a 32-bit host address, canary after the source. stm32u5_write_extract.h: ../../hal/stm32u5.c - sed -n '/^void RAMFUNCTION hal_flash_wait_complete/,/^}/p' $< > $@ - sed -n '/^void RAMFUNCTION hal_flash_clear_errors/,/^}/p' $< >> $@ + # hal_flash_wait_complete is mocked in the test (program-window + # check), so only the real clear_errors + write are extracted. + sed -n '/^void RAMFUNCTION hal_flash_clear_errors/,/^}/p' $< > $@ sed -n '/^int RAMFUNCTION hal_flash_write/,/^}/p' $< >> $@ unit-stm32u5-write: unit-stm32u5-write.c stm32u5_write_extract.h diff --git a/tools/unit-tests/unit-stm32l5-write.c b/tools/unit-tests/unit-stm32l5-write.c index 61780451..f6fccc35 100644 --- a/tools/unit-tests/unit-stm32l5-write.c +++ b/tools/unit-tests/unit-stm32l5-write.c @@ -12,12 +12,14 @@ * The fix read-modify-writes the whole unit: the destination is * aligned down to the unit, the bytes outside the requested span come * from flash and go back unchanged, and both words are stored through - * the aligned pointer. That is asserted here for unaligned starts - * (the host data model cannot observe the program-unit split itself, - * only where the bytes end up). Both words are always stored in one - * PG window -- the flash has no 32-bit program mode -- which this - * host model cannot observe, so that is asserted by construction - * in the HAL, not here. + * the aligned pointer. That is asserted here for unaligned starts. + * The program-window invariant is enforced by the mock + * hal_flash_wait_complete below: the real one only spins on + * FLASH_SR_BSY (never set on the host register file), so the mock + * diffs the flash against the previous window and asserts that the + * changed bytes fit in one aligned 8-byte unit. A pre-fix HAL split + * the two word stores across two units for an unaligned start, and + * the 20-byte unaligned test goes red on it. * * Same harness as the STM32U5 twin: extracted functions, registers on * a host file, stale destination flash, canary after the source. @@ -75,6 +77,10 @@ static uint32_t g_flash_regs[0x40 / sizeof(uint32_t)]; #define FLASH_MEM_ADDR 0x10000000UL static uint8_t *g_flash_mem; +/* Snapshot of the flash at the last program-window boundary; the + * mock hal_flash_wait_complete() diffs against it. */ +static uint8_t g_flash_prev[FLASH_MEM_SZ]; + /* Source buffer followed by a canary: a pre-fix short write reads the * canary and lands it in the destination flash. */ #define DATA_SZ 64 @@ -82,6 +88,31 @@ static uint8_t *g_flash_mem; static uint8_t g_data[DATA_SZ + CANARY_SZ]; #define g_canary (g_data + DATA_SZ) +/* Mock hal_flash_wait_complete(): the real one (hal/stm32l5.c) only + * spins on FLASH_SR_BSY, which the host register file never sets. This + * one adds the program-window check the host model cannot see any + * other way: the bytes changed since the previous window must fit + * within one aligned 8-byte program unit. The pre-fix HAL issued its + * two word stores relative to the caller address, so an unaligned + * start split them across two units and this assertion goes red. */ +static void hal_flash_wait_complete(uint8_t bank) +{ + int i; + int first = -1; + int last = -1; + + for (i = 0; i < FLASH_MEM_SZ; i++) { + if (g_flash_mem[i] != g_flash_prev[i]) { + if (first < 0) + first = i; + last = i; + } + } + if (first >= 0) + ck_assert_int_le(last, (first & ~0x07) + 7); + memcpy(g_flash_prev, g_flash_mem, FLASH_MEM_SZ); +} + /* The real functions from hal/stm32l5.c (extracted by the Makefile). */ #include "stm32l5_write_extract.h" @@ -92,6 +123,7 @@ static void setup(void) memset(g_flash_regs, 0, sizeof(g_flash_regs)); for (i = 0; i < FLASH_MEM_SZ; i++) g_flash_mem[i] = 0x12; /* stale */ + memcpy(g_flash_prev, g_flash_mem, FLASH_MEM_SZ); for (i = 0; i < DATA_SZ; i++) g_data[i] = (uint8_t)(0x30 + i); /* 0x70..0x8F: distinct from the data bytes (0x30..0x6F), the stale diff --git a/tools/unit-tests/unit-stm32u5-write.c b/tools/unit-tests/unit-stm32u5-write.c index fed3fa5d..053e6564 100644 --- a/tools/unit-tests/unit-stm32u5-write.c +++ b/tools/unit-tests/unit-stm32u5-write.c @@ -13,11 +13,13 @@ * aligned down to the unit, the bytes outside the requested span come * from flash and go back unchanged, and all four words are stored * through the aligned pointer. That is asserted here for unaligned - * starts (the host data model cannot observe the program-unit split - * itself, only where the bytes end up). All four words are always - * stored in one program -- the controller only starts it on the - * fourth word -- which this host model cannot observe, so that is - * asserted by construction in the HAL, not here. + * starts. The program-window invariant is enforced by the mock + * hal_flash_wait_complete below: the real one only spins on + * FLASH_SR_BSY (never set on the host register file), so the mock + * diffs the flash against the previous window and asserts that the + * changed bytes fit in one aligned 16-byte unit. A pre-fix HAL split + * the four word stores across two units for an unaligned start, and + * the 20-byte unaligned test goes red on it. * * Same harness as the STM32L5 twin: extracted functions, registers on * a host file, stale destination flash, canary after the source. @@ -76,6 +78,10 @@ static uint32_t g_flash_regs[0x40 / sizeof(uint32_t)]; #define FLASH_MEM_ADDR 0x11000000UL static uint8_t *g_flash_mem; +/* Snapshot of the flash at the last program-window boundary; the + * mock hal_flash_wait_complete() diffs against it. */ +static uint8_t g_flash_prev[FLASH_MEM_SZ]; + /* Source buffer followed by a canary: pre-fix, a short write reads * bytes past len and lands them in the destination flash. The canary * range avoids the data bytes (0x30..0x6F), the stale fill (0x12) and @@ -85,6 +91,31 @@ static uint8_t *g_flash_mem; static uint8_t g_data[DATA_SZ + CANARY_SZ]; #define g_canary (g_data + DATA_SZ) +/* Mock hal_flash_wait_complete(): the real one (hal/stm32u5.c) only + * spins on FLASH_SR_BSY, which the host register file never sets. This + * one adds the program-window check the host model cannot see any + * other way: the bytes changed since the previous window must fit + * within one aligned 16-byte program unit. The pre-fix HAL issued its + * four word stores relative to the caller address, so an unaligned + * start split them across two units and this assertion goes red. */ +static void hal_flash_wait_complete(uint8_t bank) +{ + int i; + int first = -1; + int last = -1; + + for (i = 0; i < FLASH_MEM_SZ; i++) { + if (g_flash_mem[i] != g_flash_prev[i]) { + if (first < 0) + first = i; + last = i; + } + } + if (first >= 0) + ck_assert_int_le(last, (first & ~0x0F) + 15); + memcpy(g_flash_prev, g_flash_mem, FLASH_MEM_SZ); +} + /* The real functions from hal/stm32u5.c (extracted by the Makefile). */ #include "stm32u5_write_extract.h" @@ -95,6 +126,7 @@ static void setup(void) memset(g_flash_regs, 0, sizeof(g_flash_regs)); for (i = 0; i < FLASH_MEM_SZ; i++) g_flash_mem[i] = 0x12; /* stale */ + memcpy(g_flash_prev, g_flash_mem, FLASH_MEM_SZ); for (i = 0; i < DATA_SZ; i++) g_data[i] = (uint8_t)(0x30 + i); for (i = 0; i < CANARY_SZ; i++)