blob: bbc1e6e5af7277eb566f9785face090b15c25c52 [file]
/*
* Copyright (c) 2023-2024 Nordic Semiconductor ASA
* Copyright (c) 2026 CodeWrights GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT zephyr_sim_flash
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/linker/devicetree_regions.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include <zephyr/random/random.h>
#include <zephyr/stats/stats.h>
#include <zephyr/pm/device.h>
#include <string.h>
#include <zephyr/drivers/flash/flash_simulator.h>
#ifdef CONFIG_ARCH_POSIX
#include "flash_simulator_native.h"
#include "cmdline.h"
#include "soc.h"
/* For backward compatibility, default file name for instance 0 is "flash.bin" */
#define DEFAULT_FLASH_FILE_PATH(n) COND_CODE_0(n, ("flash.bin"), ("flash" #n ".bin"))
#endif /* CONFIG_ARCH_POSIX */
struct flash_simulator_config {
uint8_t *prog_unit_buf;
const struct flash_parameters *flash_parameters;
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
const struct flash_simulator_params *flash_simulator_params;
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
#ifdef CONFIG_FLASH_PAGE_LAYOUT
const struct flash_pages_layout *pages_layout;
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
#ifdef CONFIG_ARCH_POSIX
const char *flash_file_default_path;
#endif /* CONFIG_ARCH_POSIX */
size_t base_offset;
size_t erase_unit;
size_t flash_size;
};
struct flash_simulator_data {
uint8_t *mock_flash;
bool flash_erase_at_start;
#ifdef CONFIG_ARCH_POSIX
int flash_fd;
const char *flash_file_path;
bool flash_rm_at_exit;
bool flash_in_ram;
#endif /* CONFIG_ARCH_POSIX */
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
const struct flash_simulator_cb *flash_simulator_cbs;
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
};
/* maximum number of pages that can be tracked by the stats module */
#define STATS_PAGE_COUNT_THRESHOLD 256
#define STATS_SECT_EC(N, _) STATS_SECT_ENTRY32(erase_cycles_unit##N)
#define STATS_NAME_EC(N, _) STATS_NAME(flash_sim_stats, erase_cycles_unit##N)
#define STATS_SECT_DIRTYR(N, _) STATS_SECT_ENTRY32(dirty_read_unit##N)
#define STATS_NAME_DIRTYR(N, _) STATS_NAME(flash_sim_stats, dirty_read_unit##N)
#ifdef CONFIG_FLASH_SIMULATOR_STATS
/* increment a unit erase cycles counter */
#define ERASE_CYCLES_INC(U) \
do { \
if (U < STATS_PAGE_COUNT_THRESHOLD) { \
(*(&flash_sim_stats.erase_cycles_unit0 + (U)) += 1); \
} \
} while (false)
#if (CONFIG_FLASH_SIMULATOR_STAT_PAGE_COUNT > STATS_PAGE_COUNT_THRESHOLD)
/* Limitation above is caused by used LISTIFY */
/* Using FLASH_SIMULATOR_FLASH_PAGE_COUNT allows to avoid terrible */
/* error logg at the output and work with the stats module partially */
#define FLASH_SIMULATOR_FLASH_PAGE_COUNT STATS_PAGE_COUNT_THRESHOLD
#else
#define FLASH_SIMULATOR_FLASH_PAGE_COUNT CONFIG_FLASH_SIMULATOR_STAT_PAGE_COUNT
#endif
/* simulator statistics */
STATS_SECT_START(flash_sim_stats)
STATS_SECT_ENTRY32(bytes_read) /* total bytes read */
STATS_SECT_ENTRY32(bytes_written) /* total bytes written */
STATS_SECT_ENTRY32(double_writes) /* num. of writes to non-erased units */
STATS_SECT_ENTRY32(flash_read_calls) /* calls to flash_read() */
STATS_SECT_ENTRY32(flash_read_time_us) /* time spent in flash_read() */
STATS_SECT_ENTRY32(flash_write_calls) /* calls to flash_write() */
STATS_SECT_ENTRY32(flash_write_time_us) /* time spent in flash_write() */
STATS_SECT_ENTRY32(flash_erase_calls) /* calls to flash_erase() */
STATS_SECT_ENTRY32(flash_erase_time_us) /* time spent in flash_erase() */
/* -- per-unit statistics -- */
/* erase cycle count for unit */
LISTIFY(FLASH_SIMULATOR_FLASH_PAGE_COUNT, STATS_SECT_EC, ())
/* number of read operations on worn out erase units */
LISTIFY(FLASH_SIMULATOR_FLASH_PAGE_COUNT, STATS_SECT_DIRTYR, ())
STATS_SECT_END;
STATS_SECT_DECL(flash_sim_stats) flash_sim_stats;
STATS_NAME_START(flash_sim_stats)
STATS_NAME(flash_sim_stats, bytes_read)
STATS_NAME(flash_sim_stats, bytes_written)
STATS_NAME(flash_sim_stats, double_writes)
STATS_NAME(flash_sim_stats, flash_read_calls)
STATS_NAME(flash_sim_stats, flash_read_time_us)
STATS_NAME(flash_sim_stats, flash_write_calls)
STATS_NAME(flash_sim_stats, flash_write_time_us)
STATS_NAME(flash_sim_stats, flash_erase_calls)
STATS_NAME(flash_sim_stats, flash_erase_time_us)
LISTIFY(FLASH_SIMULATOR_FLASH_PAGE_COUNT, STATS_NAME_EC, ())
LISTIFY(FLASH_SIMULATOR_FLASH_PAGE_COUNT, STATS_NAME_DIRTYR, ())
STATS_NAME_END(flash_sim_stats);
/* simulator dynamic thresholds */
STATS_SECT_START(flash_sim_thresholds)
STATS_SECT_ENTRY32(max_write_calls)
STATS_SECT_ENTRY32(max_erase_calls)
STATS_SECT_ENTRY32(max_len)
STATS_SECT_END;
STATS_SECT_DECL(flash_sim_thresholds) flash_sim_thresholds;
STATS_NAME_START(flash_sim_thresholds)
STATS_NAME(flash_sim_thresholds, max_write_calls)
STATS_NAME(flash_sim_thresholds, max_erase_calls)
STATS_NAME(flash_sim_thresholds, max_len)
STATS_NAME_END(flash_sim_thresholds);
#define FLASH_SIM_STATS_INC(group__, var__) STATS_INC(group__, var__)
#define FLASH_SIM_STATS_INCN(group__, var__, n__) STATS_INCN(group__, var__, n__)
#define FLASH_SIM_STATS_INIT_AND_REG(group__, size__, name__) \
STATS_INIT_AND_REG(group__, size__, name__)
#else
#define ERASE_CYCLES_INC(U) do {} while (false)
#define FLASH_SIM_STATS_INC(group__, var__)
#define FLASH_SIM_STATS_INCN(group__, var__, n__)
#define FLASH_SIM_STATS_INIT_AND_REG(group__, size__, name__)
#endif /* CONFIG_FLASH_SIMULATOR_STATS */
static int flash_range_is_valid(const struct device *dev, off_t offset,
size_t len)
{
const struct flash_simulator_config *cfg = dev->config;
if ((offset < 0 || offset >= cfg->flash_size ||
(cfg->flash_size - offset) < len)) {
return 0;
}
return 1;
}
static int flash_sim_read(const struct device *dev, const off_t offset,
void *data,
const size_t len)
{
const struct flash_simulator_config *cfg = dev->config;
struct flash_simulator_data *dev_data = dev->data;
if (!flash_range_is_valid(dev, offset, len)) {
return -EINVAL;
}
if (!IS_ENABLED(CONFIG_FLASH_SIMULATOR_UNALIGNED_READ)) {
if ((offset % cfg->flash_parameters->write_block_size) ||
(len % cfg->flash_parameters->write_block_size)) {
return -EINVAL;
}
}
FLASH_SIM_STATS_INC(flash_sim_stats, flash_read_calls);
memcpy(data, dev_data->mock_flash + offset, len);
FLASH_SIM_STATS_INCN(flash_sim_stats, bytes_read, len);
#ifdef CONFIG_FLASH_SIMULATOR_SIMULATE_TIMING
k_busy_wait(CONFIG_FLASH_SIMULATOR_MIN_READ_TIME_US);
FLASH_SIM_STATS_INCN(flash_sim_stats, flash_read_time_us,
CONFIG_FLASH_SIMULATOR_MIN_READ_TIME_US);
#endif
return 0;
}
static int flash_sim_write(const struct device *dev, const off_t offset,
const void *data, const size_t len)
{
const struct flash_simulator_config *cfg = dev->config;
struct flash_simulator_data *dev_data = dev->data;
if (!flash_range_is_valid(dev, offset, len)) {
return -EINVAL;
}
if ((offset % cfg->flash_parameters->write_block_size) ||
(len % cfg->flash_parameters->write_block_size)) {
return -EINVAL;
}
FLASH_SIM_STATS_INC(flash_sim_stats, flash_write_calls);
#ifdef CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE
/* check if any unit has been already programmed */
memset(cfg->prog_unit_buf, cfg->flash_parameters->erase_value,
cfg->flash_parameters->write_block_size);
#else
memcpy(cfg->prog_unit_buf, dev_data->mock_flash + offset,
cfg->flash_parameters->write_block_size);
#endif
for (uint32_t i = 0; i < len; i += cfg->flash_parameters->write_block_size) {
if (memcmp(cfg->prog_unit_buf, dev_data->mock_flash + offset + i,
cfg->flash_parameters->write_block_size)) {
FLASH_SIM_STATS_INC(flash_sim_stats, double_writes);
#if !CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES
return -EIO;
#endif
}
}
#ifdef CONFIG_FLASH_SIMULATOR_STATS
bool data_part_ignored = false;
if (flash_sim_thresholds.max_write_calls != 0) {
if (flash_sim_stats.flash_write_calls >
flash_sim_thresholds.max_write_calls) {
return 0;
} else if (flash_sim_stats.flash_write_calls ==
flash_sim_thresholds.max_write_calls) {
if (flash_sim_thresholds.max_len == 0) {
return 0;
}
data_part_ignored = true;
}
}
#endif
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
flash_simulator_write_byte_cb_t write_cb = NULL;
const struct flash_simulator_cb *cb = dev_data->flash_simulator_cbs;
if (cb != NULL) {
write_cb = cb->write_byte;
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
for (uint32_t i = 0; i < len; i++) {
#ifdef CONFIG_FLASH_SIMULATOR_STATS
if (data_part_ignored) {
if (i >= flash_sim_thresholds.max_len) {
return 0;
}
}
#endif /* CONFIG_FLASH_SIMULATOR_STATS */
uint8_t data_val = *((const uint8_t *)data + i);
#ifdef CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE
if (cfg->flash_parameters->erase_value == 0xff) {
/* only pull bits to zero */
data_val &= *(dev_data->mock_flash + offset + i);
} else {
/* only pull bits to one */
data_val |= *(dev_data->mock_flash + offset + i);
}
#endif
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
if (write_cb != NULL) {
int ret = write_cb(dev, offset + i, data_val);
if (ret < 0) {
return ret;
}
data_val = (uint8_t)ret;
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
*(dev_data->mock_flash + offset + i) = data_val;
}
FLASH_SIM_STATS_INCN(flash_sim_stats, bytes_written, len);
#ifdef CONFIG_FLASH_SIMULATOR_SIMULATE_TIMING
/* wait before returning */
k_busy_wait(CONFIG_FLASH_SIMULATOR_MIN_WRITE_TIME_US);
FLASH_SIM_STATS_INCN(flash_sim_stats, flash_write_time_us,
CONFIG_FLASH_SIMULATOR_MIN_WRITE_TIME_US);
#endif
return 0;
}
static int unit_erase(const struct device *dev, const uint32_t unit)
{
const struct flash_simulator_config *cfg = dev->config;
struct flash_simulator_data *dev_data = dev->data;
const off_t unit_addr = unit * cfg->erase_unit;
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
flash_simulator_erase_unit_cb_t erase_cb = NULL;
const struct flash_simulator_cb *cb = dev_data->flash_simulator_cbs;
if (cb != NULL) {
erase_cb = cb->erase_unit;
}
if (erase_cb != NULL) {
return erase_cb(dev, unit_addr);
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
/* erase the memory unit by setting it to erase value */
memset(dev_data->mock_flash + unit_addr, cfg->flash_parameters->erase_value,
cfg->erase_unit);
return 0;
}
static int flash_sim_erase(const struct device *dev, const off_t offset,
const size_t len)
{
const struct flash_simulator_config *cfg = dev->config;
if (!flash_range_is_valid(dev, offset, len)) {
return -EINVAL;
}
/* erase operation must be aligned to the erase unit boundary */
if ((offset % cfg->erase_unit) || (len % cfg->erase_unit)) {
return -EINVAL;
}
FLASH_SIM_STATS_INC(flash_sim_stats, flash_erase_calls);
#ifdef CONFIG_FLASH_SIMULATOR_STATS
if ((flash_sim_thresholds.max_erase_calls != 0) &&
(flash_sim_stats.flash_erase_calls >=
flash_sim_thresholds.max_erase_calls)){
return 0;
}
#endif
/* the first unit to be erased */
uint32_t unit_start = offset / cfg->erase_unit;
/* erase as many units as necessary and increase their erase counter */
for (uint32_t i = 0; i < len / cfg->erase_unit; i++) {
int ret;
ERASE_CYCLES_INC(unit_start + i);
ret = unit_erase(dev, unit_start + i);
if (ret < 0) {
return ret;
}
}
#ifdef CONFIG_FLASH_SIMULATOR_SIMULATE_TIMING
/* wait before returning */
k_busy_wait(CONFIG_FLASH_SIMULATOR_MIN_ERASE_TIME_US);
FLASH_SIM_STATS_INCN(flash_sim_stats, flash_erase_time_us,
CONFIG_FLASH_SIMULATOR_MIN_ERASE_TIME_US);
#endif
return 0;
}
#ifdef CONFIG_FLASH_PAGE_LAYOUT
static void flash_sim_page_layout(const struct device *dev,
const struct flash_pages_layout **layout,
size_t *layout_size)
{
const struct flash_simulator_config *cfg = dev->config;
*layout = cfg->pages_layout;
*layout_size = 1;
}
#endif
static int flash_sim_get_size(const struct device *dev, uint64_t *size)
{
const struct flash_simulator_config *cfg = dev->config;
*size = cfg->flash_size;
return 0;
}
static const struct flash_parameters *
flash_sim_get_parameters(const struct device *dev)
{
const struct flash_simulator_config *cfg = dev->config;
return cfg->flash_parameters;
}
#ifdef CONFIG_FLASH_EX_OP_ENABLED
int flash_sim_ex_op(const struct device *dev, uint16_t code, const uintptr_t in, void *out)
{
ARG_UNUSED(dev);
ARG_UNUSED(code);
ARG_UNUSED(in);
ARG_UNUSED(out);
return -ENOTSUP;
}
#endif /* CONFIG_FLASH_EX_OP_ENABLED */
static DEVICE_API(flash, flash_sim_api) = {
.read = flash_sim_read,
.write = flash_sim_write,
.erase = flash_sim_erase,
.get_parameters = flash_sim_get_parameters,
.get_size = flash_sim_get_size,
#ifdef CONFIG_FLASH_PAGE_LAYOUT
.page_layout = flash_sim_page_layout,
#endif
#ifdef CONFIG_FLASH_EX_OP_ENABLED
.ex_op = flash_sim_ex_op,
#endif /* CONFIG_FLASH_EX_OP_ENABLED */
};
#ifdef CONFIG_ARCH_POSIX
static int flash_mock_init(const struct device *dev)
{
int rc;
const struct flash_simulator_config *cfg = dev->config;
struct flash_simulator_data *dev_data = dev->data;
if (dev_data->flash_in_ram == false && dev_data->flash_file_path == NULL) {
dev_data->flash_file_path = cfg->flash_file_default_path;
}
rc = flash_mock_init_native(dev_data->flash_in_ram, &dev_data->mock_flash, cfg->flash_size,
&dev_data->flash_fd, dev_data->flash_file_path,
cfg->flash_parameters->erase_value,
dev_data->flash_erase_at_start);
if (rc < 0) {
return -EIO;
} else {
return 0;
}
}
#else
static int flash_mock_init(const struct device *dev)
{
const struct flash_simulator_config *cfg = dev->config;
struct flash_simulator_data *dev_data = dev->data;
if (dev_data->flash_erase_at_start) {
memset(dev_data->mock_flash, cfg->flash_parameters->erase_value, cfg->flash_size);
}
return 0;
}
#endif /* CONFIG_ARCH_POSIX */
static int flash_sim_pm_control(const struct device *dev, enum pm_device_action action)
{
/* No action needed, exists only to enable validating get/put balancing in tests */
return 0;
}
static int flash_init(const struct device *dev)
{
int rc;
FLASH_SIM_STATS_INIT_AND_REG(flash_sim_stats, STATS_SIZE_32, "flash_sim_stats");
FLASH_SIM_STATS_INIT_AND_REG(flash_sim_thresholds, STATS_SIZE_32,
"flash_sim_thresholds");
rc = flash_mock_init(dev);
if (rc == 0) {
rc = pm_device_driver_init(dev, flash_sim_pm_control);
}
return rc;
}
/* Extension to generic flash driver API */
void *z_impl_flash_simulator_get_memory(const struct device *dev,
size_t *mock_size)
{
const struct flash_simulator_config *cfg = dev->config;
struct flash_simulator_data *dev_data = dev->data;
*mock_size = cfg->flash_size;
return dev_data->mock_flash;
}
const struct flash_simulator_params *z_impl_flash_simulator_get_params(const struct device *dev)
{
__maybe_unused const struct flash_simulator_config *cfg = dev->config;
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
return cfg->flash_simulator_params;
#else
return NULL;
#endif
}
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
void z_impl_flash_simulator_set_callbacks(const struct device *dev,
const struct flash_simulator_cb *cb)
{
struct flash_simulator_data *dev_data = dev->data;
dev_data->flash_simulator_cbs = cb;
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
#ifdef CONFIG_USERSPACE
#include <zephyr/internal/syscall_handler.h>
void *z_vrfy_flash_simulator_get_memory(const struct device *dev,
size_t *mock_size)
{
K_OOPS(K_SYSCALL_SPECIFIC_DRIVER(dev, K_OBJ_DRIVER_FLASH, &flash_sim_api));
return z_impl_flash_simulator_get_memory(dev, mock_size);
}
void z_vrfy_flash_simulator_set_callbacks(const struct device *dev,
const struct flash_simulator_cb *cb)
{
K_OOPS(K_SYSCALL_SPECIFIC_DRIVER(dev, K_OBJ_DRIVER_FLASH, &flash_sim_api));
z_impl_flash_simulator_set_callbacks(dev, cb);
}
const struct flash_simulator_params *z_vrfy_flash_simulator_get_params(const struct device *dev)
{
K_OOPS(K_SYSCALL_SPECIFIC_DRIVER(dev, K_OBJ_DRIVER_FLASH, &flash_sim_api));
return z_impl_flash_simulator_get_params(dev);
}
#include <zephyr/syscalls/flash_simulator_get_memory_mrsh.c>
#endif /* CONFIG_USERSPACE */
#define SOC_NV_FLASH_COMPAT(n) COND_CODE_1(DT_NODE_HAS_COMPAT(n, soc_nv_flash), (n), ())
#define SOC_NV_FLASH_NODE(n) DT_INST_FOREACH_CHILD_STATUS_OKAY(n, SOC_NV_FLASH_COMPAT)
#define FLASH_SIMULATOR_BASE_OFFSET(n) DT_REG_ADDR(SOC_NV_FLASH_NODE(n))
#define FLASH_SIMULATOR_ERASE_UNIT(n) DT_PROP(SOC_NV_FLASH_NODE(n), erase_block_size)
#define FLASH_SIMULATOR_PROG_UNIT(n) DT_PROP(SOC_NV_FLASH_NODE(n), write_block_size)
#define FLASH_SIMULATOR_FLASH_SIZE(n) DT_REG_SIZE(SOC_NV_FLASH_NODE(n))
#define FLASH_SIMULATOR_ERASE_VALUE(n) DT_INST_PROP(n, erase_value)
#define FLASH_SIMULATOR_PAGE_COUNT(n) \
(FLASH_SIMULATOR_FLASH_SIZE(n) / FLASH_SIMULATOR_ERASE_UNIT(n))
/* Resolve the no_explicit_erase capability per device instance.
* DT_INST_PROP for a boolean type is 0 when absent, 1 when set in DTS,
* so a logical OR with the Kconfig fallback covers all cases correctly.
*/
#define FLASH_SIMULATOR_NO_EXPLICIT_ERASE(n) \
(DT_INST_PROP(n, no_explicit_erase) || \
!IS_ENABLED(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE))
#define MOCK_FLASH_SECTION(n) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, memory_region), \
(Z_GENERIC_SECTION(LINKER_DT_NODE_REGION_NAME(DT_INST_PHANDLE(n, memory_region)))), ())
#define FLASH_SIMULATOR_INIT(n) \
IF_DISABLED(CONFIG_ARCH_POSIX, ( \
static uint8_t mock_flash_##n[FLASH_SIMULATOR_FLASH_SIZE(n)] MOCK_FLASH_SECTION(n);\
)) \
static uint8_t prog_unit_buf_##n[FLASH_SIMULATOR_PROG_UNIT(n)]; \
static struct flash_simulator_data flash_simulator_data_##n = { \
.mock_flash = COND_CODE_1(CONFIG_ARCH_POSIX, (NULL), (mock_flash_##n)), \
.flash_erase_at_start = !DT_INST_NODE_HAS_PROP(n, memory_region), \
IF_ENABLED(CONFIG_ARCH_POSIX, ( \
.flash_fd = -1, \
)) \
}; \
\
static const struct flash_parameters flash_parameters_##n = { \
.write_block_size = FLASH_SIMULATOR_PROG_UNIT(n), \
.erase_value = FLASH_SIMULATOR_ERASE_VALUE(n), \
.caps = { \
.no_explicit_erase = FLASH_SIMULATOR_NO_EXPLICIT_ERASE(n), \
}, \
}; \
\
IF_ENABLED(CONFIG_FLASH_SIMULATOR_CALLBACKS, ( \
static const struct flash_simulator_params flash_simulator_params_##n = { \
.memory_size = FLASH_SIMULATOR_FLASH_SIZE(n), \
.base_offset = FLASH_SIMULATOR_BASE_OFFSET(n), \
.erase_unit = FLASH_SIMULATOR_ERASE_UNIT(n), \
.prog_unit = FLASH_SIMULATOR_PROG_UNIT(n), \
.explicit_erase = IS_ENABLED(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE), \
.erase_value = FLASH_SIMULATOR_ERASE_VALUE(n), \
}; \
)) \
\
IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, ( \
static const struct flash_pages_layout flash_sim_pages_layout_##n = { \
.pages_count = FLASH_SIMULATOR_PAGE_COUNT(n), \
.pages_size = FLASH_SIMULATOR_ERASE_UNIT(n), \
}; \
)) \
\
static const struct flash_simulator_config flash_simulator_config_##n = { \
.prog_unit_buf = prog_unit_buf_##n, \
.flash_parameters = &flash_parameters_##n, \
.base_offset = FLASH_SIMULATOR_BASE_OFFSET(n), \
.erase_unit = FLASH_SIMULATOR_ERASE_UNIT(n), \
.flash_size = FLASH_SIMULATOR_FLASH_SIZE(n), \
IF_ENABLED(CONFIG_FLASH_SIMULATOR_CALLBACKS, ( \
.flash_simulator_params = &flash_simulator_params_##n, \
)) \
IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, ( \
.pages_layout = &flash_sim_pages_layout_##n, \
)) \
IF_ENABLED(CONFIG_ARCH_POSIX, ( \
.flash_file_default_path = DEFAULT_FLASH_FILE_PATH(n) \
)) \
}; \
\
BUILD_ASSERT((FLASH_SIMULATOR_ERASE_UNIT(n) % FLASH_SIMULATOR_PROG_UNIT(n)) == 0, \
"Erase unit must be a multiple of program unit"); \
\
PM_DEVICE_DT_INST_DEFINE(n, flash_sim_pm_control); \
DEVICE_DT_INST_DEFINE(n, flash_init, PM_DEVICE_DT_INST_GET(n), &flash_simulator_data_##n, \
&flash_simulator_config_##n, POST_KERNEL, \
CONFIG_FLASH_INIT_PRIORITY, &flash_sim_api);
DT_INST_FOREACH_STATUS_OKAY(FLASH_SIMULATOR_INIT);
#ifdef CONFIG_ARCH_POSIX
#define FLASH_SIMULATOR_CLEANUP(n) \
flash_mock_cleanup_native( \
flash_simulator_data_##n.flash_in_ram, flash_simulator_data_##n.flash_fd, \
flash_simulator_data_##n.mock_flash, flash_simulator_config_##n.flash_size, \
flash_simulator_data_##n.flash_file_path, \
flash_simulator_data_##n.flash_rm_at_exit);
static void flash_native_cleanup(void)
{
DT_INST_FOREACH_STATUS_OKAY(FLASH_SIMULATOR_CLEANUP)
}
#define FLASH_SIMULATOR_INSTANCE_COMMAND_LINE_OPTS(n, INST_NAME) \
{ \
.option = INST_NAME, \
.name = "path", \
.type = 's', \
.dest = (void *)&flash_simulator_data_##n.flash_file_path, \
.descript = "Path to binary file to be used as flash by " INST_NAME \
", by default \"" DEFAULT_FLASH_FILE_PATH(n) "\"" \
}, \
{ \
.option = INST_NAME "_erase", \
.is_switch = true, \
.type = 'b', \
.dest = (void *)&flash_simulator_data_##n.flash_erase_at_start, \
.descript = "Erase the flash content of " INST_NAME " at startup" \
}, \
{ \
.option = INST_NAME "_rm", \
.is_switch = true, \
.type = 'b', \
.dest = (void *)&flash_simulator_data_##n.flash_rm_at_exit, \
.descript = "Remove the flash file of " INST_NAME " when terminating the " \
"execution" \
}, \
{ \
.option = INST_NAME "_in_ram", \
.is_switch = true, \
.type = 'b', \
.dest = (void *)&flash_simulator_data_##n.flash_in_ram, \
.descript = "Instead of a file, keep the content of " INST_NAME " just in RAM. " \
"If this is set, " INST_NAME ", " INST_NAME "_erase & " \
INST_NAME "_rm are ignored. The flash content is always erased at startup." \
},
#define FLASH_SIMULATOR_COMMAND_LINE_OPTS(n) \
FLASH_SIMULATOR_INSTANCE_COMMAND_LINE_OPTS(n, DEVICE_DT_NAME(SOC_NV_FLASH_NODE(n)))
static void flash_native_options(void)
{
static struct args_struct_t flash_options[] = {
/* For backward compatibility, instance 0 is also available with "flash" */
/* as command line option prefix. */
COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT), (
FLASH_SIMULATOR_INSTANCE_COMMAND_LINE_OPTS(0, "flash")
), ())
DT_INST_FOREACH_STATUS_OKAY(FLASH_SIMULATOR_COMMAND_LINE_OPTS)
ARG_TABLE_ENDMARKER
};
native_add_command_line_opts(flash_options);
}
NATIVE_TASK(flash_native_options, PRE_BOOT_1, 1);
NATIVE_TASK(flash_native_cleanup, ON_EXIT, 1);
#endif /* CONFIG_ARCH_POSIX */