From ff81b5244833fc7d163f0d26ed35538c9bb577f7 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Wed, 6 Mar 2024 21:53:44 +0000 Subject: [PATCH] drivers/flash_simulator: Add support for non-erase device The commit adds support for testing non-explicite-erase device on Flash Simulator. This is addition to already supported explicit erase before write, aka Flash, type of device behaviour. Signed-off-by: Dominik Ermel --- drivers/flash/Kconfig.simulator | 20 +++ drivers/flash/flash_simulator.c | 15 +- .../flash_simulator/flash_sim_impl/src/main.c | 160 ++++++++++++++++++ .../flash_sim_impl/testcase.yaml | 11 ++ 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/drivers/flash/Kconfig.simulator b/drivers/flash/Kconfig.simulator index e32a2a72bb0..fd75479b316 100644 --- a/drivers/flash/Kconfig.simulator +++ b/drivers/flash/Kconfig.simulator @@ -22,12 +22,32 @@ config FLASH_SIMULATOR_UNALIGNED_READ Disable this option only if you want to simulate a specific FLASH interface that requires aligned read access. +config FLASH_SIMULATOR_EXPLICIT_ERASE + bool "Program-erase device" + select FLASH_HAS_EXPLICIT_ERASE + default y + help + Explicit erase (non-erase-on-write) Flash, which is device that requires erase + to erase-value prior to write as it only allows to change bits from erase-value + to the opposite. + +config FLASH_SIMULATOR_RAMLIKE + bool + default y if !FLASH_SIMULATOR_EXPLICIT_ERASE + select FLASH_HAS_NO_EXPLICIT_ERASE + select FLASH_SIMULATOR_DOUBLE_WRITES + help + This is used for setting FLASH_HAS_NO_EXPLICIT_ERASE. + config FLASH_SIMULATOR_DOUBLE_WRITES bool "Allow program units to be programmed more than once" help If selected, writing to a non-erased program unit will succeed, otherwise, it will return an error. Keep in mind that write operations can only change value of a bit from erase-value to the opposite. + This option does not impact FLASH_SIMULATOR_RAMLIKE. + In case when FLASH_SIMULATOR_EXPLICIT_ERASE is selected multiple writes to the same bit + but only change from erase-value to opposite will be registered. config FLASH_SIMULATOR_SIMULATE_TIMING bool "Hardware timing simulation" diff --git a/drivers/flash/flash_simulator.c b/drivers/flash/flash_simulator.c index effd53c1f2b..3a7d578a250 100644 --- a/drivers/flash/flash_simulator.c +++ b/drivers/flash/flash_simulator.c @@ -162,7 +162,12 @@ static const struct flash_driver_api flash_sim_api; static const struct flash_parameters flash_sim_parameters = { .write_block_size = FLASH_SIMULATOR_PROG_UNIT, - .erase_value = FLASH_SIMULATOR_ERASE_VALUE + .erase_value = FLASH_SIMULATOR_ERASE_VALUE, + .caps = { +#if !defined(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE) + .no_explicit_erase = false, +#endif + }, }; static int flash_range_is_valid(const struct device *dev, off_t offset, @@ -226,8 +231,12 @@ static int flash_sim_write(const struct device *dev, const off_t offset, FLASH_SIM_STATS_INC(flash_sim_stats, flash_write_calls); +#if defined(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE) /* check if any unit has been already programmed */ memset(buf, FLASH_SIMULATOR_ERASE_VALUE, sizeof(buf)); +#else + memcpy(buf, MOCK_FLASH(offset), sizeof(buf)); +#endif for (uint32_t i = 0; i < len; i += FLASH_SIMULATOR_PROG_UNIT) { if (memcmp(buf, MOCK_FLASH(offset + i), sizeof(buf))) { FLASH_SIM_STATS_INC(flash_sim_stats, double_writes); @@ -265,10 +274,14 @@ static int flash_sim_write(const struct device *dev, const off_t offset, #endif /* CONFIG_FLASH_SIMULATOR_STATS */ /* only pull bits to zero */ +#if IS_ENABLED(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE) #if FLASH_SIMULATOR_ERASE_VALUE == 0xFF *(MOCK_FLASH(offset + i)) &= *((uint8_t *)data + i); #else *(MOCK_FLASH(offset + i)) |= *((uint8_t *)data + i); +#endif +#else + *(MOCK_FLASH(offset + i)) = *((uint8_t *)data + i); #endif } diff --git a/tests/drivers/flash_simulator/flash_sim_impl/src/main.c b/tests/drivers/flash_simulator/flash_sim_impl/src/main.c index 69871c2b8aa..ac90a821410 100644 --- a/tests/drivers/flash_simulator/flash_sim_impl/src/main.c +++ b/tests/drivers/flash_simulator/flash_sim_impl/src/main.c @@ -8,6 +8,10 @@ #include #include +/* Warning: The test has been written for testing boards with single + * instance of Flash Simulator device only. + */ + /* configuration derived from DT */ #ifdef CONFIG_ARCH_POSIX #define SOC_NV_FLASH_NODE DT_CHILD(DT_INST(0, zephyr_sim_flash), flash_0) @@ -75,6 +79,44 @@ static void test_check_pattern32(off_t start, uint32_t (*pattern_gen)(void), } } +/* ret < 0 is errno; ret == 1 is bad value in flash */ +static int test_check_erase(const struct device *dev, off_t offset, size_t size) +{ + uint8_t buf[FLASH_SIMULATOR_PROG_UNIT]; + int rc; + int i = 0; + + BUILD_ASSERT(sizeof(buf) >= FLASH_SIMULATOR_PROG_UNIT); + + i = 0; + while (i < size) { + size_t chunk = MIN(size - i, sizeof(buf)); + /* The memset is done to set buf to something else than + * FLASH_SIMULATOR_ERASE_VALUE, as we are trying to chek + * whether that is what is now in the memory. + */ + memset(buf, ~FLASH_SIMULATOR_ERASE_VALUE, sizeof(buf)); + rc = flash_read(flash_dev, offset + i, + buf, chunk); + if (rc < 0) { + TC_PRINT("Unexpected flash_read fail @ %ld", + (long)(offset + i)); + return rc; + } + do { + if ((uint8_t)buf[i & (sizeof(buf) - 1)] != + (uint8_t)FLASH_SIMULATOR_ERASE_VALUE) { + TC_PRINT("Flash not erased at %ld\n", + (long)(offset + i)); + return 1; + } + ++i; + --chunk; + } while (chunk); + } + return 0; +} + /* Get access to the device and erase it ready for testing*/ static void test_init(void) { @@ -259,6 +301,8 @@ ZTEST(flash_sim_api, test_align) zassert_equal(-EINVAL, rc, "Unexpected error code (%d)", rc); } +#if !IS_ENABLED(CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES) && \ + !IS_ENABLED(CONFIG_FLASH_SIMULATOR_RAMLIKE) ZTEST(flash_sim_api, test_double_write) { int rc; @@ -282,6 +326,122 @@ ZTEST(flash_sim_api, test_double_write) &data, sizeof(data)); zassert_equal(-EIO, rc, "Unexpected error code (%d)", rc); } +#endif + +#if IS_ENABLED(CONFIG_FLASH_SIMULATOR_RAMLIKE) +ZTEST(flash_sim_api, test_ramlike) +{ + /* Within code below there is assumption that the src size is + * equal or greater than the FLASH_SIMULATOR_PROG_UNIT + * (write-block-size) of device. + */ + const uint8_t src[] = "Hello world! This is test string"; + uint8_t buf[FLASH_SIMULATOR_PROG_UNIT]; + /* Round up to next write-block-size */ + int max = (sizeof(src) + FLASH_SIMULATOR_PROG_UNIT - 1) & + ~(FLASH_SIMULATOR_PROG_UNIT - 1); + int rc; + int i = 0; + int is = 0; /* The index within src */ + + BUILD_ASSERT(sizeof(src) >= FLASH_SIMULATOR_PROG_UNIT); + + /* Scrub memory with something constant */ + memset(buf, FLASH_SIMULATOR_ERASE_VALUE, sizeof(buf)); + while (i < max) { + rc = flash_write(flash_dev, FLASH_SIMULATOR_BASE_OFFSET + i, + buf, sizeof(buf)); + zassert_equal(0, rc, "flash_write should succeed"); + i += sizeof(buf); + } + + /* Check the scrubbing */ + zassert_equal(0, test_check_erase(flash_dev, FLASH_SIMULATOR_BASE_OFFSET, max), + "Area not erased"); + + /* Now write something new */ + i = 0; + while (i < max) { + do { + buf[i & (sizeof(buf) - 1)] = src[is]; + ++i; + ++is; + + /* Continue writing from the beginning of the src */ + if (is >= sizeof(src)) { + is = 0; + } + } while (i & (sizeof(buf) - 1)); + rc = flash_write(flash_dev, FLASH_SIMULATOR_BASE_OFFSET + i - sizeof(buf), + buf, sizeof(buf)); + zassert_equal(0, rc, "flash_write should succeed"); + } + + /* Check the write */ + i = 0; + is = 0; + while (i < max) { + rc = flash_read(flash_dev, FLASH_SIMULATOR_BASE_OFFSET + i, + buf, sizeof(buf)); + zassert_equal(0, rc, "flash_read should succeed"); + do { + zassert_equal((uint8_t)src[is], (uint8_t)buf[i & (sizeof(buf) - 1)], + "Expected src and buf to match at index %d\n", i); + ++i; + ++is; + /* Src has wrapped around */ + if (is >= sizeof(src)) { + is = 0; + } + } while (i & (sizeof(buf) - 1)); + zassert_equal(0, rc, "Unexpected value read"); + } + + /* Because we are checking random access writes, we are now going to + * write binary not of the same data we have just written. If this would be + * program-erase type memory, where you can only change from erase value + * to opposite, such write would render incorrect data in memory, + * but this is random access device so we should get exactly what we write. + */ + i = 0; + is = 0; + while (i < max) { + do { + buf[i & (sizeof(buf) - 1)] = ~src[is]; + ++i; + ++is; + + /* Continue writing from the beginning of the src */ + if (is >= sizeof(src)) { + is = 0; + } + } while (i & (sizeof(buf) - 1)); + rc = flash_write(flash_dev, FLASH_SIMULATOR_BASE_OFFSET + i - sizeof(buf), + buf, sizeof(buf)); + zassert_equal(0, rc, "flash_write should succeed"); + } + + /* Check the write */ + i = 0; + is = 0; + while (i < max) { + rc = flash_read(flash_dev, FLASH_SIMULATOR_BASE_OFFSET + i, + buf, sizeof(buf)); + zassert_equal(0, rc, "flash_read should succeed"); + do { + zassert_equal((uint8_t)~src[is], (uint8_t)buf[i & (sizeof(buf) - 1)], + "Expected src and buf to match at index %d\n", i); + ++i; + ++is; + /* Src has wrapped around */ + if (is >= sizeof(src)) { + is = 0; + } + } while (i & (sizeof(buf) - 1)); + zassert_equal(0, rc, "Unexpected value read"); + } +} +#endif ZTEST(flash_sim_api, test_get_erase_value) { diff --git a/tests/drivers/flash_simulator/flash_sim_impl/testcase.yaml b/tests/drivers/flash_simulator/flash_sim_impl/testcase.yaml index 11efcf49278..7f2fa5a4719 100644 --- a/tests/drivers/flash_simulator/flash_sim_impl/testcase.yaml +++ b/tests/drivers/flash_simulator/flash_sim_impl/testcase.yaml @@ -13,6 +13,17 @@ tests: - nucleo_f411re integration_platforms: - qemu_x86 + drivers.flash.flash_simulator.ramlike: + extra_args: CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE=n + platform_allow: + - qemu_x86 + - native_posix + - native_posix/native/64 + - native_sim + - native_sim/native/64 + - nucleo_f411re + integration_platforms: + - qemu_x86 drivers.flash.flash_simulator.qemu_erase_value_0x00: extra_args: DTC_OVERLAY_FILE=boards/qemu_x86_ev_0x00.overlay platform_allow: qemu_x86