ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_ftl.c File Reference

Flash Translation Layer implementation – copy-on-write + wear-levelling. More...

#include "ra8_ftl.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_io_blockdev.h"
#include "ra8_io_blockdev_backend.h"
Include dependency graph for ra8_ftl.c:

Go to the source code of this file.

Enumerations

enum  ra8_ftl_impl_const_t : uint32_t {
  k_ra8_ftl_one_block = 1 ,
  k_ra8_ftl_req_erase_unit = 1 ,
  k_ra8_ftl_count_max = 0xFFFFFFFFU
}
 Implementation-private layout constants. More...

Functions

static ra8_err_t internal_reclaim_stale (ra8_ftl_t *ftl)
 Reclaim every STALE physical block by erasing it back to FREE.
static ra8_err_t internal_pick_free (const ra8_ftl_t *ftl, uint32_t *out)
 Select the least-erased FREE physical block (wear-levelling).
static ra8_err_t internal_alloc_blank (ra8_ftl_t *ftl, uint32_t *out)
 Allocate a blank physical block, reclaiming stale blocks if needed.
static ra8_err_t internal_read_one (const ra8_ftl_t *ftl, uint32_t lbn, uint8_t *dst)
 Read one logical block, resolving the map (or erase value if unmapped).
static ra8_err_t internal_write_one (ra8_ftl_t *ftl, uint32_t lbn, const uint8_t *src)
 Write one logical block via copy-on-write relocation.
static ra8_err_t internal_bounds (const ra8_ftl_t *ftl, uint32_t lba, uint32_t count)
 Reject an out-of-range presented [lba, lba+count) range.
static ra8_err_t internal_dev_read (void *ctx, uint32_t lba, uint32_t count, uint8_t *buf)
 Presented vtable: read count logical blocks at lba.
static ra8_err_t internal_dev_write (void *ctx, uint32_t lba, uint32_t count, const uint8_t *buf)
 Presented vtable: write count logical blocks at lba.
static ra8_err_t internal_dev_erase (void *ctx, uint32_t lba, uint32_t count)
 Presented vtable: erase count logical blocks at lba.
static ra8_err_t internal_dev_get_caps (const void *ctx, ra8_io_blockdev_caps_t *out)
 Presented vtable: report the FTL's free-overwrite capabilities.
static ra8_err_t internal_dev_sync (void *ctx)
 Presented vtable: flush the underlying device.
static ra8_err_t internal_check_caps (const ra8_io_blockdev_caps_t *caps, uint32_t logical_blocks, uint32_t physical_blocks, uint8_t *erase_out)
 Validate the underlying device's capabilities for FTL use.
static ra8_err_t internal_reset_tables (ra8_ftl_t *ftl)
 Cold-start the FTL bookkeeping tables.
static ra8_err_t internal_validate_init_args (const ra8_ftl_t *bd, const ra8_io_blockdev_t *raw, const uint16_t *map, const ra8_ftl_pblock_t *pblocks, const uint8_t *scratch, uint32_t logical_blocks, uint32_t physical_blocks)
 Validate the pointer + sizing arguments to ra8_ftl_init.
ra8_err_t ra8_ftl_init (ra8_ftl_t *bd, const ra8_io_blockdev_t *raw, uint16_t *map, uint32_t logical_blocks, ra8_ftl_pblock_t *pblocks, uint32_t physical_blocks, uint8_t *scratch)
 Initialise an FTL over an erase-before-write block device.
ra8_err_t ra8_ftl_as_blockdev (ra8_ftl_t *ftl, ra8_io_blockdev_t *out)
 Expose an initialised FTL as a free-overwrite ra8_io_blockdev_t.
ra8_err_t ra8_ftl_wear_stats (const ra8_ftl_t *ftl, uint32_t *max_out, uint32_t *min_out)
 Report the highest per-physical-block erase count seen so far.
ra8_err_t ra8_ftl_phys_of (const ra8_ftl_t *ftl, uint32_t lbn, uint16_t *phys_out)
 Report the physical block currently backing a logical block.

Variables

static const char *const s_tag = "ra8_ftl"
 Module log tag.
