From 64a720bbab76c4de34c971a508299e13ecd1f63b Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Mon, 17 Aug 2026 17:59:11 -0400 Subject: [PATCH] Pin the producer-lead change; record what the bench measured Bump the grblHAL pin to the producer lead and margin instrumentation. Rewrite BRINGUP item 16 to the present state. The scheduling half is done - the producer runs SCHED_FIFO below the shipper, core_mx carries priority inheritance, and clamping is reported per run - and two bench runs 90 s apart on one image show what that does and does not cover: a nice-5 CPU hog passes clean while the camera streaming clamps 7 runs, because per-frame cache maintenance over a 4.8 MB non-coherent capture buffer is kernel-context work no userspace priority can preempt. Record the margin finding, since it explains why a 4 ms stall was enough: the queue depth cancels between the shipper's due index and the producer's base, so the pacing lead is the only slack. Record the cycle-churn ceiling that caps that lead at 10 ms, and that the re-base fix is what unlocks more. Camera gating stays owed, with capture resolution named as the lever that shortens the stall rather than merely spacing stalls out. --- docs/BRINGUP.md | 55 ++++++++++++------- .../grblhal-glowforge-pin.inc | 2 +- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/docs/BRINGUP.md b/docs/BRINGUP.md index 23a06a6..114ac7b 100644 --- a/docs/BRINGUP.md +++ b/docs/BRINGUP.md @@ -938,25 +938,42 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`. log EV_SW head-bit edges plus `head/beam_detect_digital|_analog` while firing. -16. **Step timing under CPU contention.** The board runs one core, and of - grblHAL's four threads only the shipper is `SCHED_FIFO`. The producer — - the thread that emulates the stepper timer and stamps every step onto the - virtual time grid — is `SCHED_OTHER` at nice 5, the same class and nice as - forgectrl's MHD connection threads, so a camera stream viewer (~35 % of the - core on its own) competes with step generation on equal terms. When the - producer's virtual clock falls behind wall clock by more than the ring - depth (200 ms), `gf_stream_pulse` clamps late events forward and the - backlog ships one step per machine tick: 28 160 steps/s against the 1 778 - that 2000 mm/min asks for, a ~16× velocity burst the motors cannot follow. - `cnc/underruns` stays 0 through all of it, because the ring never goes - dry — the stream is continuous and only its timing is wrong, which is - exactly what the present counters cannot see. Owed: put the producer on - `SCHED_FIFO` just below the shipper; gate or throttle the camera stream - while a job runs (forgectrl already holds the run state, so this shares the - bench slot with the HTTP surface caps in item 8); and report the clamp - count per run instead of only cumulatively at process exit. The per-run - `LOG_DEBUG` line is the instrument — a clean 2000 mm/min run with no camera - consumer reports `max behind 1.5 ms, clamped 0`. +16. **Step timing under CPU contention.** The board runs one core. The producer + thread advances virtual time and stamps every step onto the pulse grid, so + any interval it is kept off the CPU is an interval the grid does not + advance; events after it map behind the ship cursor, where + `gf_stream_pulse` clamps them forward and the backlog ships one step per + machine tick — 28 160 steps/s against the 1 778 that 2000 mm/min asks for, + a ~16× velocity burst no motor follows. `cnc/underruns` reads 0 throughout, + because the ring never goes dry: the stream is continuous and only its + timing is wrong, which is exactly what the kernel counters cannot see. + + The margin absorbing a stall is **not** the 200 ms queue depth. The + shipper's due index carries the same `+ gf.depth` the producer's base + starts at, so the two cancel and the producer's lead over the cursor is the + only slack there is. It was 2 ms. It is now `GFSINK_LEAD_MS`, default 10, + and the per-run `LOG_DEBUG` line reports the measured `min margin` in ms + against it rather than leaving it to be derived. The ceiling is the + cycle-churn path: `gf_stream_wakeup` re-bases production onto the wall + cursor only when the cursor has passed it, so a lead that survives an idle + gap skips the re-base and accumulates as dark padding — 2 and 10 ms give an + identical 64 790-byte churn stream, 15 ms and above inflate it to ~225 k. + **Owed:** make the re-base reclaim the overshoot, which is what unlocks a + lead beyond 10 ms. + + Done: the producer runs `SCHED_FIFO` one below the shipper, `core_mx` + carries priority inheritance to bound the inversion that promotion would + otherwise create, and the clamp count is reported per run at `WARNING`. + **Still owed: gate or throttle the camera while a job runs.** Priority + alone does not cover it — bench runs 90 s apart on one image show a nice-5 + CPU hog passing clean (20 legs, 0 clamps) while the camera streaming + clamped 7 runs, because its per-frame cache maintenance over a 4.8 MB + non-coherent capture buffer is kernel-context work no userspace priority + can preempt. Measured stall: 3.9–4.4 ms. Capture resolution is the lever + that shortens it (the mainline `ov5648` offers 1280×960 and 640×480 binned + modes, 4.1× and 16.4× fewer bytes); frame rate only spaces the stalls out, + and the existing `FORGECTRL_STREAM_FPS` cap skips demosaic and encode but + still dequeues every frame. Shares the bench slot with item 8. 17. **Laser power model and the missing duty floor.** grblHAL maps S onto the analog PWM duty (`$30`/`$31` → `$35`/`$36`, written raw into PWMSAR against the 127-count period), and ForgeFIRM overrides only `$32`, so a shipped diff --git a/meta-forgefirm/recipes-forgefirm/grblhal-glowforge/grblhal-glowforge-pin.inc b/meta-forgefirm/recipes-forgefirm/grblhal-glowforge/grblhal-glowforge-pin.inc index 65c398c..7c9fdf6 100644 --- a/meta-forgefirm/recipes-forgefirm/grblhal-glowforge/grblhal-glowforge-pin.inc +++ b/meta-forgefirm/recipes-forgefirm/grblhal-glowforge/grblhal-glowforge-pin.inc @@ -2,5 +2,5 @@ # changes; keep only SRCREV and PV here - the image manifest leaves # *-pin.inc out of the layer content hash because the component entry # already identifies the pinned source (forgefirm-image-manifest.bbclass). -SRCREV = "026c169c6ee028907a0235d4e3bbf5cf9d91e2a8" +SRCREV = "fd059b3bf52de103f598cf94cb860916e5fffd00" PV = "0.1.0"