ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
wearlevel.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file wearlevel.h
6 * @brief Flash wear-leveling slot selector (PROTOCORE_ENABLE_WEARLEVEL).
7 *
8 * Flash/NVS cells wear out after a bounded number of erase cycles, so a device that repeatedly writes a
9 * record (a log line, a config snapshot, a counter) to the *same* location burns that block out early.
10 * This is the pure core of wear leveling: given a per-slot erase/write count, `protocore_wearlevel_pick`
11 * returns the least-worn slot to write next, so writes spread evenly and the whole region ages together.
12 *
13 * The app owns the actual slots (NVS keys, flash sectors, VFS files) and the persisted counts; this core
14 * just decides *where* the next write goes and reports the wear imbalance. Pure, zero heap, no stdlib,
15 * so it is fully host-testable. It composes with the mount (the storage medium) and server/logbuf
16 * (whose sink can offload to a wear-leveled store).
17 */
18
19#ifndef PROTOCORE_WEARLEVEL_H
20#define PROTOCORE_WEARLEVEL_H
21
22#include "protocore_config.h" // the entry point: protocore_types.h for the widths
23
24#if PROTOCORE_ENABLE_WEARLEVEL
25
27
28/** @brief The slot table a call reads, and the slot it names. */
29typedef struct
30{
31 const uint32_t *counts; ///< per-slot write/erase counts; the app persists these across boots
32 uint32_t *counts_rw; ///< the same table, where a mark writes to it
33 size_t n; ///< number of slots
34 size_t idx; ///< the slot a mark records a write to
35} WearArgs;
36
37/**
38 * @brief The wear-levelling policy over a caller-owned count table.
39 *
40 * A caller sets the members a call takes, invokes it through ::Wearlevel, and reads the outcome off
41 * the same handle. The table is the caller's; nothing is held here.
42 *
43 * @var WearlevelNs::args the slot table a call reads, and the slot it names
44 * @var WearlevelNs::n_out the slot a pick chose
45 * @var WearlevelNs::spread the imbalance a spread reports
46 * @var WearlevelNs::pick the least-worn slot to write next
47 * @var WearlevelNs::mark record a write to args.idx (saturating, so a count never wraps to 0)
48 * @var WearlevelNs::imbalance max count - min count across the slots (0 = perfectly level)
49 *
50 * pick round-robins naturally: after writing to the chosen slot the app bumps its count with mark,
51 * so the next pick moves on and the region wears uniformly. Ties resolve to the lowest index, and a
52 * null table or zero length picks 0. imbalance is a monotone health metric for a /health-style
53 * endpoint: it stays small under pick+mark and grows if the app writes off-policy.
54 *
55 * No storage member: every call works in the caller's table.
56 */
57typedef struct
58{
59 WearArgs args;
60 size_t n_out;
61 uint32_t spread;
62} WearlevelVars;
63
64/** @brief The operands and the outcome. */
65extern WearlevelVars WearlevelV;
66
67/** @brief The entries. */
68typedef struct
69{
70 void (*const pick)(uint8_t *work);
71 void (*const mark)(uint8_t *work);
72 void (*const imbalance)(uint8_t *work);
73} WearlevelNs;
74
75// What the table binds, defined once in the .c and taking one parameter each: everything
76// else an entry needs is an operand in WearlevelV or a region of the borrow at a fixed offset.
77void protocore_wearlevel_pick(uint8_t *work);
78void protocore_wearlevel_mark(uint8_t *work);
79void protocore_wearlevel_imbalance(uint8_t *work);
80
81// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
82// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
83// `Wearlevel.pick(work)` resolves to a named function and becomes a DIRECT call. An extern table
84// leaves the call indirect and the symbol live at every level, -O2 -flto included.
85static const WearlevelNs Wearlevel __attribute__((unused)) = {
86 .pick = protocore_wearlevel_pick,
87 .mark = protocore_wearlevel_mark,
88 .imbalance = protocore_wearlevel_imbalance,
89};
90
92
93#endif // PROTOCORE_ENABLE_WEARLEVEL
94
95#endif // PROTOCORE_WEARLEVEL_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
#define PROTOCORE_END_DECLS
Definition types.h:97