static const ra8_io_blockdev_iface_t s_ftl_iface
 Presented FTL vtable – a clean free-overwrite block device.

Detailed Description

Flash Translation Layer implementation – copy-on-write + wear-levelling.

Tag
[Ring 4 / PAL] {World: NS}

Implements ra8_ftl_init, ra8_ftl_as_blockdev, and the ra8_io_blockdev_iface vtable the FTL presents upward. Reads resolve the logical->physical map and forward to the underlying device (or synthesise the erase value for unmapped blocks). Writes allocate the least-erased free physical block, erase it, program the data, and re-point the map – never touching a non-blank block.

Definition in file ra8_ftl.c.

Enumeration Type Documentation

◆ ra8_ftl_impl_const_t

enum ra8_ftl_impl_const_t : uint32_t

Implementation-private layout constants.

Single-block transfer count and the required underlying erase granularity.

Since
0.1.0
Enumerator
k_ra8_ftl_one_block 

Count for a single-block raw transfer.

k_ra8_ftl_req_erase_unit 

Required raw erase_unit_blocks value.

k_ra8_ftl_count_max 

Sentinel: highest possible erase count.

Definition at line 45 of file ra8_ftl.c.

Function Documentation

◆ internal_alloc_blank()

ra8_err_t internal_alloc_blank ( ra8_ftl_t * ftl,
uint32_t * out )
static

Allocate a blank physical block, reclaiming stale blocks if needed.

Tries internal_pick_free; if no free block exists it runs internal_reclaim_stale and retries exactly once. Two attempts suffice because the FTL guarantees at least one spare block, so after reclamation a free block always exists. The chosen block is erased (advancing its erase count) so it is blank before the caller programs it.

Parameters
[in,out]ftlInitialised FTL handle.
[out]outReceives the allocated, erased physical block index.
Returns
ra8_err_t Error code.
Return values
k_ra8_okA blank block was allocated into *out.
k_ra8_err_null_ptrftl or out was NULL.
k_ra8_err_no_memNo block available even after reclamation.
otherPropagated underlying erase error.
Precondition
ftl is initialised with at least one spare block.
out is writable.
Postcondition
On success block *out is FREE-tagged and reads back as erase value.
On any non-ok return no map entry is changed.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 184 of file ra8_ftl.c.

References ra8_ftl_pblock_t::erase_count, internal_pick_free(), internal_reclaim_stale(), k_ra8_err_no_data, k_ra8_err_no_mem, k_ra8_ftl_one_block, k_ra8_ok, ra8_ftl_t::pblocks, RA8_CHECK_NULL_PTR, ra8_io_blockdev_erase(), ra8_ftl_t::raw, and s_tag.

Referenced by internal_write_one().

◆ internal_bounds()

ra8_err_t internal_bounds ( const ra8_ftl_t * ftl,
uint32_t lba,
uint32_t count )
static

Reject an out-of-range presented [lba, lba+count) range.

Two single-condition checks (no compound decision) guarding against count overflow and a range that runs past logical_blocks.

Parameters
[in]ftlInitialised FTL handle.
[in]lbaFirst logical block address.
[in]countNumber of logical blocks in the range.
Returns
ra8_err_t Error code.
Return values
k_ra8_okRange is within the presented capacity.
k_ra8_err_out_of_rangeRange exceeds logical_blocks.
Precondition
ftl is initialised.
ftl->logical_blocks reflects the presented size.
Postcondition
No state is mutated.
The return reflects only the range/capacity comparison.
Note
Thread-safe (pure comparison).
Since
0.1.0

Definition at line 341 of file ra8_ftl.c.

References k_ra8_err_out_of_range, k_ra8_ok, and ra8_ftl_t::logical_blocks.

Referenced by internal_dev_erase(), internal_dev_read(), and internal_dev_write().

◆ internal_check_caps()

ra8_err_t internal_check_caps ( const ra8_io_blockdev_caps_t * caps,
uint32_t logical_blocks,
uint32_t physical_blocks,
uint8_t * erase_out )
static

Validate the underlying device's capabilities for FTL use.

Single-condition checks (no compound decisions): the device must not be read-only, must have erase_unit_blocks == 1, must hold at least physical_blocks blocks, and must leave at least one spare over logical_blocks. Also snapshots the erase value into *erase_out.

Parameters
[in]capsUnderlying device capabilities.
[in]logical_blocksBlocks the FTL will present.
[in]physical_blocksBlocks the caller claims the device has.
[out]erase_outReceives the medium erase value.
Returns
ra8_err_t Error code.
Return values
k_ra8_okUnderlying device is suitable.
k_ra8_err_null_ptrerase_out was NULL.
k_ra8_err_invalid_argDevice unsuitable (read-only, wrong erase unit, too small, or no spare block).
Precondition
caps was filled by ra8_io_blockdev_get_caps.
erase_out is writable.
Postcondition
On success *erase_out holds the medium erase byte.
No device state is mutated.
Note
Thread-safe (pure validation).
Since
0.1.0

Definition at line 609 of file ra8_ftl.c.

References ra8_io_blockdev_caps_t::block_count, ra8_io_blockdev_caps_t::erase_unit_blocks, ra8_io_blockdev_caps_t::erase_value, k_ra8_err_invalid_arg, k_ra8_ftl_min_spare, k_ra8_ftl_req_erase_unit, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_io_blockdev_caps_t::read_only, and s_tag.

Referenced by ra8_ftl_init().

◆ internal_dev_erase()

ra8_err_t internal_dev_erase ( void * ctx,
uint32_t lba,
uint32_t count )
static

Presented vtable: erase count logical blocks at lba.

For the free-overwrite device the FTL presents, an erase simply unmaps each logical block: its current physical block becomes STALE (reclaimed later) and a subsequent read returns the erase value. The per-block loop is statically bounded by count.

Parameters
[in]ctxFTL handle (as a void cookie).
[in]lbaFirst logical block address.
[in]countNumber of logical blocks to erase.
Returns
ra8_err_t Error code.
Return values
k_ra8_okLogical blocks unmapped.
k_ra8_err_null_ptrctx was NULL.
k_ra8_err_out_of_rangeRange past presented capacity.
Precondition
ctx is an initialised FTL.
count is non-zero for any observable effect.
Postcondition
On success each block in range reads back as the erase value.
On failure no mapping is changed.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 475 of file ra8_ftl.c.

References internal_bounds(), k_ra8_ftl_pstate_stale, k_ra8_ftl_unmapped, k_ra8_ok, ra8_ftl_t::map, ra8_ftl_t::pblocks, RA8_CHECK_NULL_PTR, s_tag, and ra8_ftl_pblock_t::state.

◆ internal_dev_get_caps()

ra8_err_t internal_dev_get_caps ( const void * ctx,
ra8_io_blockdev_caps_t * out )
static

Presented vtable: report the FTL's free-overwrite capabilities.

Reports logical_blocks blocks of a medium that needs no erase-before-write (the FTL hides that), with the underlying erase value preserved so the FAT bridge handles formatting consistently.

Parameters
[in]ctxFTL handle (as a const void cookie).
[out]outCapabilities snapshot.
Returns
ra8_err_t Error code.
Return values
k_ra8_ok*out populated.
k_ra8_err_null_ptrctx or out was NULL.
Precondition
ctx is an initialised FTL.
out is writable.
Postcondition
On success *out advertises must_erase_before_write == false.
No FTL or device state is mutated.
Note
Thread-safe (pure read).
Since
0.1.0

Definition at line 518 of file ra8_ftl.c.

References ra8_io_blockdev_caps_t::block_count, ra8_io_blockdev_caps_t::erase_unit_blocks, ra8_ftl_t::erase_value, ra8_io_blockdev_caps_t::erase_value, k_ra8_ftl_one_block, k_ra8_io_block_size_bytes, k_ra8_ok, ra8_io_blockdev_caps_t::logical_block_bytes, ra8_ftl_t::logical_blocks, ra8_io_blockdev_caps_t::must_erase_before_write, ra8_io_blockdev_caps_t::program_size_bytes, RA8_CHECK_NULL_PTR, ra8_io_blockdev_caps_t::read_only, and s_tag.

