diff --git a/dev/tasks.md b/dev/tasks.md index 0d23f32..8142c16 100644 --- a/dev/tasks.md +++ b/dev/tasks.md @@ -5,35 +5,36 @@ rule 20). ## Open -- [ ] decide whether the millisecond clock stays 64 bits. **Measured: 608 B of - flash, 7.6 % of the image**, plus 16 B of RAM - `uptime::millis()` and the - three timestamps that hold its value narrowed from `uint64_t` to - `uint32_t`, 8004 B down to 7396. +Nothing. - It is not free, and the price is not only the display. A 32-bit - millisecond counter wraps every **49.7 days**, and this board runs - continuously, so two things follow. The `uptime` command would restart - from zero at each wrap. And the interval tests would have to be rewritten - as subtractions - `now - last >= interval` is correct across a wrap where - `now >= last + interval` is not, and `terminal.hpp`'s monitor tick is - written the second way today. That shape is *why* the counter is 64 bits: - it puts the wrap out of reach so a naive comparison cannot be wrong. +## Decided against - So the question is whether a wrapping uptime display is acceptable, and - the answer decides 608 B. If it is, the three call sites in - `statistics.hpp` and `terminal.hpp` move to subtraction in the same - change, and that is the whole of the work. +Both of these are measured, and both are recorded here rather than deleted so +that the next person to measure them does not read a number as an opportunity +and re-propose work the owner has already refused. -- [ ] the 1 kHz tick's own cost, which is separate and smaller. The compare - handler increments a 64-bit counter, so it calls libgcc's `__adddi3_s8`, - and a call inside a signal handler decides the prologue - twelve push/pop - pairs to cover what the helper might clobber. Splitting the counter into - two 32-bit halves removes the call and halves the prologue: the handler - goes from ~47 instructions to ~26 and keeps the full 64-bit range, the - carry running once every 49.7 days. +### The millisecond clock stays 64 bits - **Measured and not taken, because it costs 66 B of flash to save about - 0.4 % of the CPU** - `millis()` then reassembles the halves, and the - readers pay for it. It is the right change only if the tick's cycles ever - matter; today nothing here is timing-critical. Falls away entirely if the - counter narrows above, which is the reason to decide that one first. +Narrowing `uptime::millis()` and the three timestamps that hold its value from +`uint64_t` to `uint32_t` is **608 B of flash, 7.6 % of the image** (8004 B down +to 7396), plus 16 B of RAM. + +**Refused, owner-stated: this board runs continuously and an uptime that +restarts every 49.7 days is not acceptable.** That is what a 32-bit +millisecond counter wraps at, and the display is not the only cost - the +interval tests would have to become subtractions, since `now - last >= +interval` survives a wrap where `now >= last + interval` does not, and +`terminal.hpp`'s monitor tick is written the second way. The 64-bit counter is +what puts the wrap out of reach, which is the property being bought. + +### The 1 kHz tick keeps its 64-bit increment + +The compare handler increments 64 bits, so it calls libgcc's `__adddi3_s8`, and +a call inside a signal handler decides the prologue - twelve push/pop pairs for +what the helper might clobber. Splitting the counter into two 32-bit halves +removes the call and keeps the full range, taking the handler from ~47 +instructions to ~26 with the carry running once every 49.7 days. + +**Refused: it costs 66 B of flash to buy about 0.4 % of the CPU**, and nothing +here is timing-critical. It was only ever worth considering alongside the +narrowing above, which is refused outright.