Files
littlefs/bd/lfs3_kiwibd.h
T
Christopher Haster 3db2bb980b runners: emubd/kiwibd: Adopted lower-level bus+buffer bd sim
After letting it sit for a bit, the previous byte+op sim comes across as
overly clever in a way that is counter-productive. This is highlighted
by erase-timing scaling in a confusing way when per-op.

Fortunately, with a bit of tweaking, we can instead model the bd sim as
separate bus+buffer timings. This seems more intuitive and is closer to
how the actual hardware works.

---

In the bus+buffer model, bd operations are simulated using two sets of
timing estimates:

  buffer timings (nor)          bus timings (nor)
  read_timing (0)               readed_timing (40 ns/B)
  prog_timing (1563 ns/B)       progged_timing (19 ns/B)
  erase_timing (10986 ns/B)     erased_timing (0)

Bus timings are a simple multiplier of the bytes read/progged/erased,
while buffer timings are rounded up + aligned to the nearest "width":

  bd geometry (nor)             bd buffers (nor)
  read_size (1 B)               read_width (1 B)
  prog_size (1 B)               prog_width (256 B)
  erase_size (4096 B)           erase_width (4096 B)

For most purposes, the width should just be the device's read/prog/erase
buffer, but I went with the name width to try to keep it generic and
avoid confusion with "buffer" elsewhere in the codebase.

Some notes:

- Like the byte+op sim, the bus+buffer sim allows penalizing small
  operations without artificially limiting what operations are possible.

- Because buffer timings depend on read/prog/erase alignment, there's no
  simple equation from ops+bytes to bus+buffer. But as a tradeoff, this
  new sim more accurately penalizes unaligned operations.

- All timings are still kept as per-byte instead of per-width. This has
  proven to be more flexible when benchmarking, as you usually what
  timings to scale with the relevant operation.

- Currently this implemented by changing reads/progs/erases to track the
  number of "widths" read/progged/erased after alignment. Which makes
  the simtime formula roughly:

    simtime = reads*read_width*read_timing + readed*readed_timing
              (per-butter)                   (per-bus)

  I considered keeping separate counters for calls (read_calls/
  prog_calls/erase_calls?), but not sure there's a good reason to. The
  theory behind these widths is there no functional difference between
  one big call vs multiple width sized calls, though maybe they would be
  useful for debugging?

  We can always add these later if they turn out to be useful.

- When widths are disable (0), reads/progs/erases reverts to the number
  of read/prog/erase calls.

  This is the behavior when BENCH_SIMPLE is defined at compile-time.
2026-03-09 22:50:29 -05:00

185 lines
5.8 KiB
C