◆ internal_dev_read()

ra8_err_t internal_dev_read ( void * ctx,
uint32_t lba,
uint32_t count,
uint8_t * buf )
static

Presented vtable: read count logical blocks at lba.

Bounds-checks then resolves each logical block one at a time through internal_read_one. The per-block loop is statically bounded by count.

Parameters
[in]ctxFTL handle (as a void cookie).
[in]lbaFirst logical block address.
[in]countNumber of logical blocks to read.
[out]bufDestination buffer (>= count * 512 bytes).
Returns
ra8_err_t Error code.
Return values
k_ra8_okBlocks resolved into buf.
k_ra8_err_null_ptrctx or buf was NULL.
k_ra8_err_out_of_rangeRange past presented capacity.
otherPropagated underlying read error.
Precondition
ctx is an initialised FTL.
buf is writable for count * 512 bytes.
Postcondition
On success buf mirrors the logical contents.
On failure buf may be partially written; callers retry the range.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 380 of file ra8_ftl.c.

References internal_bounds(), internal_read_one(), k_ra8_io_block_size_bytes, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

◆ internal_dev_sync()

ra8_err_t internal_dev_sync ( void * ctx)
static

Presented vtable: flush the underlying device.

The FTL holds no write buffering of its own (each write commits immediately), so it simply forwards the sync to the underlying device.

Parameters
[in]ctxFTL handle (as a void cookie).
Returns
ra8_err_t Error code.
Return values
k_ra8_okUnderlying device flushed (or nothing to flush).
k_ra8_err_null_ptrctx was NULL.
otherPropagated underlying sync error.
Precondition
ctx is an initialised FTL.
The device is idle (no concurrent access).
Postcondition
On success the medium reflects every prior successful write.
No FTL state is mutated.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 557 of file ra8_ftl.c.

References RA8_CHECK_NULL_PTR, ra8_io_blockdev_sync(), ra8_ftl_t::raw, and s_tag.

◆ internal_dev_write()

ra8_err_t internal_dev_write ( void * ctx,
uint32_t lba,
uint32_t count,
const uint8_t * buf )
static

Presented vtable: write count logical blocks at lba.

Bounds-checks then relocates each logical block one at a time through internal_write_one (copy-on-write). The per-block loop is statically bounded by count.

Parameters
[in]ctxFTL handle (as a void cookie).
[in]lbaFirst logical block address.
[in]countNumber of logical blocks to write.
[in]bufSource buffer (>= count * 512 bytes).
Returns
ra8_err_t Error code.
Return values
k_ra8_okBlocks written.
k_ra8_err_null_ptrctx or buf was NULL.
k_ra8_err_out_of_rangeRange past presented capacity.
otherPropagated underlying erase/program error.
Precondition
ctx is an initialised FTL.
buf is readable for count * 512 bytes.
Postcondition
On success logical blocks [lba, lba+count) equal buf.
On failure earlier blocks in the range may already be committed.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 428 of file ra8_ftl.c.

References internal_bounds(), internal_write_one(), k_ra8_io_block_size_bytes, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

◆ internal_pick_free()

ra8_err_t internal_pick_free ( const ra8_ftl_t * ftl,
uint32_t * out )
static

Select the least-erased FREE physical block (wear-levelling).

Linear scan bounded by physical_blocks tracking the FREE block with the lowest erase count. Spreading allocation toward the least-worn free block is the wear-levelling policy. Returns k_ra8_err_no_data if no FREE block exists so the caller can trigger reclamation and retry.

Parameters
[in]ftlInitialised FTL handle.
[out]outReceives the chosen physical block index.
Returns
ra8_err_t Error code.
Return values
k_ra8_okA free block was chosen into *out.
k_ra8_err_null_ptrftl or out was NULL.
k_ra8_err_no_dataNo FREE physical block is available.
Precondition
ftl is initialised.
out is writable.
Postcondition
On success *out < ftl->physical_blocks and that block is FREE.
No FTL or device state is mutated.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 128 of file ra8_ftl.c.

References ra8_ftl_pblock_t::erase_count, k_ra8_err_no_data, k_ra8_ftl_pstate_free, k_ra8_ok, ra8_ftl_t::pblocks, ra8_ftl_t::physical_blocks, RA8_CHECK_NULL_PTR, s_tag, and ra8_ftl_pblock_t::state.

Referenced by internal_alloc_blank().

◆ internal_read_one()

ra8_err_t internal_read_one ( const ra8_ftl_t * ftl,
uint32_t lbn,
uint8_t * dst )
static

Read one logical block, resolving the map (or erase value if unmapped).

If the logical block is unmapped the destination is filled with the medium erase value (the block has never been written). Otherwise the underlying physical block is read through.

Parameters
[in]ftlInitialised FTL handle.
[in]lbnLogical block number (< logical_blocks).
[out]dst512-byte destination buffer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okBlock resolved into dst.
k_ra8_err_null_ptrftl or dst was NULL.
otherPropagated underlying read error.
Precondition
ftl is initialised.
lbn < ftl->logical_blocks.
Postcondition
On success dst holds the logical block's current contents.
No FTL or device state is mutated.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 247 of file ra8_ftl.c.

References ra8_ftl_t::erase_value, k_ra8_ftl_one_block, k_ra8_ftl_unmapped, k_ra8_io_block_size_bytes, k_ra8_ok, ra8_ftl_t::map, memset(), RA8_CHECK_NULL_PTR, ra8_io_blockdev_read(), ra8_ftl_t::raw, and s_tag.

Referenced by internal_dev_read().

◆ internal_reclaim_stale()

ra8_err_t internal_reclaim_stale ( ra8_ftl_t * ftl)
static

Reclaim every STALE physical block by erasing it back to FREE.

Scans the physical metadata and, for each STALE block, issues a single-block erase on the underlying device, advances its erase count, and marks it FREE. The loop is statically bounded by physical_blocks. Called only when the allocator finds no FREE block, so reclamation cost is amortised.

Parameters
[in,out]ftlInitialised FTL handle.
Returns
ra8_err_t Error code.
Return values
k_ra8_okReclamation pass completed (some blocks may stay live).
k_ra8_err_null_ptrftl was NULL.
otherPropagated underlying-device erase error.
Precondition
ftl is initialised.
ftl->pblocks reflects the live mapping.
Postcondition
Every block that was STALE on entry is FREE on a successful return.
On an erase error the scan stops and the error is propagated.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 83 of file ra8_ftl.c.

References ra8_ftl_pblock_t::erase_count, k_ra8_ftl_one_block, k_ra8_ftl_pstate_free, k_ra8_ftl_pstate_stale, k_ra8_ok, ra8_ftl_t::pblocks, ra8_ftl_t::physical_blocks, RA8_CHECK_NULL_PTR, ra8_io_blockdev_erase(), ra8_ftl_t::raw, s_tag, and ra8_ftl_pblock_t::state.

Referenced by internal_alloc_blank().

◆ internal_reset_tables()

ra8_err_t internal_reset_tables ( ra8_ftl_t * ftl)
static

Cold-start the FTL bookkeeping tables.

Sets every logical map entry to k_ra8_ftl_unmapped and every physical block to FREE with a zero erase count. Both loops are statically bounded by their respective table sizes.

Parameters
[in,out]ftlHandle whose map/pblocks/sizes are already populated.
Returns
ra8_err_t Error code.
Return values
k_ra8_okTables initialised to the cold-start state.
k_ra8_err_null_ptrftl was NULL.
Precondition
ftl->map and ftl->pblocks point at correctly sized caller storage.
ftl->logical_blocks and ftl->physical_blocks are set.
Postcondition
Every logical block is unmapped.
Every physical block is FREE with erase_count == 0.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 656 of file ra8_ftl.c.

References ra8_ftl_pblock_t::erase_count, k_ra8_ftl_pstate_free, k_ra8_ftl_unmapped, k_ra8_ok, ra8_ftl_t::logical_blocks, ra8_ftl_t::map, ra8_ftl_t::pblocks, ra8_ftl_t::physical_blocks, RA8_CHECK_NULL_PTR, s_tag, and ra8_ftl_pblock_t::state.

