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:
parent
a5f7ceff81
commit
ff81b52448
4 changed files with 205 additions and 1 deletions
|
@ -22,12 +22,32 @@ config FLASH_SIMULATOR_UNALIGNED_READ
|
||||||
Disable this option only if you want to simulate
|
Disable this option only if you want to simulate
|
||||||
a specific FLASH interface that requires aligned read access.
|
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
|
config FLASH_SIMULATOR_DOUBLE_WRITES
|
||||||
bool "Allow program units to be programmed more than once"
|
bool "Allow program units to be programmed more than once"
|
||||||
help
|
help
|
||||||
If selected, writing to a non-erased program unit will succeed, otherwise, it will return an error.
|
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
|
Keep in mind that write operations can only change value of a bit from erase-value to the
|
||||||
opposite.
|
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
|
config FLASH_SIMULATOR_SIMULATE_TIMING
|
||||||
bool "Hardware timing simulation"
|
bool "Hardware timing simulation"
|
||||||
|
|
|
@ -162,7 +162,12 @@ static const struct flash_driver_api flash_sim_api;
|
||||||
|
|
||||||
static const struct flash_parameters flash_sim_parameters = {
|
static const struct flash_parameters flash_sim_parameters = {
|
||||||
.write_block_size = FLASH_SIMULATOR_PROG_UNIT,
|
.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,
|
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);
|
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 */
|
/* check if any unit has been already programmed */
|
||||||
memset(buf, FLASH_SIMULATOR_ERASE_VALUE, sizeof(buf));
|
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) {
|
for (uint32_t i = 0; i < len; i += FLASH_SIMULATOR_PROG_UNIT) {
|
||||||
if (memcmp(buf, MOCK_FLASH(offset + i), sizeof(buf))) {
|
if (memcmp(buf, MOCK_FLASH(offset + i), sizeof(buf))) {
|
||||||
FLASH_SIM_STATS_INC(flash_sim_stats, double_writes);
|
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 */
|
#endif /* CONFIG_FLASH_SIMULATOR_STATS */
|
||||||
|
|
||||||
/* only pull bits to zero */
|
/* only pull bits to zero */
|
||||||
|
#if IS_ENABLED(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE)
|
||||||
#if FLASH_SIMULATOR_ERASE_VALUE == 0xFF
|
#if FLASH_SIMULATOR_ERASE_VALUE == 0xFF
|
||||||
*(MOCK_FLASH(offset + i)) &= *((uint8_t *)data + i);
|
*(MOCK_FLASH(offset + i)) &= *((uint8_t *)data + i);
|
||||||
#else
|
#else
|
||||||
*(MOCK_FLASH(offset + i)) |= *((uint8_t *)data + i);
|
*(MOCK_FLASH(offset + i)) |= *((uint8_t *)data + i);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
*(MOCK_FLASH(offset + i)) = *((uint8_t *)data + i);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
#include <zephyr/drivers/flash.h>
|
#include <zephyr/drivers/flash.h>
|
||||||
#include <zephyr/device.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 */
|
/* configuration derived from DT */
|
||||||
#ifdef CONFIG_ARCH_POSIX
|
#ifdef CONFIG_ARCH_POSIX
|
||||||
#define SOC_NV_FLASH_NODE DT_CHILD(DT_INST(0, zephyr_sim_flash), flash_0)
|
#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*/
|
/* Get access to the device and erase it ready for testing*/
|
||||||
static void test_init(void)
|
static void test_init(void)
|
||||||
{
|
{
|
||||||
|
@ -259,6 +301,8 @@ ZTEST(flash_sim_api, test_align)
|
||||||
zassert_equal(-EINVAL, rc, "Unexpected error code (%d)", rc);
|
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)
|
ZTEST(flash_sim_api, test_double_write)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -282,6 +326,122 @@ ZTEST(flash_sim_api, test_double_write)
|
||||||
&data, sizeof(data));
|
&data, sizeof(data));
|
||||||
zassert_equal(-EIO, rc, "Unexpected error code (%d)", rc);
|
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)
|
ZTEST(flash_sim_api, test_get_erase_value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,17 @@ tests:
|
||||||
- nucleo_f411re
|
- nucleo_f411re
|
||||||
integration_platforms:
|
integration_platforms:
|
||||||
- qemu_x86
|
- 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:
|
drivers.flash.flash_simulator.qemu_erase_value_0x00:
|
||||||
extra_args: DTC_OVERLAY_FILE=boards/qemu_x86_ev_0x00.overlay
|
extra_args: DTC_OVERLAY_FILE=boards/qemu_x86_ev_0x00.overlay
|
||||||
platform_allow: qemu_x86
|
platform_allow: qemu_x86
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue