Full design job (rapid / M4 cut trace / return rapid) ran smoothly end to end on grblHAL-glowforge over TCP:23, laser locked. Records the two driver fixes the first attempts surfaced: locked laser spindle and continuation-wakeup cursor alignment.
10 KiB
ForgeFIRM bring-up status & cold-start runbook
Last updated: 2026-08-02 — milestone 2 (factory-true motion tuning)
bench-verified, and the controller promoted to a canonical grblHAL driver
repo (grblHAL-glowforge) with bench parity re-proven on hardware.
Read together with AUDIT_ACTION_PLAN.md in the project root (sibling of
this repo; per-finding status of the 2026-07-03 audit) and
kernel-module-glowforge/UAPI.md (the pulse-stream feeder contract).
Where the project stands
Audit phases 0–5: complete and hardware-verified. Both motion blockers
fixed (cnc probe / 40v-supply; SDMA script relocated to <26 0xF00> with a
pre-run integrity guard); the end-of-data protocol reworked and bench-proven
(underrun is a first-class underrun state behind the streaming attr;
16/16 protocol bench); laser PWM verified at 39.98 kHz (register level);
CONFIG_PREEMPT=y; uEnv/u-boot/ulfius build integrity restored; legacy
cloud mode repaired (nvmem identity → hostname XXX-XXX verified on fuses;
deadman/safety loop; camera error paths).
Phase 6 spike: achieved.
- grblHAL (unmodified core) runs on the board, speaking Grbl 1.1f over TCP port 23 (LightBurn-confirmed).
- Underrun proof: 100 kHz × 120 s under full load, 150 ms queue, 0.2 ms worst write latency, zero underruns. Measured SDMA script ceiling: ~165 kHz effective (~6 µs/byte).
- The step backend works: the driver resamples grblHAL's step
events into pulse bytes and live-feeds
/dev/glowforge. X and Y jogs from TCP G-code move the real gantry; grblHAL and kernel position counters agree step-for-step. Motion-only: the laser latch is forced locked, byte bit 4 is never emitted.
First real LightBurn job: 2026-08-02, operator-verified. Device
setup per LIGHTBURN.md (GRBL over TCP:23); a full design job — rapid
in, M4 dynamic-power cut trace at commanded speed, return rapid — ran
smoothly end to end on grblHAL-glowforge (laser locked, motion only).
Two driver fixes came out of the first attempts: the locked laser
spindle (M4/$32 support without fire capability) and the
continuation-wakeup cursor alignment (back-to-back cycles previously
clamped into step bursts — jerky, step-losing rapids; found via the
per-run clamped stat from the operator's own job log).
Milestone 2 (motion quality): bench-verified 2026-08-02. The factory
motion constants were extracted from the _RESOURCES pulse files
(scripts/bench/puls_profile.py) and applied end-to-end:
- grblHAL defaults now factory-true: 12000 mm/min max rate (X/Y), 700/590 mm/s² accel (X/Y). Machine tick default 28160 Hz (the factory's own travel-move tick; 10 kHz caps an axis at 187.5 mm/s).
- The sink now applies the whole analog machine config itself at init (modes, decay, motor_lock, PIC currents) and switches PIC currents run↔hold around motion like the factory did (135/22 running, 33/5 idle, drop deferred until the kernel queue has drained).
- Bench (
scripts/bench/bench_m2.py, all green): sustained 200 mm/s on a 120 mm jog, exact round-trip positioning, feed-hold parks and resumes cleanly, current switching observed live, zero underruns at 28160 Hz. - NOTE: stored $-settings beat freshly baked defaults — after changing
GLOWFORGE_DEFAULTSvalues, run$RST=$once on the board (the sim persists settings in its eeprom file in /data).
The bench
- Board: SSH
root@172.16.1.97(fixed DHCP lease since 2026-08-02; was .130), empty password (ssh -o PreferredAuthentications=nonelogs straight in). Dev image (forgefirm-image-dev) on SD; BusyBox userland + python3 + gdb/strace. Serial console on ttymxc0 available at the bench. - Deploying kernels: re-burn the SD with the freshly built
forgefirm-image-dev-glowforge.rootfs.wic.gz(deploy dir below). Where the boot flow loads the kernel from was never fully traced (the wic has no boot partition; the eMMC env area reads empty) — re-burning works and is the procedure. Module-only changes hot-swap: scpglowforge.koover/lib/modules/<kver>/extras/, thenrmmod glowforge && modprobe glowforge. NOTE: a module reload turns off the lid LED (relight via/sys/class/leds/lid_led*/target) and resets analog config (below). - Build host: WSL2 distro
forge-yocto, tree at~/dev/openglow-forgefirm.~/src-sync.shrsyncs the Windows repos in (includespython3-gfhardwareandgrblHAL-glowforge). Build:cd ~/dev/openglow-forgefirm/forgefirm && kas shell kas/forgefirm-glowforge.yml -c 'bitbake forgefirm-image forgefirm-image-dev'. Artifacts:forgefirm/build/tmp/deploy/images/glowforge/. - Shell gotchas (cost real time): PowerShell mangles embedded double
quotes in git-commit here-strings (avoid
"in messages);wsl -- bash -c '...'eats$VARexpansions (use script files run via PowerShell, not Git Bash, which MSYS-mangles/mnt/cpaths).
Running the controller (grblHAL-glowforge on the board)
Source: C:\dev\openglow-forgefirm\grblHAL-glowforge — the canonical
grblHAL driver repo (github.com/ScottW514/grblHAL-glowforge, branch
main): core as a submodule at src/grbl (→ ScottW514/core fork, branch
forgefirm, carrying the settings-write crash fix, PR'd upstream as
grblHAL/core#999), driver.c implementing the HAL, machine constants in
src/boards/glowforge.h. Architecture: a wall-paced producer thread runs
the core stepper ISR against a virtual step clock (1000× machine tick)
and maps step events to pulse bytes; a SCHED_FIFO shipper feeds
/dev/glowforge with the bounded queue; a recursive core mutex stands in
for interrupt masking. GFSINK unset = null-sink mode (full engine, no
hardware I/O — host testing).
- Build:
wsl -d forge-yocto -- bash <repo>/forgefirm/scripts/bench/build-glowforge.sh(from PowerShell). Producesbuild-arm/grblHAL_glowforgein the WSL tree (-O1 -g; machine constants live insrc/boards/glowforge.h, force-included into the core: 53.333 µsteps/mm XY @ ×8, 2.832 half-steps/mm Z, 0.417" Z travel, 12000 mm/min max, 700/590 mm/s² accel — factory-derived, seepuls_profile.py). - Deploy to
/usr/bin/grblHAL_glowforgeon the board (kill the running instance first — the binary can't be overwritten while executing). - Start:
cd /data && GFSINK=/dev/glowforge grblHAL_glowforge -p 23 -e /data/EEPROM-glowforge.DAT(no-t— real-time pacing is intrinsic now). Env knobs:GFSINK_RATE(machine tick, default 28160 Hz = factory travel tick),GFSINK_DEPTH_MS(queue depth = feed-hold latency, default 200). The driver applies the full analog machine config itself at init (×8 modes, decay 1, motor_lock 8, laser latched, PIC hold currents) and swaps PIC run/hold currents around motion. If the baked $-defaults changed since the last run,$RST=$once (stored settings win). Each motion run logs a producer-stats line to stderr (callbacks, µs/call, max-behind, clamped) — clamped should stay 0. - Connect LightBurn/UGS to
172.16.1.97:23, or jog raw:$J=G91X40F1200.^Xmid-motion aborts via kernelcnc/stop(controlled decel) and raises an alarm; TCP disconnects never kill the process (the deadman fd stays held).
Hardware facts bank (measured)
- SDMA pulse engine: ring free = 128 MiB − 32 KiB gap; script effective
ceiling ~165 kHz; position counters (
sdma_contextsc0/1/2 = X/Y/Z steps, sc3 = bytes) match grblHAL exactly. - Byte layout & rules: see the UAPI.md feeder contract (authoritative).
- Z: bit 6 SET = lens UP = +Z (hardware-verified; pulsedata.py was the inverted party, fixed). Home = hall trigger at TOP; usable travel ≈ 30 half-steps ≈ 10.6 mm ≈ 0.417"; 0.3534 mm/half-step. Never blind-drive Z — hall-supervised only.
- XY: 0.15 mm per full step; DIR bit set = −X / +Y (Y1/Y2 complementary).
- Factory motion profile (measured from
_RESOURCESpulse streams withpuls_profile.py): accel ≈ 700 mm/s² X / 590 mm/s² Y on v2.6.0 firmware (2018 firmware used ≈1000); header HAxr=132/HAyr=112/HAar=133 ⇒ ≈5.3 mm/s² per HA unit. Travel moves peak 202 mm/s vector (≈ 8 in/s) at STfr=28160 Hz; prints/hunts run STfr=10000. Cut feed in the sample print: 145 mm/s. Z cadence ≈ 61–115 ms per half-step (≈ 5.7 mm/s max). - Factory analog config (constant across all captured jobs, 2018→2026): PIC currents X 135 run / 33 hold, Y 22 run / 5 hold (axis DAC scales differ by design); x/y_decay=1; ×8 microstepping; run currents applied only while motion plays, hold otherwise.
- Laser PWM: 39.98 kHz register-verified (divider 13 × 127 counts).
- Switches: truthy = closed/OK; SW_INTERLOCK reads False on units without the rear plug — must NOT gate motion (beam is hardware-gated).
- Machine identity from OCOTP nvmem: serial 00000000 → hostname XXX-XXX (matches the factory label).
Next work (in rough order)
- Backend milestone 2 — motion quality: DONE and human-verified 2026-08-02. Operator confirmed motion is "butter smooth" (and near silent) on a full observation run — slow/fast/diagonal/zigzag jogs at up to 200 mm/s under grblHAL-glowforge with the factory-true analog config. The pre-tuning loudness was the 150/150 currents + unset decay mode. Milestone closed.
- Laser mapping (gated on the scope session): spindle → power bytes (bit 7) + bit 4 laser-enable, M3/M4/$32 semantics, PWM-reset rule per the contract. No live fire before the standing scope gates: LASER_PWM waveform vs factory capture + ≤1-tick laser drop at underrun.
- Homing: X/Y home switch GPIOs exist in the cnc pin map (unused so far); wire as grblHAL limits or keep StallGuard-less factory scheme; Z homes against the hall sensor (top).
- 6.5 safety mapping: door/estop evdev → feed-hold/halt in the backend; underrun → grblHAL alarm; interlock-trip recovery check.
- 6.6 camera service: persistent MJPEG (ulfius, forgectrl) — also the natural time for the deferred 5.6 emulator smoke (homing images).
- Housekeeping:
pick the controller's remote homeDONE 2026-08-02 — the controller is now the canonical driver repogithub.com/ScottW514/grblHAL-glowforge(+ScottW514/corefork; the settings-write crash fix is upstream PR grblHAL/core#999; repoint the submodule to upstream when it merges). Remaining: a Yocto recipe for grblHAL-glowforge in meta-forgefirm (pin SRCREV; fills theforgectrlslot per kas/README.md), Phase 7 doc sweep (CLAUDE.md charter refresh, README roadmap), kas flip + first GitHub release per kas/README.md once ready to publish.