Referenced by ra8_ftl_init().

◆ internal_validate_init_args()

ra8_err_t internal_validate_init_args ( const ra8_ftl_t * bd,
const ra8_io_blockdev_t * raw,
const uint16_t * map,
const ra8_ftl_pblock_t * pblocks,
const uint8_t * scratch,
uint32_t logical_blocks,
uint32_t physical_blocks )
static

Validate the pointer + sizing arguments to ra8_ftl_init.

Split out of ra8_ftl_init so that function stays within the clang-tidy statement/complexity budget. Rejects any null storage pointer, a zero logical-block count, and a physical-block count past the static k_ra8_ftl_max_pblocks ceiling.

Parameters
[in]bdFTL state to populate (non-NULL).
[in]rawBacking erase-before-write block device (non-NULL).
[in]mapLogical->physical map storage (non-NULL).
[in]pblocksPer-physical-block metadata storage (non-NULL).
[in]scratchCopy-on-write scratch buffer (non-NULL).
[in]logical_blocksLogical block count (> 0).
[in]physical_blocksPhysical block count (<= k_ra8_ftl_max_pblocks).
Returns
ra8_err_t Error code.
Return values
k_ra8_okAll arguments valid.
k_ra8_err_null_ptrA required pointer argument was NULL.
k_ra8_err_invalid_sizelogical_blocks is 0 or physical_blocks exceeds the ceiling.
Precondition
The caller passes ra8_ftl_init's arguments verbatim.
Storage arrays out-live the FTL when non-NULL.
Postcondition
On k_ra8_ok every checked argument is safe to record.
On any non-ok return no state is mutated.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 702 of file ra8_ftl.c.

References k_ra8_err_invalid_size, k_ra8_ftl_max_pblocks, k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by ra8_ftl_init().

◆ internal_write_one()

ra8_err_t internal_write_one ( ra8_ftl_t * ftl,
uint32_t lbn,
const uint8_t * src )
static

Write one logical block via copy-on-write relocation.

Allocates a blank physical block, programs src into it, then re-points the map and marks the previous physical block (if any) STALE for later reclamation. The old block is never overwritten, so the erase-before-write contract holds. On a program failure the freshly erased block is left FREE and the map is unchanged, so the prior data remains intact.

Parameters
[in,out]ftlInitialised FTL handle.
[in]lbnLogical block number (< logical_blocks).
[in]src512-byte source buffer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okLogical block now reflects src.
k_ra8_err_null_ptrftl or src was NULL.
k_ra8_err_no_memNo physical block available to relocate into.
otherPropagated underlying erase/program error.
Precondition
ftl is initialised and not read-only.
lbn < ftl->logical_blocks.
Postcondition
On success map[lbn] points at a LIVE block holding src.
On any non-ok return the prior mapping for lbn is preserved.
Note
Not thread-safe with respect to the same FTL.
Since
0.1.0

Definition at line 289 of file ra8_ftl.c.

References internal_alloc_blank(), k_ra8_ftl_one_block, k_ra8_ftl_pstate_live, k_ra8_ftl_pstate_stale, k_ra8_ftl_unmapped, k_ra8_ok, ra8_ftl_t::map, ra8_ftl_t::pblocks, RA8_CHECK_NULL_PTR, ra8_io_blockdev_write(), ra8_ftl_t::raw, s_tag, and ra8_ftl_pblock_t::state.

Referenced by internal_dev_write().

◆ ra8_ftl_as_blockdev()

ra8_err_t ra8_ftl_as_blockdev ( ra8_ftl_t * ftl,
ra8_io_blockdev_t * out )
nodiscard

Expose an initialised FTL as a free-overwrite ra8_io_blockdev_t.

Binds out to the FTL vtable with the ra8_ftl_t as its context, so the layers above (FAT, VFS, the write-back cache) see a normal block device that supports in-place overwrite, never needs an explicit erase, and reads unwritten blocks back as the underlying erase value. The handle passed as ftl must out-live the resulting block device.

