Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions drivers/flash/Kconfig.simulator
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ menuconfig FLASH_SIMULATOR

if FLASH_SIMULATOR

config FLASH_SIMULATOR_CALLBACKS
bool "Provide callback mechanism"
help
If selected the flash_simulator_set_callbacks is implemented allowing
to change write and erase functions behavior.
This option is meant to be used with tests when checking module reaction
to different write/erase errors in the memory.

config FLASH_SIMULATOR_UNALIGNED_READ
bool "Allow read access to be unaligned"
default y
Expand Down
101 changes: 90 additions & 11 deletions drivers/flash/flash_simulator.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <zephyr/stats/stats.h>
#include <string.h>

#include <zephyr/drivers/flash/flash_simulator.h>

#ifdef CONFIG_ARCH_POSIX

#include "flash_simulator_native.h"
Expand Down Expand Up @@ -170,6 +172,26 @@ static const struct flash_parameters flash_sim_parameters = {
},
};


#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
#ifdef CONFIG_ARCH_POSIX
static struct flash_simulator_params flash_sim_params = {
#else
static const struct flash_simulator_params flash_sim_params = {
.memory = mock_flash,
#endif
.memory_size = FLASH_SIMULATOR_FLASH_SIZE,
.base_offset = FLASH_SIMULATOR_BASE_OFFSET,
.erase_unit = FLASH_SIMULATOR_ERASE_UNIT,
.prog_unit = FLASH_SIMULATOR_PROG_UNIT,
.explicit_erase = IS_ENABLED(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE),
.erase_value = FLASH_SIMULATOR_ERASE_VALUE,
};

static const struct flash_simulator_cb *flash_simulator_cbs;
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */


static int flash_range_is_valid(const struct device *dev, off_t offset,
size_t len)
{
Expand Down Expand Up @@ -231,7 +253,7 @@ 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)
#ifdef CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE
/* check if any unit has been already programmed */
memset(buf, FLASH_SIMULATOR_ERASE_VALUE, sizeof(buf));
#else
Expand Down Expand Up @@ -264,6 +286,15 @@ static int flash_sim_write(const struct device *dev, const off_t offset,
}
#endif

#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
flash_simulator_write_byte_cb_t write_cb = NULL;
const struct flash_simulator_cb *cb = 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) {
Expand All @@ -273,16 +304,28 @@ static int flash_sim_write(const struct device *dev, const off_t offset,
}
#endif /* CONFIG_FLASH_SIMULATOR_STATS */

/* only pull bits to zero */
#if defined(CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE)
uint8_t data_val = *((const uint8_t *)data + i);

#ifdef CONFIG_FLASH_SIMULATOR_EXPLICIT_ERASE
#if FLASH_SIMULATOR_ERASE_VALUE == 0xFF
*(MOCK_FLASH(offset + i)) &= *((uint8_t *)data + i);
/* only pull bits to zero */
data_val &= *(MOCK_FLASH(offset + i));
#else
*(MOCK_FLASH(offset + i)) |= *((uint8_t *)data + i);
/* only pull bits to one */
data_val |= *(MOCK_FLASH(offset + i));
#endif
#else
*(MOCK_FLASH(offset + i)) = *((uint8_t *)data + i);
#endif
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
if (write_cb != NULL) {
int ret = write_cb(dev, &flash_sim_params,
offset + i, data_val);
if (ret < 0) {
return ret;
}
data_val = (uint8_t)ret;
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */
*(MOCK_FLASH(offset + i)) = data_val;
}

FLASH_SIM_STATS_INCN(flash_sim_stats, bytes_written, len);
Expand All @@ -297,20 +340,31 @@ static int flash_sim_write(const struct device *dev, const off_t offset,
return 0;
}

static void unit_erase(const uint32_t unit)
static int unit_erase(const struct device *dev, const uint32_t unit)
{
const off_t unit_addr = unit * FLASH_SIMULATOR_ERASE_UNIT;

#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
flash_simulator_erase_unit_cb_t erase_cb = NULL;
const struct flash_simulator_cb *cb = flash_simulator_cbs;

if (cb != NULL) {
erase_cb = cb->erase_unit;
}
if (erase_cb != NULL) {
return erase_cb(dev, &flash_sim_params, unit_addr);
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */

/* erase the memory unit by setting it to erase value */
memset(MOCK_FLASH(unit_addr), FLASH_SIMULATOR_ERASE_VALUE,
FLASH_SIMULATOR_ERASE_UNIT);
return 0;
}

static int flash_sim_erase(const struct device *dev, const off_t offset,
const size_t len)
{
ARG_UNUSED(dev);

if (!flash_range_is_valid(dev, offset, len)) {
return -EINVAL;
}
Expand All @@ -335,8 +389,13 @@ static int flash_sim_erase(const struct device *dev, const off_t offset,

/* erase as many units as necessary and increase their erase counter */
for (uint32_t i = 0; i < len / FLASH_SIMULATOR_ERASE_UNIT; i++) {
int ret;

ERASE_CYCLES_INC(unit_start + i);
unit_erase(unit_start + i);
ret = unit_erase(dev, unit_start + i);
if (ret < 0) {
return ret;
}
}

#ifdef CONFIG_FLASH_SIMULATOR_SIMULATE_TIMING
Expand Down Expand Up @@ -405,6 +464,9 @@ static int flash_mock_init(const struct device *dev)
rc = flash_mock_init_native(flash_in_ram, &mock_flash, FLASH_SIMULATOR_FLASH_SIZE,
&flash_fd, flash_file_path, FLASH_SIMULATOR_ERASE_VALUE,
flash_erase_at_start);
#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
flash_sim_params.memory = mock_flash;
#endif

if (rc < 0) {
return -EIO;
Expand Down Expand Up @@ -498,6 +560,15 @@ void *z_impl_flash_simulator_get_memory(const struct device *dev,
return mock_flash;
}

#ifdef CONFIG_FLASH_SIMULATOR_CALLBACKS
void z_impl_flash_simulator_set_callbacks(const struct device *dev,
const struct flash_simulator_cb *cb)
{
ARG_UNUSED(dev);
flash_simulator_cbs = cb;
}
#endif /* CONFIG_FLASH_SIMULATOR_CALLBACKS */

#ifdef CONFIG_USERSPACE

#include <zephyr/internal/syscall_handler.h>
Expand All @@ -510,6 +581,14 @@ void *z_vrfy_flash_simulator_get_memory(const struct device *dev,
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);
}

#include <zephyr/syscalls/flash_simulator_get_memory_mrsh.c>

#endif /* CONFIG_USERSPACE */
97 changes: 97 additions & 0 deletions include/zephyr/drivers/flash/flash_simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,110 @@
extern "C" {
#endif

#include <zephyr/device.h>
#include <zephyr/types.h>
#include <sys/types.h>

/**
* @file
* @brief Flash simulator specific API.
*
* Extension for flash simulator.
*/

/**
* @brief Flash simulator parameters structure
*
* Auxiliary structure containing flash simulator parameters to be used by callbacks
* that can modify the behavior of the flash simulator driver.
*/
struct flash_simulator_params {
uint8_t *memory; /**< @brief The memory pointer */
size_t memory_size; /**< @brief The total size of the memory */
size_t base_offset; /**< @brief The base offset of the flash simulator */
size_t erase_unit; /**< @brief The erase unit size */
size_t prog_unit; /**< @brief The program unit size */
bool explicit_erase; /**< @brief Whether explicit erase is required */
uint8_t erase_value; /**< @brief The value used to represent erased memory */
};

/**
* @brief Flash simulator write byte callback type
*
* Callback used to overwrite single byte write. The callback gets the requested data
* and offset as the parameter. The offset is the same offset passed to the flash write.
* The callback must return a value to be written at the specified offset or negative value
* in case of error.
*
* @param dev Flash simulator device pointer.
* @param params Flash simulator parameters.
* @param offset Offset within the flash simulator memory.
* @param data Data byte to be written.
* @return Value to be written at the specified offset or negative value in case of error.
*/
typedef int (*flash_simulator_write_byte_cb_t)(const struct device *dev,
const struct flash_simulator_params *params,
off_t offset,
const uint8_t data);

/**
* @brief Flash simulator erase unit callback type
*
* Callback used to overwrite unit erase operation. The callback gets the unit offset
* as the parameter. The offset is the same offset passed to the flash erase. The callback
* must return 0 in case of success or negative value in case of error.
*
* @note If this callback is set, the flash simulator driver will not perform any erase operation
* by itself.
*
* @param dev Flash simulator device pointer.
* @param params Flash simulator parameters.
* @param unit_offset Offset within the flash simulator memory.
* @return 0 in case of success or negative value in case of error.
*/
typedef int (*flash_simulator_erase_unit_cb_t)(const struct device *dev,
const struct flash_simulator_params *params,
off_t unit_offset);

/**
* @brief Flash simulator callbacks structure
*
* Structure containing flash simulator operation callbacks.
*/
struct flash_simulator_cb {
flash_simulator_write_byte_cb_t write_byte; /**< @brief Byte write callback */
flash_simulator_erase_unit_cb_t erase_unit; /**< @brief Unit erase callback */
};

/**
* @brief Get pointer to the flash simulator mock flash memory
*
* This function allows the caller to get the address of the RAM buffer
* in which the flash simulator emulates its flash memory content.
*
* @param[in] params flash simulator parameters.
* @param[in] offset offset within the flash simulator memory.
*
* @retval pointer to the ram buffer at specified offset
*/
static inline uint8_t *flash_simulator_mock_flash(const struct flash_simulator_params *params,
off_t offset)
{
return params->memory + offset;
}

/**
* @brief Set flash simulator callbacks
*
* This function allows the caller to set custom callbacks for byte write and
* unit erase operations performed by the flash simulator.
*
* @param[in] dev flash simulator device pointer.
* @param[in] cb pointer to the structure containing the callbacks.
*/
__syscall void flash_simulator_set_callbacks(const struct device *dev,
const struct flash_simulator_cb *cb);

/**
* @brief Obtain a pointer to the RAM buffer used but by the simulator
*
Expand Down
1 change: 1 addition & 0 deletions tests/drivers/flash_simulator/flash_sim_impl/prj.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CONFIG_ZTEST=y
CONFIG_FLASH=y
CONFIG_FLASH_SIMULATOR=y
CONFIG_FLASH_SIMULATOR_CALLBACKS=y
CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=n
CONFIG_FLASH_SIMULATOR_UNALIGNED_READ=n
Loading