diff --git a/docs/BRINGUP.md b/docs/BRINGUP.md index aca0823..5654762 100644 --- a/docs/BRINGUP.md +++ b/docs/BRINGUP.md @@ -1,7 +1,8 @@ # ForgeFIRM bring-up status & cold-start runbook Last updated: **2026-08-02** — milestone 2 (factory-true motion tuning) -bench-verified. +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). @@ -63,7 +64,7 @@ motion constants were extracted from the `_RESOURCES` pulse files `/sys/class/leds/lid_led*/target`) and resets analog config (below). - **Build host**: WSL2 distro `forge-yocto`, tree at `~/dev/openglow-forgefirm`. `~/src-sync.sh` rsyncs the Windows repos in - (now includes `python3-gfhardware`). Build: + (includes `python3-gfhardware` and `grblHAL-glowforge`). Build: `cd ~/dev/openglow-forgefirm/forgefirm && kas shell kas/forgefirm-glowforge.yml -c 'bitbake forgefirm-image forgefirm-image-dev'`. Artifacts: @@ -73,29 +74,42 @@ motion constants were extracted from the `_RESOURCES` pulse files -c '...'` eats `$VAR` expansions (use script files run via PowerShell, not Git Bash, which MSYS-mangles `/mnt/c` paths). -## Running the step backend (grblHAL on the board) +## Running the controller (grblHAL-glowforge on the board) -Source: the ForgeFIRM grblHAL step backend (grblHAL core + the -glowforge pulse-stream sink). +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). -1. Build: cross-compile the backend in the forge-yocto WSL distro (from - PowerShell). Produces `build-arm/grblHAL_glowforge` in the WSL tree - (`-O1 -g`, `GLOWFORGE_DEFAULTS=ON` → machine constants baked in: - 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, see - `puls_profile.py`). -2. Deploy to `/usr/bin/grblHAL_glowforge` on the board. -3. Start: `cd /data && GFSINK=/dev/glowforge grblHAL_glowforge -p 23 -n -t 1.0` - (`-t 1.0` is REQUIRED: the real-time throttle is what bounds the - queue). Env knobs: `GFSINK_RATE` (machine tick, default 28160 Hz = +1. Build: `wsl -d forge-yocto -- bash /forgefirm/scripts/bench/build-glowforge.sh` + (from PowerShell). Produces `build-arm/grblHAL_glowforge` in the WSL + tree (`-O1 -g`; machine constants live in `src/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, see `puls_profile.py`). +2. Deploy to `/usr/bin/grblHAL_glowforge` on the board (kill the running + instance first — the binary can't be overwritten while executing). +3. 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 sink 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 — the old - manual sysfs block is no longer needed. If the baked $-defaults - changed since the last run, `$RST=$` once (stored settings win). + 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. 4. Connect LightBurn/UGS to `172.16.1.97:23`, or jog raw: - `$J=G91X40F1200`. + `$J=G91X40F1200`. `^X` mid-motion aborts via kernel `cnc/stop` + (controlled decel) and raises an alarm; TCP disconnects never kill the + process (the deadman fd stays held). ## Hardware facts bank (measured) @@ -143,8 +157,12 @@ glowforge pulse-stream sink). backend; underrun → grblHAL alarm; interlock-trip recovery check. 5. **6.6 camera service**: persistent MJPEG (ulfius, forgectrl) — also the natural time for the deferred 5.6 emulator smoke (homing images). -6. **Housekeeping**: upstream the settings-write crash fix (grblHAL - crashes on every runtime $-settings write — NULL chained - `grbl.on_settings_changed` in gcode.c's gc_init); Phase 7 doc sweep - (CLAUDE.md charter refresh, README roadmap); kas flip + first GitHub - release per kas/README.md once ready to publish. +6. **Housekeeping**: ~~pick the controller's remote home~~ **DONE + 2026-08-02** — the controller is now the canonical driver repo + `github.com/ScottW514/grblHAL-glowforge` (+ `ScottW514/core` fork; + 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 the + `forgectrl` slot 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. diff --git a/scripts/bench/README.md b/scripts/bench/README.md index ee356b7..fb5848d 100644 --- a/scripts/bench/README.md +++ b/scripts/bench/README.md @@ -8,6 +8,7 @@ target board (dev image, python3 present) unless noted. | `feeder.c` | Spike-step-3 underrun proof: streams NOP pulse bytes to `/dev/glowforge` with wall-clock pacing, bounded queue depth, deadman flock, SCHED_FIFO. Usage: `feeder `. Passed 100 kHz × 120 s under full load with 0.2 ms worst write latency. Cross-compile with `build-feeder.sh` (WSL). | | `bench_phase2.py` | End-of-data protocol bench (audit M2–M5): underrun detection/ack, parked no-replay guard, resume(0), continuous-feed stability, 20× run/underrun cycles. Motion-safe (motors locked, laser latched). 16/16 PASS on 2026-07-26. | | `check_pwm.py` | Laser PWM register check (audit M8): reads PWM2 PWMCR/PWMPR via /dev/mem, expects divider 13 × ~127 counts ≈ 40 kHz. The scope on LASER_PWM remains the final pre-live-fire gate. | +| `build-glowforge.sh` | Cross-compiles **grblHAL-glowforge** (the canonical driver repo, `../../../grblHAL-glowforge`) in the forge-yocto WSL distro. Run: `wsl -d forge-yocto -- bash /build-glowforge.sh` (from PowerShell; Git Bash mangles /mnt/c paths). This is the production controller build. | | `build-feeder.sh` | Cross-compiles `feeder.c` the same way. | | `puls_profile.py` | Decodes factory `.puls` streams (raw or GF1-headered) into velocity/accel profiles: peak speeds, ramp-slope fits, per-move segments, Z cadence. Runs anywhere (stdlib only). Source of the factory-true grblHAL defaults (milestone 2): 700/590 mm/s² accel, 200 mm/s max rate, 28160 Hz travel tick. | | `bench_m2.py` | Milestone-2 motion bench, runs against the board over TCP:23: bounded round-trip jogs (sanity, max-rate, diagonal) + feed-hold/resume mid-move, reporting peak feed, state transitions, and position drift. All-green 2026-08-02 at factory-true settings. | diff --git a/scripts/bench/bench_m2.py b/scripts/bench/bench_m2.py index 37b7f28..7c1ba01 100644 --- a/scripts/bench/bench_m2.py +++ b/scripts/bench/bench_m2.py @@ -75,9 +75,12 @@ class Grbl: state = st[1:].split('|')[0] if not states or states[-1] != state: states.append(state) - for f in st.split('|'): + for f in st[1:-1].split('|'): + # FS:feed,speed with a spindle registered; F:feed without if f.startswith('FS:'): peak = max(peak, float(f[3:].split(',')[0])) + elif f.startswith('F:'): + peak = max(peak, float(f[2:].split(',')[0])) if state.startswith('Idle'): return peak, states time.sleep(poll) diff --git a/scripts/bench/build-glowforge.sh b/scripts/bench/build-glowforge.sh new file mode 100644 index 0000000..f0cf67b --- /dev/null +++ b/scripts/bench/build-glowforge.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Cross-compiles grblHAL-glowforge (the canonical driver repo) for the +# factory board, borrowing the Yocto cross toolchain from the ulfius work +# directory. Run from PowerShell: wsl -d forge-yocto -- bash . +set -e +TC=/home/builder/dev/openglow-forgefirm/forgefirm/build/tmp/work/cortexa9t2hf-neon-fslc-linux-gnueabi/ulfius/2.7.15 +export PATH="$TC/recipe-sysroot-native/usr/bin:$TC/recipe-sysroot-native/usr/bin/arm-fslc-linux-gnueabi:$PATH" +cd /home/builder/dev/openglow-forgefirm/grblHAL-glowforge +rm -rf build-arm +cmake -B build-arm \ + -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=arm \ + -DCMAKE_C_COMPILER=arm-fslc-linux-gnueabi-gcc \ + -DCMAKE_BUILD_TYPE=None \ + "-DCMAKE_C_FLAGS=--sysroot=$TC/recipe-sysroot -mthumb -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 -O1 -g" \ + "-DCMAKE_EXE_LINKER_FLAGS=--sysroot=$TC/recipe-sysroot" \ + > /tmp/cmake-gf.log 2>&1 +cmake --build build-arm -j8 > /tmp/gfbuild.log 2>&1 || { tail -30 /tmp/gfbuild.log; exit 1; } +echo BUILD-OK +file build-arm/grblHAL_glowforge 2>/dev/null || ls build-arm/