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 <dominik.ermel@nordicsemi.no>
This commit is contained in:
Dominik Ermel 2024-03-06 21:53:44 +00:00 committed by Henrik Brix Andersen
commit ff81b52448
4 changed files with 205 additions and 1 deletions

View file

@ -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"

View file

@ -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
}

View file

@ -8,6 +8,10 @@
#include <zephyr/drivers/flash.h>
#include <zephyr/device.h>
/* 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)
{

View file

@ -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