Parameters
[in]ftlFTL handle previously initialised by ra8_ftl_init.
[out]outBlock-device handle to bind (zero-initialised by the caller).
Returns
ra8_err_t Error code.
Return values
k_ra8_okout bound to the FTL.
k_ra8_err_null_ptrftl or out was NULL.
k_ra8_err_not_initializedftl was not initialised (no raw device).
Precondition
ftl was initialised by ra8_ftl_init.
out is writable and out-lives every block-device call.
Postcondition
On success out dispatches every operation through the FTL.
On any non-ok return out is left unbound.
Note
Not thread-safe with respect to the same FTL.
See also
ra8_ftl_init
Since
0.1.0

Definition at line 757 of file ra8_ftl.c.

References ra8_io_blockdev_t::ctx, ra8_io_blockdev_t::iface, k_ra8_err_not_initialized, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_ftl_t::raw, s_ftl_iface, and s_tag.

Referenced by demo_reopen_naive(), and demo_wear_phase().

◆ ra8_ftl_init()

ra8_err_t ra8_ftl_init ( ra8_ftl_t * bd,
const ra8_io_blockdev_t * raw,
uint16_t * map,
uint32_t logical_blocks,
ra8_ftl_pblock_t * pblocks,
uint32_t physical_blocks,
uint8_t * scratch )
nodiscard

Initialise an FTL over an erase-before-write block device.

Validates the underlying device's capabilities (it must report erase_unit_blocks == 1, must not be read-only, and must have at least logical_blocks + k_ra8_ftl_min_spare physical blocks), records the caller-provided storage, snapshots the medium erase value, and sets every logical block to k_ra8_ftl_unmapped and every physical block to FREE. No erase or program is issued here; physical blocks are erased lazily on first write. No allocation occurs.

Parameters
[out]bdFTL handle to initialise; caller zero-initialises it before use.
[in]rawBound underlying erase-before-write device.
[out]mapCaller array of logical_blocks uint16_t.
[in]logical_blocksBlocks to present to FAT (>= 1).
[out]pblocksCaller array of physical_blocks metadata entries; caller zero-initialises it.
[in]physical_blocksPhysical blocks in raw (>= logical_blocks + k_ra8_ftl_min_spare).
[out]scratchCaller 512-byte copy scratch buffer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okFTL initialised and ready.
k_ra8_err_null_ptrAny pointer argument was NULL.
k_ra8_err_invalid_sizelogical_blocks was zero or physical_blocks exceeds k_ra8_ftl_max_pblocks.
k_ra8_err_invalid_argUnderlying caps query failed, the device is read-only, erase_unit_blocks != 1, or there is no spare block.
k_ra8_err_not_initializedNo backend is bound to raw.
Precondition
All pointer arguments out-live every call through the FTL.
pblocks[0 .. physical_blocks) were zero-initialised by the caller.
Postcondition
On success every logical block is unmapped and every physical block FREE.
On any non-ok return bd is left unbound.
Note
Not thread-safe with respect to the same FTL.
See also
ra8_ftl_as_blockdev
Since
0.1.0

Definition at line 724 of file ra8_ftl.c.

References ra8_ftl_t::erase_value, internal_check_caps(), internal_reset_tables(), internal_validate_init_args(), k_ra8_ok, ra8_ftl_t::logical_blocks, ra8_ftl_t::map, ra8_ftl_t::pblocks, ra8_ftl_t::physical_blocks, ra8_io_blockdev_get_caps(), ra8_ftl_t::raw, and ra8_ftl_t::scratch.

Referenced by demo_reopen_naive(), and demo_wear_phase().

◆ ra8_ftl_phys_of()

ra8_err_t ra8_ftl_phys_of ( const ra8_ftl_t * ftl,
uint32_t lbn,
uint16_t * phys_out )
nodiscard

Report the physical block currently backing a logical block.

Read-only mapping telemetry: resolves map[lbn] and returns the physical block index that presently holds the logical block's data, or k_ra8_ftl_unmapped if the logical block has never been written. Because a copy-on-write write re-points map[lbn] at a freshly allocated physical block, calling this after each overwrite exposes the wear-levelling relocation: the reported index migrates while the logical address stays fixed. Intended for demos, tests, and telemetry – not the data path.

