MMgr v1.0.0
Zero-heap memory manager in C11. One borrowed buffer, carved by asserted offset.
Loading...
Searching...
No Matches
praet_ordo.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_ordo.h
9 * @brief The schedule context: one flag word per channel, the two volatiles that coordinate the
10 * interrupt, and the reader/setter that is the only thing touching either.
11 * @author dstroy0 (Douglas Quigg) <dquigg123@gmail.com>
12 * @date 2026-09-01
13 *
14 * @note A context exists only where DMA has to schedule internally. A channel is not a context.
15 * @note Two separate concerns live here and must not be folded together. The quaternary core in the
16 * flag word is the channel's DMA state, where busy means the engine is moving bytes. The busy
17 * volatile below is the reader/setter's lock, and means it is mid-update.
18 * @note Time is microseconds throughout. DMA timing is tight enough that a millisecond is not a unit
19 * anything here can be expressed in.
20 */
21#ifndef MMGR_PRAET_ORDO_H
22#define MMGR_PRAET_ORDO_H
23
24#include "mmgr.h"
25
26// PRAET_CHANNELS, PRAET_SETTLE_MICROS, PRAET_KEEPALIVE_MICROS and PRAET_RECOVERY come from here. None
27// has a value that is right for every part. An unset one takes a default and raises a warning
28// naming itself. What stops the build is praet_iudex.h, below, once all of them have spoken
30
31// Every bit of the flag word, and the assertions that keep the map from drifting. Kept apart from the
32// entries because the map is one thing and it is the same on every build - a status a build never
33// sets still owns its bit
35
36// Where the microseconds come from, and what a tick is worth against them. Every deadline below is
37// microseconds, and so nothing here means anything without it
39
40// Last, and after every knob has reported. This is the single one place a configuration stops the build, so
41// that a build missing several knobs hears about all of them instead of the first
43
44// The examination arm's recorder. Expands to nothing unless a build asked for it, and the one
45// function that writes a flag word is what calls into it
47
48EMBED_BEGIN_DECLS
49
50#if PRAET_RECOVERY
51
52/**
53 * @brief Recovery that backs the transfer out and leaves the bytes as they are.
54 *
55 * @note What you want where the destination is about to be overwritten anyway, or where a partial
56 * result is still worth reading.
57 */
58#define PRAET_RESTITUERE_ET_REDINTEGRARE 0u
59
60/**
61 * @brief Recovery that treats the transfer's bytes as unsafe.
62 *
63 * @note What you want where a half-written buffer would be read as a whole one. Which of the two is
64 * right depends on where in a program the stall happened, and so it is the caller's call and not
65 * this library's.
66 */
67#define PRAET_RESTITUERE_ET_AD_NIHILUM_REDIGERE 1u
68
69#endif
70
71/**
72 * @brief The two answers a declaration may give about the boundary word check.
73 *
74 * @note Written at length deliberately. This is not a knob that rides along with recovery being on - it
75 * is its own deliberate yes or no, made once per context, and the name is meant to be
76 * impossible to skim past in a declaration.
77 * @note Real enumerators. A mistyped token is an undeclared identifier at the
78 * declaration instead of quietly reading as the off arm.
79 */
80typedef enum
81{
82 AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE = 0,
83 AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE = 1
85
86/**
87 * @brief Marks a declaration so that every use of it reports, carrying a message.
88 *
89 * @param[in] message_ Text the diagnostic reports, as a string literal.
90 * @note Aliased under this module's prefix and written once, as
91 * embed_compiler_directives.h asks a consumer to do with an attribute.
92 * @note The deprecated attribute, where the error and warning ones fall short. Those report at a call
93 * that survives compilation, and a declaration at file scope has no call in it - measured, both
94 * silent. This one reports at any use, and a typedef naming the type is a use.
95 * @note Nothing marked with this is deprecated. It is the only attribute that reports from a
96 * declarator, and what it is being used for is to make a deliberate answer visible. Nothing is
97 * retired. The message says which answer, and so the line reads correctly whatever GCC
98 * prefixes it with.
99 * @warning Expands to nothing where EMBED_HAS_ATTRIBUTE(deprecated) is 0. The answer is then silent
100 * and only a wrong token still fails, the half that cannot be missed.
101 */
102#if EMBED_HAS_ATTRIBUTE(deprecated)
103#define PRAET_DENUNTIATIO_ATTR(message_) __attribute__((deprecated(message_)))
104#else
105#define PRAET_DENUNTIATIO_ATTR(message_)
106#endif
107
108/**
109 * @brief A type that exists only for the AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE token.
110 *
111 * @note The declarator pastes the token onto this and typedefs the result. A token that is neither
112 * of the two fails on an unknown type name that has the offending token in it. Pasting onto a
113 * macro instead gave a syntax error several lines down that named nothing - measured, on the
114 * mistyped token this exists to catch.
115 * @note That same typedef makes the answer report. Both arms carry the attribute, because a
116 * choice that only speaks up one way trains everyone to read its silence as the safe answer,
117 * and neither answer here is safe by default.
118 */
120 "AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE - this context measures no boundary word. A "
121 "recovery is exact to the word and no "
122 "finer") unsigned char PraetCrcResponsum_AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE;
123
124/**
125 * @brief A type that exists only for the AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE token.
126 */
128 "AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE - this context checksums the word a stalled "
129 "transfer was inside, one word, bit at a time, on the recovery path "
130 "only") unsigned char PraetCrcResponsum_AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE;
131
132/**
133 * @brief Pastes @p token_ onto the answer type prefix.
134 *
135 * @param[in] token_ One of the two AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC tokens.
136 * @return The answer type for that token.
137 */
138#define PRAET_CRC_RESPONSUM_IN_LOCO_FIGERE(token_) PraetCrcResponsum_##token_
139
140/**
141 * @brief The answer type @p token_ names, after expanding it.
142 *
143 * @param[in] token_ One of the two AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC tokens, or a macro holding one.
144 * @return That token's answer type.
145 * @note This is what refuses a plain 1 or TRUE in the answer's place. Those are values, and the two
146 * tokens are the only things that name a type here.
147 */
148#define PRAET_CRC_RESPONSUM(token_) PRAET_CRC_RESPONSUM_IN_LOCO_FIGERE(token_)
149
150/**
151 * @brief The value the AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE token stands for.
152 *
153 * @note Pasted onto, and so the declarator gets the answer at preprocessing time and can
154 * assert on it.
155 */
156#define PRAET_CRC_VALUE_AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE 0
157
158/**
159 * @brief The value the AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE token stands for.
160 */
161#define PRAET_CRC_VALUE_AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE 1
162
163/**
164 * @brief Pastes @p token_ onto the value prefix.
165 *
166 * @param[in] token_ One of the two AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC tokens.
167 * @return The value macro for that token.
168 */
169#define PRAET_CRC_VALUE_IN_LOCO_FIGERE(token_) PRAET_CRC_VALUE_##token_
170
171/**
172 * @brief The value @p token_ stands for, after expanding it.
173 *
174 * @param[in] token_ One of the two AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC tokens, or a macro holding one.
175 * @return 1 for the ENABLE token, 0 for the DISABLE token.
176 * @note Usable in an #if, as lets a caller compile one thing or another around their own
177 * answer without keeping a second macro in step with it.
178 */
179#define PRAET_CRC_VALUE(token_) PRAET_CRC_VALUE_IN_LOCO_FIGERE(token_)
180
181#if PRAET_RECOVERY
182
183/**
184 * @brief Storage the boundary word check needs, where a declaration asked for it.
185 *
186 * @param crc_choice_ One of the two AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC tokens.
187 * @return An initializer for the members that carry the choice.
188 */
189#define PRAET_ORDINEM_INCIPE(crc_choice_) {.word_boundary_crc = PRAET_CRC_VALUE_##crc_choice_}
190
191#else
192
193/**
194 * @brief An empty context, for a build with no recovery machinery to configure.
195 *
196 * @param crc_choice_ One of the two AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC tokens, unused here.
197 * @return A zero initializer.
198 * @note The token is still demanded by the declarator and still reports. What changes is that there
199 * is no member to put it in, and the assertion there refuses the ENABLE arm outright.
200 */
201#define PRAET_ORDINEM_INCIPE(crc_choice_) {0}
202
203#endif
204
205/**
206 * @brief Declares one schedule context and answers the boundary word question for it.
207 *
208 * @param name_ Name of the context this declares.
209 * @param crc_choice_ AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE or
210 * AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_DISABLE.
211 * @note The instantiation is the declaration. This emits initialized data carrying the answer, and so
212 * nothing runs to make a context usable and the answer cannot be changed afterwards.
213 * @note Reports whichever token it was given. A caller who wanted a quieter build takes the message
214 * away by not declaring a context, the only honest way out of it.
215 * @warning A token that is neither of the two fails on the typedef, with the token it was given in
216 * the message. Asking for the check on a build with PRAET_RECOVERY off fails the assertion
217 * below, which names what to do about it.
218 */
219#define PraetOrdoContext(name_, crc_choice_) \
220 typedef PRAET_CRC_RESPONSUM(crc_choice_) name_##_boundary_word_answer; \
221 EMBED_STATIC_ASSERT( \
222 PRAET_RECOVERY || (PRAET_CRC_VALUE(crc_choice_) == 0), \
223 "AD_VERBI_CONFINIUM_RESTITUE_PAULATIM_CRC_ENABLE needs PRAET_RECOVERY set to 1, since the word it " \
224 "measures is the one a recovery is deciding about"); \
225 static PraetOrdo name_ = PRAET_ORDINEM_INCIPE(crc_choice_)
226
227/**
228 * @brief Everything one context schedules over.
229 *
230 * @param flags One uint32 per channel: quaternary core at the bottom, statuses above it,
231 * region descriptor in the byte at PRAET_REGION_SHIFT. Plain, because the
232 * two volatiles below are what coordinate against the interrupt. Fixed at
233 * 32 bits whatever embed_word is, because the layout is a fixed set of bits
234 * and an environment building at a 16-bit word has nowhere to put the
235 * region byte.
236 * @param bound First byte of the memory each attached channel moves, as the pool named
237 * at its attach [BORROWS]. Present only where PRAET_RECOVERY is on.
238 * @param bound_bytes How many bytes are at that address, as the pool's own extent said.
239 * Present only where PRAET_RECOVERY is on.
240 * @param start First byte of each running channel's transfer, which is bound plus the
241 * offset a submit named. The caller's storage, and the caller's to write -
242 * nothing here writes through it [BORROWS]. Present only where
243 * PRAET_RECOVERY is on.
244 * @param length Bytes each running channel was asked to move. Present only where
245 * PRAET_RECOVERY is on.
246 * @param position Bytes each running channel has moved, as of its last kick. Present only
247 * where PRAET_RECOVERY is on.
248 * @param boundary_crc Checksum of the word a recovery found the engine inside, where the
249 * declaration asked for the check. Meaningful only where the channel reads
250 * PRAET_MEASURED.
251 * @param word_boundary_crc What the declaration answered about the boundary word check. Written once
252 * by PraetOrdoContext and never again, as lets the one
253 * branch reading it fold away where the answer was no.
254 * @param keepalive_deadline Microsecond mark each running channel must be kicked before.
255 * @param settle_deadline Microsecond mark the engine finishes settling at. One timer, because
256 * settling is the engine coming up and not a property of a channel.
257 * @param praet_set_bitflag Raised by the interrupt, and by anything that changes what a service call
258 * would find. Volatile, and so it is not cached across the preemption.
259 * @param praet_busy_bitflag The reader/setter's lock. Volatile, and set while it updates, and that
260 * makes it ignore the interrupt for that span.
261 * @param elapsed_micros Microseconds elapsed, as the caller advances them.
262 * @note Both volatiles carry the module prefix. `set` and `busy` are words a caller is entitled to
263 * have taken for a macro of their own, and a macro over a member name breaks the struct at
264 * every use with a diagnostic that points nowhere near the cause.
265 * @warning The context sits at an address the linker resolves and is written by offset from it.
266 * Nothing computes where a member lives, and it cannot be relocated or copied - its address
267 * is its identity.
268 */
269typedef struct
270{
271 uint32_t flags[PRAET_CHANNELS];
272 embed_word keepalive_deadline[PRAET_CHANNELS];
273#if PRAET_RECOVERY
274 uint8_t *bound[PRAET_CHANNELS];
275 embed_word bound_bytes[PRAET_CHANNELS];
276 uint8_t *start[PRAET_CHANNELS];
277 embed_word length[PRAET_CHANNELS];
278 embed_word position[PRAET_CHANNELS];
279 uint32_t boundary_crc[PRAET_CHANNELS];
280 embed_bool word_boundary_crc;
281#endif
282 embed_word settle_deadline;
283 volatile embed_word praet_set_bitflag;
284 volatile embed_word praet_busy_bitflag;
285 embed_word elapsed_micros;
286} PraetOrdo;
287
288/**
289 * @brief Returns how far the port says @p channel has got, in bytes.
290 *
291 * @param[in] channel Channel to read.
292 * @return Bytes moved so far. Zero from a port with nothing to say.
293 * @note The fifth port hook, and the one the four in memoriam_praetereo.h do not have. Every
294 * controller this library targets exposes a remaining count, and the orchestrator needs it to
295 * feed a watchdog with something a caller can act on.
296 * @note Reached only from praet_ordo_poll. Nothing above it sees a byte count that has not been
297 * through the flag word first, as keeps how progress is tracked out of a caller's
298 * business.
299 * @note Carries a weak refusing default, the same as the four hooks in memoriam_praetereo.c. It
300 * returns zero, and praet_ordo_take_progress treats zero as no movement. A build that supplies
301 * no port walks every channel and still marks a stalled one, because an unkicked keepalive
302 * window is what marks a channel stopped.
303 * @note The default sits in praet_ordo.c, the unit that calls it. GCC on PE-COFF emits a weak
304 * definition as a weak external that ld does not resolve from another unit, and a weak
305 * definition beside its call resolves on every linker this library is built with.
306 */
307uint16_t praet_hw_progress(embed_word channel);
308
309/**
310 * @brief Clears a context back to every channel detached.
311 *
312 * @param[out] context Context to clear [BORROWS].
313 * @note A declaration emits this as initialized data. The entry exists so one context can be reused.
314 * @warning Leaves the boundary word answer alone. That came from the declaration and is not state a
315 * reset has any business changing.
316 */
318
319/**
320 * @brief Attaches a channel over a block of bytes, claims its vector, and starts the settle timer.
321 *
322 * @param[in,out] context Context the channel belongs to [BORROWS].
323 * @param[in] channel Channel to attach.
324 * @param[in] bound First byte of the memory this channel moves. The caller's [BORROWS].
325 * @param[in] bytes How many bytes are there.
326 * @param[in] region Region descriptor for this channel, as a byte.
327 * @return EMBED_TRUE where the channel was detached and is now attached.
328 * @note Attach and detach are the only entry and exit, and configuration takes no part in either.
329 * How long the engine takes to come up is a property of the part, and so it arrives as
330 * PRAET_SETTLE_MICROS at compile time and not as an argument here.
331 * @note Reach this through PraetAttach, which takes the pool by name. Calling it directly means
332 * stating an address and an extent separately, and nothing here can tell whether the two go
333 * together.
334 * @note Attaching does not claim the pool. A cellblock and a ring both write their own records into
335 * the bytes they dress, and so MMGR_PARS_CLAIMED_ONCE exists to keep two of those off one pool.
336 * This writes no records into the bytes, and an engine filling ring segments while a consumer
337 * drains them is the arrangement the module is for.
338 * @warning Fails closed. A channel that is not detached is left exactly as it was.
339 * @warning Two channels may be attached over one block, and nothing here reports it. Which of them
340 * runs when is the caller's plan, and this library does not have it.
341 */
342embed_bool praet_ordo_adnectere(PraetOrdo *context, embed_word channel, uint8_t *bound, embed_word bytes,
343 embed_word region);
344
345/**
346 * @brief Attaches a channel over a declared pool, by name.
347 *
348 * @param context_ Context the channel belongs to.
349 * @param channel_ Channel to attach.
350 * @param pool_ Pool declared by ParsMemoriaeInternae or ParsMemoriaeExternum.
351 * @param region_ Region descriptor for this channel, as a byte.
352 * @return What praet_ordo_adnectere answered.
353 * @note The attach surface on a buffer, and how a caller is meant to reach a channel. Naming the pool
354 * makes the address and the extent come from one place: mmgr_pars_storage_##pool_ and
355 * pool_##_bytes are both emitted by the declaration, and neither exists for anything that was
356 * not declared as a pool.
357 * @note Not a question of who owns the bytes. What is being tested is whether whoever hands them over
358 * has every answer about them: the address, the extent, and which channel is over them. A
359 * caller holding all three has said everything this needs, and where the bytes came from is
360 * their business.
361 * @note Both names have internal linkage. A translation unit that did not declare the pool cannot
362 * reach either and does not compile. That leaves the answers first hand, and a caller
363 * giving all of them makes these bytes legal to touch. The caller said so.
364 * @warning The region descriptor is still stated here. Which memory a pool sits in was settled by
365 * which of the two declarations wrote it, and neither emits a marker this could read, and so
366 * deriving it needs something added where the pool is declared.
367 */
368#define PraetAttach(context_, channel_, pool_, region_) \
369 (PRAET_ALVEUS_SUPERARE(context_, channel_, pool_), \
370 praet_ordo_adnectere(&(context_), (channel_), mmgr_pars_storage_##pool_, (embed_word)pool_##_bytes, \
371 (embed_word)(region_)))
372
373/**
374 * @brief Declares which pool a channel of a context is over.
375 *
376 * @param context_ Context the channel belongs to.
377 * @param channel_ Channel number, as a literal, since it is pasted into the symbol this emits.
378 * @param pool_ Pool declared by ParsMemoriaeInternae or ParsMemoriaeExternum.
379 * @note Written once per channel, at file scope, beside the context's own declaration. It emits an
380 * enumerator whose name carries the context, the channel and the pool, and that name is the
381 * binding: PraetAttach and PraetSubmit both name it, and so reaching either with a pool the channel
382 * was not declared over is an undeclared identifier that prints the triple that was written.
383 * @note The same shape locus_carcerum uses. MMGR_CARCER_BODY pastes the site and the pool into
384 * prisonsite_##_##name_##_ctx and MMGR_CARCER_MEM makes the pool the member's name. A
385 * cellblock's entries cannot be handed another cellblock's bytes. Here the channel joins the
386 * paste, because a context has several and they may be over different pools.
387 * @note Costs nothing. An enumerator emits no storage and is settled while the unit compiles.
388 * @warning Channel numbers are settled at compile time, as lets this paste one. A channel
389 * chosen at run time cannot be bound this way and reaches praet_ordo_adnectere directly,
390 * where nothing relates the address to the extent.
391 */
392#define PraetChannel(context_, channel_, pool_) \
393 EMBED_STATIC_ASSERT((channel_) < PRAET_CHANNELS, \
394 #context_ " has no channel " #channel_ ", because PRAET_CHANNELS says how many it carries"); \
395 EMBED_STATIC_ASSERT(sizeof(mmgr_pars_storage_##pool_) > 0u, \
396 #pool_ " has no bytes for " #context_ " channel " #channel_ " to move"); \
397 enum \
398 { \
399 context_##_channel##channel_##_is_over_##pool_ = 1 \
400 }
401
402/**
403 * @brief Names the binding a channel was declared with, and fails where there is none.
404 *
405 * @param context_ Context the channel belongs to.
406 * @param channel_ Channel number, as a literal.
407 * @param pool_ Pool the caller believes the channel is over.
408 * @note Reads the enumerator PraetChannel emitted. Nothing is computed from it, and the reference is
409 * the purpose: a triple nobody declared names an identifier that does not exist.
410 */
411#define PRAET_ALVEUS_SUPERARE(context_, channel_, pool_) ((void)context_##_channel##channel_##_is_over_##pool_)
412
413/**
414 * @brief Asks for a channel to be detached.
415 *
416 * @param[in,out] context Context the channel belongs to [BORROWS].
417 * @param[in] channel Channel to detach.
418 * @note Writes the request and reports nothing. The caller reads the result out of the flag word,
419 * as gives a void teardown something to report through.
420 * @note A second request against a channel already detaching changes nothing. Hardware teardown
421 * writes the disable and polls the busy bit, and so calling again is the normal path.
422 */
423void praet_ordo_separare(PraetOrdo *context, embed_word channel);
424
425#if PRAET_RECOVERY
426
427/**
428 * @brief Marks a channel busy over @p length bytes at @p offset into what it was attached over.
429 *
430 * @param[in,out] context Context the channel belongs to [BORROWS].
431 * @param[in] channel Channel to run on.
432 * @param[in] offset Bytes into the attached memory the transfer starts at.
433 * @param[in] length Bytes it was asked to move.
434 * @return EMBED_TRUE where the channel took it.
435 * @note Takes no pointer. The address came from the pool named at the attach. An offset and a
436 * length are all a transfer adds. Handing an address here would let a caller
437 * state one that has nothing to do with what the channel is attached over.
438 * @note Start, length and position are what make a backout possible. Without the three, a stalled
439 * transfer leaves a buffer nobody can say anything about, and so this is the form
440 * PRAET_RECOVERY selects and why it is not optional once that is on.
441 * @note Takes no duration. How long a transfer runs is not something this library can know, and so
442 * nothing here predicts it - the port reports completion when it happens.
443 * @warning Refuses a channel that is settling, already busy, detaching, or not attached. Fails
444 * closed: a refused submit changes no state.
445 * @warning Does not test the offset and the length against the attached extent. That is settled
446 * before anything runs, by PraetSubmit, which has the pool's own extent to compare against.
447 * Reaching this directly with numbers nobody checked is how a transfer runs off the end.
448 */
449embed_bool praet_ordo_relatio(PraetOrdo *context, embed_word channel, embed_word offset, embed_word length);
450
451/**
452 * @brief Fails the build when a span runs past the pool it names.
453 *
454 * @param pool_ Pool declared by ParsMemoriaeInternae or ParsMemoriaeExternum.
455 * @param offset_ Bytes into that pool the span starts at.
456 * @param length_ Bytes in the span.
457 * @note The bound, and it is settled before anything runs. A span that does not fit gives a negative
458 * bitfield width, and the member's name is what the compiler prints, the same way
459 * MMGR_PARS_CLAIMED_ONCE says what it means.
460 * @note Usable in an expression, which a static assertion is not. That lets the check sit in
461 * front of a call whose answer the caller reads.
462 * @note Measured at -O0 and at -O2: a span that fits reports nothing at either, and one that does not
463 * fails at both. Nothing here needs the optimizer, unlike a check riding on a call that has to
464 * be folded away.
465 * @warning Reads pool_##_bytes, and so it holds for a span whose offset and length are known while
466 * compiling. A length computed at run time is not bounded by this and is not bounded
467 * anywhere else either.
468 */
469#define PRAET_SPAN_FITS(pool_, offset_, length_) \
470 ((void)sizeof(struct { \
471 int this_span_runs_past_the_pool_the_channel_was_attached_over \
472 : (((offset_) + (length_)) <= sizeof(mmgr_pars_storage_##pool_)) \
473 ? 1 \
474 : -1; \
475 }))
476
477/**
478 * @brief Feeds the watchdog for one channel and records how far it has got.
479 *
480 * @param[in,out] context Context the channel belongs to [BORROWS].
481 * @param[in] channel Channel that showed a sign of life.
482 * @param[in] position Bytes moved so far.
483 * @note What the port calls when the engine has moved. A sign of life and how far it got are the
484 * same event, and so one call carries both.
485 * @note Pushes the keepalive window out and clears a stall that had been recorded, because a channel
486 * that is moving again is not stalled.
487 * @note Says nothing about the transfer being finished. praet_ordo_completed reports that.
488 */
489void praet_ordo_efficere(PraetOrdo *context, embed_word channel, embed_word position);
490
491/**
492 * @brief Returns how far a channel's transfer has got.
493 *
494 * @param[in] context Context the channel belongs to [BORROWS].
495 * @param[in] channel Channel to read.
496 * @return Bytes moved so far.
497 * @note With the start and length the caller submitted, this is the whole picture: the bytes at
498 * [start, start + position) were written and the rest were not. A caller needs exactly this to
499 * back a stalled transfer out, or to zero exactly what was touched instead of the whole buffer.
500 */
501embed_word praet_ordo_situs(const PraetOrdo *context, embed_word channel);
502
503/**
504 * @brief Returns the bytes a stalled transfer may have written, rounded up to a whole word.
505 *
506 * @param[in] context Context the channel belongs to [BORROWS].
507 * @param[in] channel Channel to read.
508 * @return Bytes at start that must be treated as written, never above the length.
509 * @note The position is sampled every few poll ticks, and so it is exact to word granularity and can be
510 * up to one word behind what the engine actually wrote. A caller zeroing only as far as the
511 * position would leave a partial word unscrubbed, and so the rounding lives here instead of in
512 * every caller that has to remember it.
513 * @note This is the number to scrub. praet_ordo_situs is what was sampled.
514 */
515embed_word praet_ordo_commotus_est(const PraetOrdo *context, embed_word channel);
516
517/**
518 * @brief Returns the checksum of the word a recovery found the engine inside.
519 *
520 * @param[in] context Context the channel belongs to [BORROWS].
521 * @param[in] channel Channel to read.
522 * @return The checksum, where the channel reads PRAET_MEASURED. Zero otherwise.
523 * @note Read PRAET_MEASURED first. Zero is a legal checksum, and so the value alone does not say whether
524 * a word was measured.
525 * @note The caller makes this useful. This library holds the destination and never the
526 * source, and so it can say what the boundary word now contains and not whether that matches what was
527 * meant to be there. Comparing the two is the caller's, the only place both are known.
528 */
529uint32_t praet_ordo_boundary_crc(const PraetOrdo *context, embed_word channel);
530
531#else
532
533/**
534 * @brief Marks a channel busy and starts its keepalive window.
535 *
536 * @param[in,out] context Context the channel belongs to [BORROWS].
537 * @param[in] channel Channel to run on.
538 * @return EMBED_TRUE where the channel took it.
539 * @note Takes no bytes. Where PRAET_RECOVERY is off nothing records what a transfer was pointed at.
540 * A start and a length would be stored and never read.
541 * @warning Refuses a channel that is settling, already busy, detaching, or not attached. Fails
542 * closed: a refused submit changes no state.
543 */
544embed_bool praet_ordo_relatio(PraetOrdo *context, embed_word channel);
545
546/**
547 * @brief Fails the build when a span runs past the pool it names.
548 *
549 * @param pool_ Pool declared by ParsMemoriaeInternae or ParsMemoriaeExternum.
550 * @param offset_ Bytes into that pool the span starts at.
551 * @param length_ Bytes in the span.
552 * @note The same check as on the other arm, and present here for the same reason it is there. A build
553 * with no recovery records nothing about a transfer, and a span that runs off the end of the
554 * pool is still a span that runs off the end of the pool.
555 */
556#define PRAET_SPAN_FITS(pool_, offset_, length_) \
557 ((void)sizeof(struct { \
558 int this_span_runs_past_the_pool_the_channel_was_attached_over \
559 : (((offset_) + (length_)) <= sizeof(mmgr_pars_storage_##pool_)) \
560 ? 1 \
561 : -1; \
562 }))
563
564/**
565 * @brief Feeds the watchdog for one channel.
566 *
567 * @param[in,out] context Context the channel belongs to [BORROWS].
568 * @param[in] channel Channel that showed a sign of life.
569 * @note A sign of life, alone. How far the engine got is what PRAET_RECOVERY buys, and this
570 * build does not carry it.
571 * @note Pushes the keepalive window out and clears a stall that had been recorded, because a channel
572 * that is moving again is not stalled.
573 */
574void praet_ordo_efficere(PraetOrdo *context, embed_word channel);
575
576#endif
577
578/**
579 * @brief Submits a span of a declared pool on a channel attached over that pool.
580 *
581 * @param context_ Context the channel belongs to.
582 * @param channel_ Channel to run on.
583 * @param pool_ Pool declared by ParsMemoriaeInternae or ParsMemoriaeExternum.
584 * @param offset_ Bytes into that pool the transfer starts at.
585 * @param length_ Bytes it moves.
586 * @return What praet_ordo_relatio answered.
587 * @note The submit surface on a buffer, and the counterpart of PraetAttach. Naming the pool again
588 * gives the bound something to compare against, since the extent lives with the
589 * declaration and not with the channel.
590 * @note The bound is checked here and never inside the entry. Sizes and spans are settled while
591 * compiling. A test at run time would be paying for a question that was already answered.
592 * @note Names the binding PraetChannel declared, and so submitting a pool this channel is not over fails
593 * on an identifier carrying the context, the channel and the pool that was written. That is the
594 * same guarantee locus_carcerum gets from pasting the site and the pool into one symbol.
595 */
596#if PRAET_RECOVERY
597#define PraetSubmit(context_, channel_, pool_, offset_, length_) \
598 (PRAET_ALVEUS_SUPERARE(context_, channel_, pool_), PRAET_SPAN_FITS(pool_, offset_, length_), \
599 praet_ordo_relatio(&(context_), (channel_), (embed_word)(offset_), (embed_word)(length_)))
600#else
601#define PraetSubmit(context_, channel_, pool_, offset_, length_) \
602 (PRAET_ALVEUS_SUPERARE(context_, channel_, pool_), PRAET_SPAN_FITS(pool_, offset_, length_), \
603 praet_ordo_relatio(&(context_), (channel_)))
604#endif
605
606/**
607 * @brief Reports that a channel's transfer finished.
608 *
609 * @param[in,out] context Context the channel belongs to [BORROWS].
610 * @param[in] channel Channel that finished.
611 * @param[in] failed EMBED_TRUE where it ended in an error, EMBED_FALSE on a completion.
612 * @note Completion arrives from the port and never from a timer. A timer deciding a transfer had
613 * finished would be this library guessing at hardware.
614 */
615void praet_ordo_completed(PraetOrdo *context, embed_word channel, embed_bool failed);
616
617#if PRAET_RECOVERY
618
619/**
620 * @brief Resolves a stalled channel by the recovery the caller picks.
621 *
622 * @param[in,out] context Context the channel belongs to [BORROWS].
623 * @param[in] channel Channel to resolve.
624 * @param[in] recovery PRAET_RESTITUERE_ET_REDINTEGRARE or PRAET_RESTITUERE_ET_AD_NIHILUM_REDIGERE.
625 * @return EMBED_TRUE where a stalled channel was resolved.
626 * @note Returns the channel to attached and records which recovery was taken. A later reader can
627 * tell a backed-out transfer from one whose bytes were treated as unsafe.
628 * @note Nothing here writes the caller's storage. This context holds no pointer to it. A caller
629 * choosing PRAET_RESTITUERE_ET_AD_NIHILUM_REDIGERE zeroes its own bytes and this records that it did.
630 * @warning Refuses a channel that is not stalled. Fails closed: state is only ever unknown after the
631 * watchdog said so, and resolving a healthy channel would discard a live transfer.
632 */
633embed_bool praet_ordo_resolve(PraetOrdo *context, embed_word channel, embed_word recovery);
634
635#endif
636
637/**
638 * @brief Advances the clock by @p micros.
639 *
640 * @param[in,out] context Context to advance [BORROWS].
641 * @param[in] micros Microseconds to add.
642 * @note Raises set, because time passing is one of the things that changes what a service call would
643 * find - a keepalive window can elapse without anything else happening.
644 */
645void praet_ordo_advance(PraetOrdo *context, embed_word micros);
646
647/**
648 * @brief Advances the clock by @p ticks, scaled against the declared frequency.
649 *
650 * @param[in,out] context Context to advance [BORROWS].
651 * @param[in] ticks Ticks read off the clock this build declared.
652 * @note What a port calls, because a counter reads in ticks and every deadline here is microseconds.
653 * The scaling is one compile-time constant. A build whose clock runs at a whole megahertz
654 * pays a multiply and a shift for it.
655 * @warning Ticks short of a whole microsecond are dropped, and nothing carries them over. A port
656 * feeding this one tick at a time on a fast clock never advances anything, and for that reason a
657 * port reads the counter and passes the difference instead of counting calls.
658 */
659void praet_ordo_advance_ticks(PraetOrdo *context, embed_word ticks);
660
661/**
662 * @brief Stands in for the interrupt raising the set volatile.
663 *
664 * @param[in,out] context Context the interrupt belongs to [BORROWS].
665 * @note All an interrupt does. It may run any number of times and the raises collapse, because there
666 * is exactly one reader and setting a raised flag is a no-op. Nothing is counted and nothing is
667 * queued.
668 */
670
671/**
672 * @brief Drives the port and brings every flag word up to date. The caller's poll.
673 *
674 * @param[in,out] context Context to poll [BORROWS].
675 * @note What a caller calls, and the only thing they have to. Everything a channel does between an
676 * attach and a completion happens here: the port is asked how far each running channel has
677 * got, the watchdog is fed with the answer, a settle that has elapsed is cleared, a channel
678 * nothing reported on is marked stalled, and a detach whose transfer has finished completes.
679 * @note Reports nothing. What a caller wants is in the flag word, which this leaves current, and one
680 * load reads it. A return value here would be a second way to learn the same thing, and the two
681 * would eventually disagree.
682 * @note Also the reader/setter, and the first call on interrupt exit. It reads set, takes busy, does
683 * the above, then unsets both volatiles. Taking busy makes it ignore the interrupt for
684 * that span, and the interrupt may keep raising set throughout without anything being lost,
685 * because this recomputes every channel from what it can see and consumes no event.
686 * @note Does nothing where set was not raised, and where busy was already set. A nested call while
687 * the lock is out is the re-entry case, and it declines, leaving the outer call's half-done
688 * state untouched.
689 * @note Every state change a channel undergoes goes through here, as puts all of the
690 * access control in one function. That fell out of the lock: one reader means one place.
691 */
693
694/**
695 * @brief Returns one channel's flag word.
696 *
697 * @param[in] context Context the channel belongs to [BORROWS].
698 * @param[in] channel Channel to read.
699 * @return The whole word: quaternary core at the bottom, statuses and region above.
700 * @note One load. Knowing a channel is idle does not require waiting for a callback.
701 */
702uint32_t praet_ordo_flags(const PraetOrdo *context, embed_word channel);
703
704EMBED_END_DECLS
705
706#endif
The embedded_types headers every module builds on.
Where the microseconds come from: the caller's clock, or one of ours pinned to a core they name,...
Read after every knob has reported, and the single place a configuration stops the build.
void praet_ordo_efficere(PraetOrdo *context, embed_word channel)
Feeds the watchdog for one channel.
embed_bool praet_ordo_relatio(PraetOrdo *context, embed_word channel)
Marks a channel busy and starts its keepalive window.
void praet_ordo_separare(PraetOrdo *context, embed_word channel)
Asks for a channel to be detached.
#define PRAET_DENUNTIATIO_ATTR(message_)
Marks a declaration so that every use of it reports, carrying a message.
Definition praet_ordo.h:105
void praet_ordo_advance_ticks(PraetOrdo *context, embed_word ticks)
Advances the clock by ticks, scaled against the declared frequency.
uint16_t praet_hw_progress(embed_word channel)
Returns how far the port says channel has got, in bytes.
PraetRecoveryWordBoundaryBitwiseCrc
The two answers a declaration may give about the boundary word check.
Definition praet_ordo.h:81
void praet_ordo_advance(PraetOrdo *context, embed_word micros)
Advances the clock by micros.
void praet_ordo_poll(PraetOrdo *context)
Drives the port and brings every flag word up to date. The caller's poll.
uint32_t praet_ordo_flags(const PraetOrdo *context, embed_word channel)
Returns one channel's flag word.
void praet_ordo_reset(PraetOrdo *context)
Clears a context back to every channel detached.
void praet_ordo_raise(PraetOrdo *context)
Stands in for the interrupt raising the set volatile.
embed_bool praet_ordo_adnectere(PraetOrdo *context, embed_word channel, uint8_t *bound, embed_word bytes, embed_word region)
Attaches a channel over a block of bytes, claims its vector, and starts the settle timer.
void praet_ordo_completed(PraetOrdo *context, embed_word channel, embed_bool failed)
Reports that a channel's transfer finished.
Every knob this module reads, what it takes when the caller did not set it, and the warning that says...
#define PRAET_CHANNELS
Logical channels one context carries.
Which states and transitions a run actually reached.
Every bit of the channel flag word, and the assertions that keep the map from drifting.
Everything one context schedules over.
Definition praet_ordo.h:270