/*
* kiwibd - A lightweight variant of emubd, useful for emulating large
* disks backed by a file or in RAM.
*
* Unlike emubd, file-backed disks are _not_ mirrored in RAM. kiwibd has
* fewer features than emubd, prioritizing speed for benchmarking.
*
*
*/
#ifndef LFS3_KIWIBD_H
#define LFS3_KIWIBD_H
#include "lfs3.h"
#include "lfs3_util.h"
// Block device specific tracing
#ifndef LFS3_KIWIBD_TRACE
#ifdef LFS3_KIWIBD_YES_TRACE
#define LFS3_KIWIBD_TRACE(...) LFS3_TRACE(__VA_ARGS__)
#else
#define LFS3_KIWIBD_TRACE(...)
#endif
#endif
// Type for measuring read/program/erase operations
typedef uint64_t lfs3_kiwibd_io_t;
typedef int64_t lfs3_kiwibd_sio_t;
// Type for delays in nanoseconds
typedef uint64_t lfs3_kiwibd_ns_t;
typedef int64_t lfs3_kiwibd_sns_t;
// kiwibd config, this is required for testing
struct lfs3_kiwibd_cfg {
// Optional statically allocated buffer for the block device. Ignored
// if disk_path is provided.
void *buffer;
// 8-bit erase value to use for simulating erases. -1 simulates a noop
// erase, which is faster than simulating a fixed erase value. -2 emulates
// nor-masking, which is useful for testing other filesystems (littlefs
// does _not_ rely on this!).
int32_t erase_value;
// Simulated read width, this is only used for simulated read timing
// and emulates the physical read hardware on the device. Defaults
// to 1 byte.
lfs3_size_t read_width;
// Simulated prog width, this is only used for simulated prog timing
// and emulates the physical prog hardware on the device. Defaults
// to 1 byte.
lfs3_size_t prog_width;
// Simulated erase width, this is only used for simulated erase timing
// and emulates physical erase hardware on the device. Defaults to 1
// byte.
lfs3_size_t erase_width;
// Simulated per-byte read timing in nanoseconds, this is added to
// simtime each read call after aligning up to the necessary number
// of read_widths to emulate the read operation.
lfs3_kiwibd_ns_t read_timing;
// Simulated per-byte prog timing in nanoseconds, this is added to
// simtime each prog call after aligning up to the necessary number
// of prog_widths to emulate the prog operation.
lfs3_kiwibd_ns_t prog_timing;
// Simulated per-byte erase timing in nanoseconds, this is added to
// simtime each erase call after aligning up to the necessary number
// of erase_widths to emulate the erase operation.
lfs3_kiwibd_ns_t erase_timing;
// Simulated per-byte read timing in nanoseconds, this ignores
// read_width and can be used to simulate relevant bus overhead.
lfs3_kiwibd_ns_t readed_timing;
// Simulated per-byte prog timing in nanoseconds, this ignores
// prog_width and can be used to simulate relevant bus overhead.
lfs3_kiwibd_ns_t progged_timing;
// Simulated per-byte erase timing in nanoseconds, this ignores
// erase_width and can be used to simulate relevant bus overhead.
lfs3_kiwibd_ns_t erased_timing;
// Artificial read transaction delay in nanoseconds, there is no
// purpose for this other than slowing down the simulation.
lfs3_kiwibd_ns_t read_sleep;
// Artificial prog transaction delay in nanoseconds, there is no
// purpose for this other than slowing down the simulation.
lfs3_kiwibd_ns_t prog_sleep;
// Artificial erase transaction delay in nanoseconds, there is no
// purpose for this other than slowing down the simulation.
lfs3_kiwibd_ns_t erase_sleep;
};
// kiwibd state
typedef struct lfs3_kiwibd {
// backing disk
int fd;
union {
uint8_t *scratch;
uint8_t *mem;
} u;
// amount read/progged/erased
lfs3_kiwibd_io_t reads;
lfs3_kiwibd_io_t progs;
lfs3_kiwibd_io_t erases;
lfs3_kiwibd_io_t readed;
lfs3_kiwibd_io_t progged;
lfs3_kiwibd_io_t erased;
const struct lfs3_kiwibd_cfg *cfg;
} lfs3_kiwibd_t;
/// Block device API ///
// Create a kiwibd using the geometry in lfs3_cfg
//
// If path is provided, kiwibd will use the file to back the block
// device, allowing emulation of block devices > available RAM.
//
int lfs3_kiwibd_create(const struct lfs3_cfg *cfg, const char *path);
int lfs3_kiwibd_createcfg(const struct lfs3_cfg *cfg, const char *path,
const struct lfs3_kiwibd_cfg *bdcfg);
// Clean up memory associated with block device
int lfs3_kiwibd_destroy(const struct lfs3_cfg *cfg);
// Read a block
int lfs3_kiwibd_read(const struct lfs3_cfg *cfg, lfs3_block_t block,
lfs3_off_t off, void *buffer, lfs3_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs3_kiwibd_prog(const struct lfs3_cfg *cfg, lfs3_block_t block,
lfs3_off_t off, const void *buffer, lfs3_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs3_kiwibd_erase(const struct lfs3_cfg *cfg, lfs3_block_t block);
// Sync the block device
int lfs3_kiwibd_sync(const struct lfs3_cfg *cfg);
/// Additional kiwibd features ///
// Get simulated runtime in nanoseconds
lfs3_kiwibd_sns_t lfs3_kiwibd_simtime(const struct lfs3_cfg *cfg);
// Reset simulation counters
int lfs3_kiwibd_simreset(const struct lfs3_cfg *cfg);
// Get total number of read transactions
lfs3_kiwibd_sio_t lfs3_kiwibd_reads(const struct lfs3_cfg *cfg);
// Get total number of prog transactions
lfs3_kiwibd_sio_t lfs3_kiwibd_progs(const struct lfs3_cfg *cfg);
// Get total number of erase transactions
lfs3_kiwibd_sio_t lfs3_kiwibd_erases(const struct lfs3_cfg *cfg);
// Get total amount of bytes read
lfs3_kiwibd_sio_t lfs3_kiwibd_readed(const struct lfs3_cfg *cfg);
// Get total amount of bytes programmed
lfs3_kiwibd_sio_t lfs3_kiwibd_progged(const struct lfs3_cfg *cfg);
// Get total amount of bytes erased
lfs3_kiwibd_sio_t lfs3_kiwibd_erased(const struct lfs3_cfg *cfg);
#endif