Parameters
[in]ftlInitialised FTL handle.
[in]lbnLogical block number (< logical_blocks).
[out]phys_outReceives the physical block index, or k_ra8_ftl_unmapped when the block is unwritten.
Returns
ra8_err_t Error code.
Return values
k_ra8_ok*phys_out populated.
k_ra8_err_null_ptrftl or phys_out was NULL.
k_ra8_err_not_initializedftl was not initialised.
k_ra8_err_out_of_rangelbn >= logical_blocks.
Precondition
ftl was initialised by ra8_ftl_init.
phys_out is writable.
Postcondition
On success *phys_out is either k_ra8_ftl_unmapped or a valid physical index (< physical_blocks).
No FTL or device state is mutated.
Note
Not thread-safe with respect to the data path.
See also
ra8_ftl_wear_stats
Since
0.1.0

Definition at line 793 of file ra8_ftl.c.

References k_ra8_err_not_initialized, k_ra8_err_out_of_range, k_ra8_ok, ra8_ftl_t::logical_blocks, ra8_ftl_t::map, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by demo_restore(), and demo_write_verify().

◆ ra8_ftl_wear_stats()

ra8_err_t ra8_ftl_wear_stats ( const ra8_ftl_t * ftl,
uint32_t * max_out,
uint32_t * min_out )
nodiscard

Report the highest per-physical-block erase count seen so far.

Wear-levelling diagnostic: returns the maximum and minimum erase_count across all physical blocks. A healthy FTL keeps these close together; a large spread indicates a hot block. Intended for tests and telemetry, not the data path.

Parameters
[in]ftlInitialised FTL handle.
[out]max_outReceives the maximum erase count.
[out]min_outReceives the minimum erase count.
Returns
ra8_err_t Error code.
Return values
k_ra8_ok*max_out and *min_out populated.
k_ra8_err_null_ptrAny pointer argument was NULL.
k_ra8_err_not_initializedftl was not initialised.
Precondition
ftl was initialised by ra8_ftl_init.
max_out and min_out are writable.
Postcondition
On success *max_out >= *min_out.
No FTL or device state is mutated.
Note
Thread-safe with respect to the data path is NOT guaranteed.
Since
0.1.0

Definition at line 769 of file ra8_ftl.c.

References ra8_ftl_pblock_t::erase_count, k_ra8_err_not_initialized, k_ra8_ftl_count_max, k_ra8_ok, ra8_ftl_t::pblocks, ra8_ftl_t::physical_blocks, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by demo_report_wear().

Variable Documentation

◆ s_ftl_iface

const ra8_io_blockdev_iface_t s_ftl_iface
static
Initial value:
= {
.get_caps = internal_dev_get_caps,
}
static ra8_err_t internal_dev_write(void *ctx, uint32_t lba, uint32_t count, const uint8_t *buf)
Presented vtable: write count logical blocks at lba.
Definition ra8_ftl.c:428
static ra8_err_t internal_dev_read(void *ctx, uint32_t lba, uint32_t count, uint8_t *buf)
Presented vtable: read count logical blocks at lba.
Definition ra8_ftl.c:380
static ra8_err_t internal_dev_sync(void *ctx)
Presented vtable: flush the underlying device.
Definition ra8_ftl.c:557
static ra8_err_t internal_dev_get_caps(const void *ctx, ra8_io_blockdev_caps_t *out)
Presented vtable: report the FTL's free-overwrite capabilities.
Definition ra8_ftl.c:518
static ra8_err_t internal_dev_erase(void *ctx, uint32_t lba, uint32_t count)
Presented vtable: erase count logical blocks at lba.
Definition ra8_ftl.c:475

Presented FTL vtable – a clean free-overwrite block device.

Definition at line 566 of file ra8_ftl.c.

Referenced by ra8_ftl_as_blockdev().

◆ s_tag

const char* const s_tag = "ra8_ftl"
static

Module log tag.

Definition at line 34 of file ra8_ftl.c.