MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
praet_praefinitum.h
Go to the documentation of this file.
1/* MMgr - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2 * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Commercial OR LicenseRef-Educational
3 *
4 * Every use falls under AGPL-3.0-or-later unless you hold explicit permission, which is either a
5 * negotiated commercial licensing contract or an educator's license issued to you personally.
6 */
7/**
8 * @file praet_praefinitum.h
9 * @brief Every knob this module reads, what it takes when the caller did not set it, and the warning
10 * that says so.
11 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
12 * @date 2026-09-01
13 *
14 * @note A knob that quietly takes a default is how a build breaks in a way nobody traces back. Three
15 * knobs get set, two do not, the part misbehaves, and the compiler said nothing.
16 * @note Nothing here stops the build. Every unset knob takes its default, raises a warning naming
17 * itself and the value it took, and leaves a flag behind. praet_iudex.h reads those
18 * flags at the end and stops once, listing what to go fix.
19 * @note That order is essential. An #error halts its translation unit where it stands. A
20 * build missing four knobs reports one, gets fixed, and reports the next - four rounds to learn
21 * four facts that were all known on the first. Warning here and stopping at the end hands the
22 * whole basket over at once.
23 * @note Nothing about it needs a build system. It is preprocessor directives. A bare gcc or clang
24 * on one file behaves the same way as any tree that drives them. A keep-going build reaches
25 * across translation units on top of this, which is a separate half of the same idea and not
26 * something this depends on.
27 * @note #warning is a GNU and clang directive and is standard from C23. This library targets those
28 * two compilers and no others, and so it is available everywhere this builds.
29 * @note Every message is a #warning directive, with no macro over _Pragma. GCC's warning pragma
30 * keeps only the first string literal after it. A message written as adjacent literals
31 * arrives cut off at the first one - which still reads as a finished sentence and still passes a
32 * check written against its opening words. That got past a green sweep column here once. A
33 * #warning takes the rest of the line and arrives whole.
34 * @warning A directive cannot come out of a macro expansion, and so this only works where the report sits
35 * at file scope. Every knob here does. The one report that fires from inside a macro is the
36 * boundary word answer at a declaration, and praet_ordo.h reaches that a different way.
37 * @warning No message interpolates a macro. Neither #warning nor the pragma expands one. A name in
38 * the middle of a message arrives as the name of the macro, and its value never appears.
39 */
40#ifndef MMGR_PRAET_PRAEFINITUM_H
41#define MMGR_PRAET_PRAEFINITUM_H
42
43/**
44 * @brief Logical channels one context carries.
45 *
46 * @note Per part, and the caller is the single one who knows which part. There is no number here that is
47 * right for everyone.
48 */
49#ifndef PRAET_CHANNELS
50#define PRAET_CHANNELS 8u
51#define PRAET_UNSET_CHANNELS 1
52#warning \
53 "PRAET_CHANNELS was not set and took the library default of 8. Set it to the channels your part gives one engine."
54#endif
55
56/**
57 * @brief Microseconds the engine spends settling before it will take a transfer.
58 *
59 * @note Microseconds and not milliseconds. DMA timing is tight enough that a millisecond is not a unit
60 * anything here can be expressed in.
61 * @warning At EMBED_WORD_BITS=16 a microsecond count wraps at 65535, about 65 milliseconds. No
62 * engine takes that long to come up, and so this knob is safe at every width.
63 */
64#ifndef PRAET_SETTLE_MICROS
65#define PRAET_SETTLE_MICROS 0u
66#define PRAET_UNSET_SETTLE_MICROS 1
67#warning \
68 "PRAET_SETTLE_MICROS was not set and took the library default of 0, so this build waits for nothing after an attach. Set it to what your engine takes to come up."
69#endif
70
71/**
72 * @brief Microseconds a running channel may go without the port kicking it before it reads stalled.
73 *
74 * @note A watchdog window and not a transfer length. The port kicks this whenever the engine shows a
75 * sign of life, and a window that goes unkicked says the channel stopped moving - it never
76 * says the transfer finished. Completion arrives from the port, because how long a transfer
77 * takes is not something this library can know.
78 * @note That is also why the width is not a problem. A watchdog window is short deliberately, and so the
79 * 65 millisecond ceiling a 16-bit word puts on a microsecond count is far above any value
80 * worth setting here, while a transfer's runtime has no bound at all.
81 */
82#ifndef PRAET_KEEPALIVE_MICROS
83#define PRAET_KEEPALIVE_MICROS 1000u
84#define PRAET_UNSET_KEEPALIVE_MICROS 1
85#warning \
86 "PRAET_KEEPALIVE_MICROS was not set and took the library default of 1000. Set it to how long a moving channel may go unkicked on your part before it has stopped."
87#endif
88
89/**
90 * @brief Whether a stalled transfer can be backed out or scrubbed.
91 *
92 * @note The capability switch. Off is what a caller gets by not answering, because recovery costs
93 * three arrays per channel and a caller who never said where the bytes were cannot back
94 * anything out of them.
95 * @note With it on, submit takes the start and the length, kick reports how far the engine got, and
96 * praet_ordo_resolve exists. With it off none of those members are in the context and none
97 * of those entries are declared. A call site that wanted them fails to compile instead of
98 * linking against a version that records nothing.
99 */
100#ifndef PRAET_RECOVERY
101#define PRAET_RECOVERY 0
102#define PRAET_UNSET_RECOVERY 1
103#warning \
104 "PRAET_RECOVERY was not set and took the library default of 0. A stalled transfer cannot be backed out or scrubbed in this build. Set it to 1 to have that machinery, 0 to say you meant to leave it out."
105#endif
106
107// A capability switch is on or off, and a third value is somebody reading it as a count or a channel
108// number. Every test on it below would take the on arm silently. This one stops where it stands
109// and stays out of the basket, because every gate after it would be answering a question about a
110// value nobody can read
111#if (PRAET_RECOVERY != 0) && (PRAET_RECOVERY != 1)
112#error "PRAET_RECOVERY must be 0 or 1. It is a capability switch, not a count."
113#endif
114
115// The boundary word check is not a build knob and has no default. It is answered once per context, in
116// the declaration, by a token nobody writes by accident. See PraetOrdoContext in
117// praet_ordo.h. A -D that sets it here reads as a build-wide switch, which it is not
118#ifdef PRAET_RECOVERY_CRC
119#error \
120 "PRAET_RECOVERY_CRC is not a build knob. The boundary word check is answered per context, in the declaration: PraetOrdoContext(name, AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE) or the DISABLE token."
121#endif
122
123#endif