mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 08:41:13 -07:00
docs: kas README is build procedure; status moves to BRINGUP and the log
kas/README.md keeps the build, lockfile, pin and release procedure and a
short build-time facts list; its push/release checklist and Scarthgap
migration backlog are archived verbatim in CAMPAIGN-LOG ("Superseded
status notes"). The real-time argument (PREEMPT, why PREEMPT_RT is not
selectable or needed) joins the BRINGUP hardware facts bank next to the
ring measurements it rests on; the camera pointers go to the 0011-0013
patch headers; the image recipe's gfui-client comment drops its README
pointer.
Docs and a recipe comment only; no catalog consequence.
This commit is contained in:
@@ -95,8 +95,9 @@ The control board is common to Glowforge Basic, Plus, and Pro. The 5 MP
|
||||
(OV5648) camera modules are fully supported and hardware-validated. The 8 MP
|
||||
(OV8856) modules found in "HD" units have a complete capture path — the kernel
|
||||
patches, device tree and sensor-aware capture profile they need are all in the
|
||||
build — but it has never run on an 8 MP machine, so treat it as untested; see
|
||||
the camera note in [kas/README.md](kas/README.md).
|
||||
build, but it has never run on an 8 MP machine, so treat it as untested; see
|
||||
`docs/BRINGUP.md` (Next work, cameras) and the headers of kernel patches
|
||||
0011-0013 in `meta-glowforge-bsp`.
|
||||
|
||||
## Safety
|
||||
|
||||
|
||||
+13
-3
@@ -788,7 +788,15 @@ until `releases/v<version>/acceptance.json` is committed.
|
||||
counters (`sdma_context`
|
||||
sc0/1/2 = X/Y/Z steps, sc3 = bytes) match grblHAL exactly. Underrun proof:
|
||||
100 kHz × 120 s under full load, 150 ms queue, 0.2 ms worst write latency,
|
||||
zero underruns.
|
||||
zero underruns. Real time: the kernel runs `CONFIG_PREEMPT=y` (the factory
|
||||
behavior; `imx_v6_v7_defconfig` alone gives `PREEMPT_VOLUNTARY`).
|
||||
PREEMPT_RT is not selectable on arm32 6.12 (no `ARCH_SUPPORTS_RT`) and is
|
||||
not needed: the ring drains at 1 byte per EPIT tick, at most 200 KB/s even at
|
||||
the 200 kHz ceiling, so the feeder's bounded queue depth of ~150 ms (a few
|
||||
KB in flight) rides out worst-case scheduling latency with orders of
|
||||
magnitude to spare. Bounded queue depth plus `SCHED_FIFO` for the feeder is
|
||||
the design; RT is worth revisiting only if the underrun bench ever
|
||||
contradicts this arithmetic.
|
||||
- Byte layout and stream rules: see the UAPI.md feeder contract
|
||||
(authoritative).
|
||||
- **Z**: bit 6 SET = lens UP = +Z (hardware-verified). Home = hall trigger at
|
||||
@@ -1070,7 +1078,8 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
|
||||
answer whether the 2-lane RAW8 full-resolution mode locks the D-PHY at
|
||||
720 Mbps/lane and what exposure/gain the sensor wants; the details, the
|
||||
reachable-mode reasoning and the factory fallback configuration are in
|
||||
`kas/README.md` §2. Also unapplied: the factory's **per-unit lens-shading
|
||||
the headers of kernel patches 0011-0013 (`meta-glowforge-bsp`,
|
||||
`recipes-kernel/linux/`). Also unapplied: the factory's **per-unit lens-shading
|
||||
calibration**, an OmniVision LENC register file the factory pushes into the
|
||||
sensor at every stream start (`load_cam_regs.sh` → a `regs` sysfs attribute
|
||||
its driver adds; OV8858 `0x58xx` addresses remapped to the OV8856's
|
||||
@@ -1204,7 +1213,8 @@ Open items only. Anything closed is in `CAMPAIGN-LOG.md`.
|
||||
Tools that genuinely need a second host (LAN flood, remote auth probes)
|
||||
stay host-side by design, and the registry marks them so.
|
||||
13. **Publish.** The kas flip and the first GitHub release, per
|
||||
`kas/README.md`, once ready to publish. Repoint the core submodule to
|
||||
`kas/README.md` ("Pins, pushes, and the release flow"), once ready to
|
||||
publish. Repoint the core submodule to
|
||||
upstream if the `step_us_min` sizing fix merges.
|
||||
14. **Update system Phase 5 — recovery refresh.** The remaining phase of
|
||||
`docs/UPDATE-SYSTEM.md` (a refreshed recovery image in boot0); Phases 0–4
|
||||
|
||||
@@ -3885,6 +3885,230 @@ sweep (CLAUDE.md charter, README roadmap, INSTALL/BUILD/kas README)~~
|
||||
**DONE 2026-08-13.** Remaining: kas flip + first GitHub release per
|
||||
kas/README.md once ready to publish.
|
||||
|
||||
### kas/README.md status sections, as listed 2026-08-24
|
||||
|
||||
The two status sections of `kas/README.md` (the push/release checklist with its DONE markers, and the Scarthgap migration backlog), verbatim, before the README was cut back to build procedure and present-state facts; outstanding items are in BRINGUP, and the OV8856 reasoning lives in the 0011-0013 patch headers.
|
||||
|
||||
#### Push & release order (source-of-truth sequencing)
|
||||
|
||||
The build is only reproducible when recipe pins, layer branches, and the kas
|
||||
config move in the right order. The sequence, with current status:
|
||||
|
||||
1. **Source repos pushed & pinned** — **DONE.** Every source repo
|
||||
(`kernel-module-glowforge`, `python3-gfhardware`, `Glowforge-Utilities`,
|
||||
`grblHAL-glowforge`, `forgectrl`) is on GitHub and its recipe pins an exact
|
||||
`SRCREV` — no `AUTOREV` anywhere. Whenever a source repo changes: push it,
|
||||
then bump the pin deliberately (BSP recipes in meta-openglow, ForgeFIRM
|
||||
components in meta-forgefirm) and re-verify with
|
||||
`bitbake -c fetch <recipe>`. A component's `SRCREV` (and the `PV` that
|
||||
moves with it) lives in `<recipe>-pin.inc` next to the recipe, nothing
|
||||
else goes in that file: the image manifest leaves `*-pin.inc` out of the
|
||||
layer content hash, so a pin bump changes the component's fingerprint
|
||||
and only that (`docs/ACCEPTANCE.md`) — a pin written into the recipe body
|
||||
still builds, but counts as a platform change and forces a full
|
||||
acceptance campaign.
|
||||
2. **meta-openglow pushed** — **DONE.** The Scarthgap port lives on
|
||||
the **`scarthgap` branch** (Yocto layer convention; the Dunfell-era `master`
|
||||
is untouched). Development continues on the local sibling checkout; push /
|
||||
fast-forward `scarthgap` as work lands.
|
||||
3. **forgefirm pushed** with the kas config and a `kas lock` lockfile pinning
|
||||
the upstream layers (poky, meta-openembedded, meta-freescale,
|
||||
meta-freescale-distro).
|
||||
4. **At release time**:
|
||||
- flip `meta-openglow` in `forgefirm-glowforge.yml` from the local-sibling
|
||||
block to the pinned-remote block (commented FUTURE block in the file);
|
||||
- refresh `kas lock`, tag all repos, and prove self-containment by building
|
||||
from a **fresh clone**.
|
||||
5. **GitHub release**: run `scripts/release.sh <version>` on the build
|
||||
host. It gates (version single-source, rootfs-vs-slot size,
|
||||
installer-embedded pubkey vs the signing key, factory-era fwup
|
||||
verification, and the **acceptance gate** - the committed
|
||||
`releases/v<version>/acceptance.json` from the bench campaign must
|
||||
authorize the built rootfs, `docs/ACCEPTANCE.md`), builds, packs and
|
||||
signs `forgefirm.fw`, stages the assets with `sha256sums.txt`, and
|
||||
prints the `gh release create` command. Assets and their exact names
|
||||
(the installer and the update manager download them verbatim):
|
||||
`forgefirm.fw`, `sha256sums.txt`,
|
||||
`forgefirm-image-glowforge.rootfs.wic.gz`, plus `acceptance.json` and
|
||||
`acceptance.md`. The release tag
|
||||
`v<version>` = `FORGEFIRM_RELEASE` = the rootfs `/etc/forgefirm-version`
|
||||
= the `.fw` meta-version; `release.sh` enforces the agreement.
|
||||
|
||||
All recipes fetch their pinned revision from GitHub, so an image build is
|
||||
reproducible from the repos alone. For fast iteration on a source repo, bump
|
||||
its pin per iteration, or add a **local, untracked** `externalsrc` bbappend
|
||||
pointing at a working checkout — never commit one, or released images stop
|
||||
matching the pins.
|
||||
|
||||
#### Scarthgap migration backlog
|
||||
|
||||
The kas scaffold + `LAYERSERIES_COMPAT` bumps let the layers be *selected* under
|
||||
Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until:
|
||||
|
||||
1. ~~**Override-syntax migration**~~ — **DONE.** All `_append`/`_prepend`/
|
||||
`_remove`/`_${PN}` override syntax converted to the colon form across
|
||||
`meta-forgefirm`, `meta-openglow-core`, and `meta-glowforge-bsp` (22
|
||||
occurrences).
|
||||
2. **Kernel forward-port (4.14 to linux-fslc 6.12.20).** The factory NXP vendor
|
||||
kernel (linux-imx 4.14.98) carried 7 out-of-tree changes; these are re-derived
|
||||
against mainline 6.12 in `meta-glowforge-bsp/recipes-kernel/linux/linux-fslc_%.bbappend`
|
||||
(the forward-port landing zone), **not** re-applied as the 4.14 patches.
|
||||
- **Foundation: DONE.** `linux-fslc` 6.12.20 builds for `glowforge` with a
|
||||
ported device tree (`glowforge.dts` + `openglow_common.dtsi` overlaid into
|
||||
`arch/arm/boot/dts/nxp/imx/`, registered via a Makefile patch) and deploys
|
||||
`zImage` + `glowforge.dtb`. Boot-core + mainline-bound peripherals only.
|
||||
- **Free wins: DONE.** bus-freq disable *dropped* (no mainline busfreq);
|
||||
`st,lis2hh12` x3 + `national,lm75b` + `ti,wl1805` + gpio keys/leds bind to
|
||||
mainline drivers. The 12 V control rail is a plain always-on fixed
|
||||
regulator with no userspace consumer node (nothing in the firmware
|
||||
switches it). The PIC SPI delay and the laser PWM prescaler are layer
|
||||
patches; see Motion polish below.
|
||||
- **Config: the board's kernel.** `glowforge.cfg` names this board's driver
|
||||
set and turns off what `imx_v6_v7_defconfig` adds for the other i.MX
|
||||
boards, and `conf/machine/glowforge.conf` names the modules and firmware
|
||||
the rootfs carries (the `kernel-modules` meta-package is not used). Every
|
||||
line of the fragment is expected to land in the built `.config` as
|
||||
written; a line that does not means a parent symbol is missing. The
|
||||
defconfig never names `PM`, the regulator core or ext4 (it had them by
|
||||
selection from suspend, the PMICs and ext3), so the fragment pins them.
|
||||
Bench record: BRINGUP item 21, CAMPAIGN-LOG 2026-08-24.
|
||||
- **Motion path: DONE and hardware-validated** (live-fed pulse stream,
|
||||
real gantry motion, laser fire). The whole chain forward-ports and
|
||||
compiles on 6.12:
|
||||
- **EPIT API**: `epit_api.c` in `arch/arm/mach-imx` (`CONFIG_MXC_EPIT_API`),
|
||||
in vmlinux, symbols exported; `&epit1/&epit2` in the DT.
|
||||
- **SDMA-expose**: re-created `dma-imx-sdma.h` + `0003-imx-sdma-*.patch`
|
||||
(un-static survivors, re-added the glowforge helpers, custom int-callback
|
||||
hook); expose symbols in `Module.symvers`.
|
||||
- **`glowforge.ko`**: ported across many 6.12 API changes
|
||||
(`tasklet_hrtimer`→soft hrtimer, `timer_setup`, LED-trigger API,
|
||||
`pwm_get`, `spi_delay`/`controller`, `filelock.h`, void `.remove`,
|
||||
1-arg i2c probe). Compiles + links, 0 undefined symbols; the recipe
|
||||
fetches the module by pinned `SRCREV`.
|
||||
- **DT**: `glowforge,cnc/thermal/pic/head` re-added with `pwms`/`pwm-names`
|
||||
phandles; `glowforge.dtb` compiles with all motion nodes.
|
||||
Motion polish, both carried as layer patches in the bbappend: the laser
|
||||
PWM prescaler (factory 1001) is patch 0009, `fsl,extra-prescale` on
|
||||
`pwm-imx27`, set to 13 on `&pwm2`; the cnc engine programs a ~1925 ns
|
||||
period so the SDMA script writes raw 7-bit power levels into PWMSAR, and
|
||||
the extra divider stretches the output to ~25 us, the ~40 kHz carrier the
|
||||
laser PSU expects (mainline `pwm-imx27` alone would run the laser PWM at
|
||||
~520 kHz, and asking for 25 us directly caps the script's writes at ~8 %
|
||||
duty). The PIC inter-word SPI delay (factory 1005) is patch 0004: mainline
|
||||
`spi-imx.c` has no `PERIODREG` support, so the patch programs the ECSPI
|
||||
sample period from `spi_transfer.delay` (which pic.c sets) and forces
|
||||
fixed per-word bursts while a delay is requested, so the wait-states land
|
||||
between words; without it the PIC answers 0x0000 to the ID read. Both are
|
||||
in every image and hardware-validated (the PIC reads and the laser fires
|
||||
on them). The factory `glowforge,imx-pwm-audio` (buzzer) driver is not
|
||||
part of ForgeFIRM.
|
||||
- **Camera — DONE and hardware-validated.** The factory
|
||||
`ov5648_mipi.c` (NXP's removed `v4l2_int_device`/`mxc_v4l2_capture`) is
|
||||
replaced by the mainline `ovti,ov5648` subdev + imx6 `imx-media` (IPU CSI)
|
||||
+ `imx6-mipi-csi2` receiver. The factory CAM_SEL MIPI switch is modeled
|
||||
with the mainline `video-mux` (gpio-mux on `gpio7 10`): both sensors →
|
||||
video-mux → `mipi_csi` → IPU CSI. Sensor `xvclk` is the board's 24 MHz
|
||||
fixed oscillator (matching the factory DTB); avdd/dovdd/dvdd rails are in
|
||||
the DT. Both cameras stream live through forgectrl (MJPEG at 15 fps with
|
||||
VPU JPEG encode, full-resolution snapshots, mux arbitration).
|
||||
**HD units (8 MP OV8856) — code complete, UNTESTED.** The DT lists both
|
||||
`ovti,ov5648` (5 MP) and `ovti,ov8856` (8 MP) at 0x36 so one image covers
|
||||
both, and the driver matching the chip ID wins. Everything the OV8856
|
||||
needs is in the build: patch 0011 gives it the `get_mbus_config` the
|
||||
IPU-CSI hard-fails without (the same gap 0006 closes for ov5648), patch
|
||||
0012 retunes both PLL multipliers for the board's 24 MHz xvclk (mainline's
|
||||
tables are written for 19.2 MHz, which would run the link 25 % above the
|
||||
frequency the driver publishes), patch 0013 adds the 2-lane RAW8 modes
|
||||
(below), the endpoint's `link-frequencies` list carries the driver's whole
|
||||
2-lane menu (it rejects the endpoint outright if any entry is missing —
|
||||
the old list omitted 720 MHz, so probe would have failed), and forgectrl
|
||||
and gfhardware pick geometry and the sensor's control set from whichever
|
||||
driver bound.
|
||||
|
||||
The capture mode is the **full 3264×2448**, reached in RAW8. The
|
||||
sensor's stock RAW10 full-resolution 2-lane mode asks for 1.44 Gbps/lane
|
||||
and the i.MX6 CSI-2 D-PHY stops at 1 Gbps (`hsfreq_map` in
|
||||
`imx6-mipi-csi2.c` ends at 1000 Mbps and `max_mbps_to_hsfreqrange_sel()`
|
||||
returns `-EINVAL` above it), so `imx6-mipi-csi2` refuses to program it —
|
||||
but 8-bit samples carry the same frame at half the rate, which puts it on
|
||||
the 360 MHz link the binned modes already use, at 180 Mpx/s and 15 fps.
|
||||
Patch 0013 builds those modes from mainline's own 4-lane 3264×2448 and
|
||||
1632×1224 register lists plus a per-mode delta list: `0x3018` for two
|
||||
lanes, `0x3031` for 8-bit readout, and double the HTS because half the
|
||||
lanes carry half a line in the same time. It also makes the sample depth a
|
||||
mode property, so pixel rate, blanking and exposure ranges follow the mode
|
||||
instead of a fixed 10. Side effect worth having: the OV8856 path becomes
|
||||
byte-identical in shape to the OV5648's (8-bit BGGR, one byte per sample),
|
||||
and 3264 is a multiple of 32 so the NEON superpixel converter applies,
|
||||
which 1640 did not allow.
|
||||
|
||||
The values are the factory firmware's: its own OV8856 driver is RAW8-only
|
||||
and ships exactly these two resolutions over two lanes with the same
|
||||
`0x3018`/`0x3031` and the same HTS/VTS pairs. It reaches them through a
|
||||
different PLL divider chain (`0x0302=0x1e`, `0x0303=0x03`, `0x030f=0x07`,
|
||||
`0x0312=0x05`, `0x4837=0x58`) that halves the link again to 180 MHz and
|
||||
the internal SCLK with it — a self-consistent alternative, recorded in the
|
||||
patch header as the configuration to fall back to if the D-PHY will not
|
||||
lock at 720 Mbps/lane on real hardware.
|
||||
|
||||
Open, and only answerable on an 8 MP machine: whether it streams at all at
|
||||
720 Mbps/lane, and exposure/gain/white-balance commissioning — the OV8856
|
||||
driver publishes no red/blue balance controls, so white balance is
|
||||
uncorrected.
|
||||
3. **u-boot** — **DONE.** The `glowforge` u-boot is
|
||||
a standalone `u-boot_2020.01.bb` (Scarthgap's poky has no u-boot 2020.01
|
||||
base recipe to extend). It reuses poky's
|
||||
`u-boot-common.inc`/`u-boot.inc`, pins `SRCREV` to the upstream **v2020.01**
|
||||
tag with the matching `Licenses/README` md5, and overlays the glowforge board
|
||||
support + arch-Kconfig patch. **Builds clean under Scarthgap (GCC 13, no
|
||||
source fixes) and deploys `u-boot-glowforge.imx`.** Remaining: move
|
||||
`fw_printenv`/`fw_setenv` from `u-boot-fw-utils` to `libubootenv`
|
||||
(`PREFERRED_PROVIDER_u-boot-fw-utils` in `glowforge.inc`) when the rootfs needs
|
||||
them.
|
||||
4. **Device tree — DONE.** The `glowforge` `.dts` is validated against the
|
||||
linux-fslc 6.12 bindings and against the running board (motion, safety
|
||||
readbacks, cameras, sensors all bind and work).
|
||||
5. **Real-time strategy — decided.** The kernel runs
|
||||
`CONFIG_PREEMPT=y` (factory behavior; `imx_v6_v7_defconfig` alone gives only
|
||||
`PREEMPT_VOLUNTARY`). **PREEMPT_RT is not selectable on arm32 6.12** (no
|
||||
`ARCH_SUPPORTS_RT`) and is **not needed for the pulse feeder**. The
|
||||
argument is about queue depth, not ring size: the ring drains at 1 byte
|
||||
per EPIT tick (≤200 KB/s even at the 200 kHz ceiling), so the live
|
||||
feeder's bounded queue depth of ~150 ms — a few KB in flight — already
|
||||
rides out worst-case scheduling latency with orders of magnitude to spare
|
||||
(measured: 0.2 ms worst write latency under full CPU + I/O load; the
|
||||
underrun bench ran 100 kHz for 120 s with zero underruns). The ring
|
||||
itself is 32 MiB (the `ring_mb` module parameter, backed by the 32 MiB
|
||||
reserved pool, matching the factory ring): ~168 s of stream at 200 kHz,
|
||||
~56 min at the 10 kHz cloud-mode tick — a capacity that matters for the whole-job preload of
|
||||
cloud mode, not for latency. Bounded queue depth + `SCHED_FIFO` for the
|
||||
feeder is the design; revisit RT only if the underrun bench ever
|
||||
contradicts this arithmetic.
|
||||
|
||||
6. **gfui-client → forgectrl — DONE.** The stock `gfui-client` is excluded
|
||||
from `forgefirm-image` (`IMAGE_INSTALL:remove = "gfui-client"` in
|
||||
`meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb`). Its slot is
|
||||
filled by `forgectrl` (github.com/ScottW514/forgectrl — the machine-services
|
||||
daemon: web control panel, cameras, telemetry, settings, diagnostics,
|
||||
cooling engine, updates, and controller-mode supervision) plus the two
|
||||
controllers it supervises, `grblhal-glowforge` (Grbl over TCP:23) and
|
||||
`gfcloud` (the optional Glowforge web-service client, off unless selected).
|
||||
|
||||
---
|
||||
|
||||
**Image status:** `forgefirm-image` **builds end-to-end** on the forward-ported
|
||||
stack and deploys `forgefirm-image-glowforge.rootfs.wic.gz` (+ `zImage`,
|
||||
`glowforge.dtb`, `u-boot-glowforge.imx`) under `build/tmp/deploy/images/glowforge/`.
|
||||
Build-time prerequisites baked into the config: `ACCEPT_FSL_EULA = "1"` (NXP
|
||||
firmware-imx — the image also installs `firmware-imx-lic` so the EULA text
|
||||
ships beside the blobs) and the kernel default in `glowforge.conf`. Every
|
||||
`LICENSE` string in the layers (`meta-forgefirm`, `meta-glowforge-bsp`,
|
||||
`meta-openglow-core`) is SPDX, and the recipes for third-party components
|
||||
that carry more than one license (`wlconf`, `python3-gfhardware`) declare
|
||||
each of them with a checksum on its license text. The stack
|
||||
is hardware-validated end to end: motion timing, the laser and safety chain,
|
||||
the camera pipeline, both controller modes, and the A/B install path.
|
||||
|
||||
## Reference notes
|
||||
|
||||
### Head-IRQ source validation — the beam-emission hypothesis
|
||||
|
||||
+57
-196
@@ -11,7 +11,7 @@ the build.
|
||||
|---|---|
|
||||
| Yocto release | **Scarthgap 5.0 LTS** |
|
||||
| Kernel | **linux-fslc 6.12** (mainline LTS, from meta-freescale) |
|
||||
| Machine | `glowforge` (i.MX6 Solo SOM — Basic/Plus/Pro) |
|
||||
| Machine | `glowforge` (i.MX6 Solo SOM; Basic/Plus/Pro) |
|
||||
| Distro | `forgefirm` |
|
||||
| Image | `forgefirm-image` |
|
||||
|
||||
@@ -84,204 +84,65 @@ kas lock kas/forgefirm-glowforge.yml # writes kas/forgefirm-glowforge.lock.
|
||||
|
||||
kas auto-loads the lockfile on subsequent runs. Commit it; refresh deliberately.
|
||||
|
||||
## Push & release order (source-of-truth sequencing)
|
||||
## Pins, pushes, and the release flow
|
||||
|
||||
The build is only reproducible when recipe pins, layer branches, and the kas
|
||||
config move in the right order. The sequence, with current status:
|
||||
The build is reproducible only when recipe pins, layer branches, and the kas
|
||||
config move in the right order.
|
||||
|
||||
1. **Source repos pushed & pinned** — **DONE.** Every source repo
|
||||
(`kernel-module-glowforge`, `python3-gfhardware`, `Glowforge-Utilities`,
|
||||
`grblHAL-glowforge`, `forgectrl`) is on GitHub and its recipe pins an exact
|
||||
`SRCREV` — no `AUTOREV` anywhere. Whenever a source repo changes: push it,
|
||||
then bump the pin deliberately (BSP recipes in meta-openglow, ForgeFIRM
|
||||
components in meta-forgefirm) and re-verify with
|
||||
`bitbake -c fetch <recipe>`. A component's `SRCREV` (and the `PV` that
|
||||
moves with it) lives in `<recipe>-pin.inc` next to the recipe, nothing
|
||||
else goes in that file: the image manifest leaves `*-pin.inc` out of the
|
||||
layer content hash, so a pin bump changes the component's fingerprint
|
||||
and only that (`docs/ACCEPTANCE.md`) — a pin written into the recipe body
|
||||
still builds, but counts as a platform change and forces a full
|
||||
acceptance campaign.
|
||||
2. **meta-openglow pushed** — **DONE.** The Scarthgap port lives on
|
||||
the **`scarthgap` branch** (Yocto layer convention; the Dunfell-era `master`
|
||||
is untouched). Development continues on the local sibling checkout; push /
|
||||
fast-forward `scarthgap` as work lands.
|
||||
3. **forgefirm pushed** with the kas config and a `kas lock` lockfile pinning
|
||||
the upstream layers (poky, meta-openembedded, meta-freescale,
|
||||
meta-freescale-distro).
|
||||
4. **At release time**:
|
||||
- flip `meta-openglow` in `forgefirm-glowforge.yml` from the local-sibling
|
||||
block to the pinned-remote block (commented FUTURE block in the file);
|
||||
- refresh `kas lock`, tag all repos, and prove self-containment by building
|
||||
from a **fresh clone**.
|
||||
5. **GitHub release**: run `scripts/release.sh <version>` on the build
|
||||
host. It gates (version single-source, rootfs-vs-slot size,
|
||||
installer-embedded pubkey vs the signing key, factory-era fwup
|
||||
verification, and the **acceptance gate** - the committed
|
||||
`releases/v<version>/acceptance.json` from the bench campaign must
|
||||
authorize the built rootfs, `docs/ACCEPTANCE.md`), builds, packs and
|
||||
signs `forgefirm.fw`, stages the assets with `sha256sums.txt`, and
|
||||
prints the `gh release create` command. Assets and their exact names
|
||||
(the installer and the update manager download them verbatim):
|
||||
`forgefirm.fw`, `sha256sums.txt`,
|
||||
`forgefirm-image-glowforge.rootfs.wic.gz`, plus `acceptance.json` and
|
||||
`acceptance.md`. The release tag
|
||||
`v<version>` = `FORGEFIRM_RELEASE` = the rootfs `/etc/forgefirm-version`
|
||||
= the `.fw` meta-version; `release.sh` enforces the agreement.
|
||||
- **Every source repo is pinned.** `kernel-module-glowforge`,
|
||||
`python3-gfhardware`, `Glowforge-Utilities`, `grblHAL-glowforge` and
|
||||
`forgectrl` are fetched from GitHub at an exact `SRCREV`; there is no
|
||||
`AUTOREV` anywhere. When a source repo changes: push it, then bump the pin
|
||||
deliberately (BSP recipes in `meta-openglow`, ForgeFIRM components in
|
||||
`meta-forgefirm`) and re-verify with `bitbake -c fetch <recipe>`. A
|
||||
component's `SRCREV` (and the `PV` that moves with it) lives in
|
||||
`<recipe>-pin.inc` next to the recipe, and nothing else goes in that file:
|
||||
the image manifest leaves `*-pin.inc` out of the layer content hash, so a pin
|
||||
bump changes the component's fingerprint and only that
|
||||
(`docs/ACCEPTANCE.md`). A pin written into the recipe body still builds, but
|
||||
counts as a platform change and forces a full acceptance campaign.
|
||||
- **`meta-openglow` lives on its `scarthgap` branch** (Yocto layer convention;
|
||||
the Dunfell-era `master` is untouched). Development happens on the local
|
||||
sibling checkout; `scarthgap` is pushed as work lands.
|
||||
- **The upstream layers are locked** by `kas lock` (poky, meta-openembedded,
|
||||
meta-freescale, meta-freescale-distro); the lockfile is committed and
|
||||
refreshed deliberately.
|
||||
- **At release time**: flip `meta-openglow` in `forgefirm-glowforge.yml` from
|
||||
the local-sibling block to the pinned-remote block (the commented block in
|
||||
the file), refresh `kas lock`, tag all repos, and prove self-containment by
|
||||
building from a fresh clone. Then `scripts/release.sh <version>` gates
|
||||
(version single-source, rootfs-vs-slot size, installer-embedded pubkey vs the
|
||||
signing key, factory-era fwup verification, and the acceptance gate: the
|
||||
committed `releases/v<version>/acceptance.json` from the bench campaign must
|
||||
authorize the built rootfs, `docs/ACCEPTANCE.md`), builds, packs and signs
|
||||
`forgefirm.fw`, stages the assets with `sha256sums.txt`, and prints the
|
||||
`gh release create` command. Assets and their exact names (the installer and
|
||||
the update manager download them verbatim): `forgefirm.fw`,
|
||||
`sha256sums.txt`, `forgefirm-image-glowforge.rootfs.wic.gz`, plus
|
||||
`acceptance.json` and `acceptance.md`. The release tag `v<version>` =
|
||||
`FORGEFIRM_RELEASE` = the rootfs `/etc/forgefirm-version` = the `.fw`
|
||||
meta-version; `release.sh` enforces the agreement.
|
||||
|
||||
All recipes fetch their pinned revision from GitHub, so an image build is
|
||||
reproducible from the repos alone. For fast iteration on a source repo, bump
|
||||
its pin per iteration, or add a **local, untracked** `externalsrc` bbappend
|
||||
pointing at a working checkout — never commit one, or released images stop
|
||||
matching the pins.
|
||||
For fast iteration on a source repo, bump its pin per iteration, or add a
|
||||
**local, untracked** `externalsrc` bbappend pointing at a working checkout;
|
||||
never commit one, or released images stop matching the pins.
|
||||
|
||||
## Scarthgap migration backlog
|
||||
## Build-time facts
|
||||
|
||||
The kas scaffold + `LAYERSERIES_COMPAT` bumps let the layers be *selected* under
|
||||
Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until:
|
||||
- `ACCEPT_FSL_EULA = "1"` is set in the kas config: the image carries NXP's
|
||||
VPU firmware blob, and `firmware-imx-lic` ships the EULA text beside it.
|
||||
- Every `LICENSE` string in the layers (`meta-forgefirm`, `meta-glowforge-bsp`,
|
||||
`meta-openglow-core`) is SPDX; recipes for third-party components with more
|
||||
than one license (`wlconf`, `python3-gfhardware`) declare each with a
|
||||
checksum on its license text.
|
||||
- `forgefirm-image-dev` is a strict superset of `forgefirm-image` (the bench
|
||||
image, `docs/ACCEPTANCE.md`); every build produces both.
|
||||
- The kernel is `linux-fslc` with the board's device tree, config fragment and
|
||||
layer patches in `meta-openglow/meta-glowforge-bsp/recipes-kernel/linux/`;
|
||||
the bbappend header lists the patches and `glowforge.cfg` documents the
|
||||
config. The bootloader recipe is `u-boot_2020.01.bb` in `recipes-bsp`.
|
||||
|
||||
1. ~~**Override-syntax migration**~~ — **DONE.** All `_append`/`_prepend`/
|
||||
`_remove`/`_${PN}` override syntax converted to the colon form across
|
||||
`meta-forgefirm`, `meta-openglow-core`, and `meta-glowforge-bsp` (22
|
||||
occurrences).
|
||||
2. **Kernel forward-port (4.14 → linux-fslc 6.12.20)** — the factory NXP vendor
|
||||
kernel (linux-imx 4.14.98) carried 7 out-of-tree changes; these are re-derived
|
||||
against mainline 6.12 in `meta-glowforge-bsp/recipes-kernel/linux/linux-fslc_%.bbappend`
|
||||
(the forward-port landing zone), **not** re-applied as the 4.14 patches.
|
||||
- **Foundation — DONE.** `linux-fslc` 6.12.20 builds for `glowforge` with a
|
||||
ported device tree (`glowforge.dts` + `openglow_common.dtsi` overlaid into
|
||||
`arch/arm/boot/dts/nxp/imx/`, registered via a Makefile patch) and deploys
|
||||
`zImage` + `glowforge.dtb`. Boot-core + mainline-bound peripherals only.
|
||||
- **Free wins — DONE.** bus-freq disable *dropped* (no mainline busfreq);
|
||||
`st,lis2hh12` ×3 + `national,lm75b` + `ti,wl1805` + gpio keys/leds bind to
|
||||
mainline drivers; `reg-userspace-consumer` enabled via `glowforge.cfg`.
|
||||
(SPI-delay / PWM-prescaler dispositions are under Motion polish below.)
|
||||
- **Motion path — DONE and hardware-validated** (live-fed pulse stream,
|
||||
real gantry motion, laser fire). The whole chain forward-ports and
|
||||
compiles on 6.12:
|
||||
- **EPIT API**: `epit_api.c` in `arch/arm/mach-imx` (`CONFIG_MXC_EPIT_API`),
|
||||
in vmlinux, symbols exported; `&epit1/&epit2` in the DT.
|
||||
- **SDMA-expose**: re-created `dma-imx-sdma.h` + `0003-imx-sdma-*.patch`
|
||||
(un-static survivors, re-added the glowforge helpers, custom int-callback
|
||||
hook); expose symbols in `Module.symvers`.
|
||||
- **`glowforge.ko`**: ported across many 6.12 API changes
|
||||
(`tasklet_hrtimer`→soft hrtimer, `timer_setup`, LED-trigger API,
|
||||
`pwm_get`, `spi_delay`/`controller`, `filelock.h`, void `.remove`,
|
||||
1-arg i2c probe). Compiles + links, 0 undefined symbols; the recipe
|
||||
fetches the module by pinned `SRCREV`.
|
||||
- **DT**: `glowforge,cnc/thermal/pic/head` re-added with `pwms`/`pwm-names`
|
||||
phandles; `glowforge.dtb` compiles with all motion nodes.
|
||||
Motion polish: PWM prescaler (factory 1001) is **obsolete** — 6.12
|
||||
`pwm-imx27` auto-computes the prescaler from the requested period. The PIC
|
||||
inter-word SPI delay (factory 1005) is a **hardware-bring-up TODO**: 6.12
|
||||
`spi-imx.c` has no `PERIODREG`/`word_delay` programming, so re-derive it in
|
||||
`spi_imx_setupxfer` (write `MX51_ECSPI_PERIODREG` from `t->word_delay`,
|
||||
guarded to ECSPI) and verify the wait-states on a scope. pic.c keeps the
|
||||
inter-transfer delay meanwhile. The factory `glowforge,imx-pwm-audio`
|
||||
(buzzer) driver is not part of ForgeFIRM.
|
||||
- **Camera — DONE and hardware-validated.** The factory
|
||||
`ov5648_mipi.c` (NXP's removed `v4l2_int_device`/`mxc_v4l2_capture`) is
|
||||
replaced by the mainline `ovti,ov5648` subdev + imx6 `imx-media` (IPU CSI)
|
||||
+ `imx6-mipi-csi2` receiver. The factory CAM_SEL MIPI switch is modeled
|
||||
with the mainline `video-mux` (gpio-mux on `gpio7 10`): both sensors →
|
||||
video-mux → `mipi_csi` → IPU CSI. Sensor `xvclk` is the board's 24 MHz
|
||||
fixed oscillator (matching the factory DTB); avdd/dovdd/dvdd rails are in
|
||||
the DT. Both cameras stream live through forgectrl (MJPEG at 15 fps with
|
||||
VPU JPEG encode, full-resolution snapshots, mux arbitration).
|
||||
**HD units (8 MP OV8856) — code complete, UNTESTED.** The DT lists both
|
||||
`ovti,ov5648` (5 MP) and `ovti,ov8856` (8 MP) at 0x36 so one image covers
|
||||
both, and the driver matching the chip ID wins. Everything the OV8856
|
||||
needs is in the build: patch 0011 gives it the `get_mbus_config` the
|
||||
IPU-CSI hard-fails without (the same gap 0006 closes for ov5648), patch
|
||||
0012 retunes both PLL multipliers for the board's 24 MHz xvclk (mainline's
|
||||
tables are written for 19.2 MHz, which would run the link 25 % above the
|
||||
frequency the driver publishes), patch 0013 adds the 2-lane RAW8 modes
|
||||
(below), the endpoint's `link-frequencies` list carries the driver's whole
|
||||
2-lane menu (it rejects the endpoint outright if any entry is missing —
|
||||
the old list omitted 720 MHz, so probe would have failed), and forgectrl
|
||||
and gfhardware pick geometry and the sensor's control set from whichever
|
||||
driver bound.
|
||||
|
||||
The capture mode is the **full 3264×2448**, reached in RAW8. The
|
||||
sensor's stock RAW10 full-resolution 2-lane mode asks for 1.44 Gbps/lane
|
||||
and the i.MX6 CSI-2 D-PHY stops at 1 Gbps (`hsfreq_map` in
|
||||
`imx6-mipi-csi2.c` ends at 1000 Mbps and `max_mbps_to_hsfreqrange_sel()`
|
||||
returns `-EINVAL` above it), so `imx6-mipi-csi2` refuses to program it —
|
||||
but 8-bit samples carry the same frame at half the rate, which puts it on
|
||||
the 360 MHz link the binned modes already use, at 180 Mpx/s and 15 fps.
|
||||
Patch 0013 builds those modes from mainline's own 4-lane 3264×2448 and
|
||||
1632×1224 register lists plus a per-mode delta list: `0x3018` for two
|
||||
lanes, `0x3031` for 8-bit readout, and double the HTS because half the
|
||||
lanes carry half a line in the same time. It also makes the sample depth a
|
||||
mode property, so pixel rate, blanking and exposure ranges follow the mode
|
||||
instead of a fixed 10. Side effect worth having: the OV8856 path becomes
|
||||
byte-identical in shape to the OV5648's (8-bit BGGR, one byte per sample),
|
||||
and 3264 is a multiple of 32 so the NEON superpixel converter applies,
|
||||
which 1640 did not allow.
|
||||
|
||||
The values are the factory firmware's: its own OV8856 driver is RAW8-only
|
||||
and ships exactly these two resolutions over two lanes with the same
|
||||
`0x3018`/`0x3031` and the same HTS/VTS pairs. It reaches them through a
|
||||
different PLL divider chain (`0x0302=0x1e`, `0x0303=0x03`, `0x030f=0x07`,
|
||||
`0x0312=0x05`, `0x4837=0x58`) that halves the link again to 180 MHz and
|
||||
the internal SCLK with it — a self-consistent alternative, recorded in the
|
||||
patch header as the configuration to fall back to if the D-PHY will not
|
||||
lock at 720 Mbps/lane on real hardware.
|
||||
|
||||
Open, and only answerable on an 8 MP machine: whether it streams at all at
|
||||
720 Mbps/lane, and exposure/gain/white-balance commissioning — the OV8856
|
||||
driver publishes no red/blue balance controls, so white balance is
|
||||
uncorrected.
|
||||
3. **u-boot** — **DONE.** The `glowforge` u-boot is
|
||||
a standalone `u-boot_2020.01.bb` (Scarthgap's poky has no u-boot 2020.01
|
||||
base recipe to extend). It reuses poky's
|
||||
`u-boot-common.inc`/`u-boot.inc`, pins `SRCREV` to the upstream **v2020.01**
|
||||
tag with the matching `Licenses/README` md5, and overlays the glowforge board
|
||||
support + arch-Kconfig patch. **Builds clean under Scarthgap (GCC 13, no
|
||||
source fixes) and deploys `u-boot-glowforge.imx`.** Remaining: move
|
||||
`fw_printenv`/`fw_setenv` from `u-boot-fw-utils` to `libubootenv`
|
||||
(`PREFERRED_PROVIDER_u-boot-fw-utils` in `glowforge.inc`) when the rootfs needs
|
||||
them.
|
||||
4. **Device tree — DONE.** The `glowforge` `.dts` is validated against the
|
||||
linux-fslc 6.12 bindings and against the running board (motion, safety
|
||||
readbacks, cameras, sensors all bind and work).
|
||||
5. **Real-time strategy — decided.** The kernel runs
|
||||
`CONFIG_PREEMPT=y` (factory behavior; `imx_v6_v7_defconfig` alone gives only
|
||||
`PREEMPT_VOLUNTARY`). **PREEMPT_RT is not selectable on arm32 6.12** (no
|
||||
`ARCH_SUPPORTS_RT`) and is **not needed for the pulse feeder**. The
|
||||
argument is about queue depth, not ring size: the ring drains at 1 byte
|
||||
per EPIT tick (≤200 KB/s even at the 200 kHz ceiling), so the live
|
||||
feeder's bounded queue depth of ~150 ms — a few KB in flight — already
|
||||
rides out worst-case scheduling latency with orders of magnitude to spare
|
||||
(measured: 0.2 ms worst write latency under full CPU + I/O load; the
|
||||
underrun bench ran 100 kHz for 120 s with zero underruns). The ring
|
||||
itself is 32 MiB (the `ring_mb` module parameter, backed by the 32 MiB
|
||||
reserved pool, matching the factory ring): ~168 s of stream at 200 kHz,
|
||||
~56 min at the 10 kHz cloud-mode tick — a capacity that matters for the whole-job preload of
|
||||
cloud mode, not for latency. Bounded queue depth + `SCHED_FIFO` for the
|
||||
feeder is the design; revisit RT only if the underrun bench ever
|
||||
contradicts this arithmetic.
|
||||
|
||||
6. **gfui-client → forgectrl — DONE.** The stock `gfui-client` is excluded
|
||||
from `forgefirm-image` (`IMAGE_INSTALL:remove = "gfui-client"` in
|
||||
`meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb`). Its slot is
|
||||
filled by `forgectrl` (github.com/ScottW514/forgectrl — the machine-services
|
||||
daemon: web control panel, cameras, telemetry, settings, diagnostics,
|
||||
cooling engine, updates, and controller-mode supervision) plus the two
|
||||
controllers it supervises, `grblhal-glowforge` (Grbl over TCP:23) and
|
||||
`gfcloud` (the optional Glowforge web-service client, off unless selected).
|
||||
|
||||
---
|
||||
|
||||
**Image status:** `forgefirm-image` **builds end-to-end** on the forward-ported
|
||||
stack and deploys `forgefirm-image-glowforge.rootfs.wic.gz` (+ `zImage`,
|
||||
`glowforge.dtb`, `u-boot-glowforge.imx`) under `build/tmp/deploy/images/glowforge/`.
|
||||
Build-time prerequisites baked into the config: `ACCEPT_FSL_EULA = "1"` (NXP
|
||||
firmware-imx — the image also installs `firmware-imx-lic` so the EULA text
|
||||
ships beside the blobs) and the kernel default in `glowforge.conf`. Every
|
||||
`LICENSE` string in the layers (`meta-forgefirm`, `meta-glowforge-bsp`,
|
||||
`meta-openglow-core`) is SPDX, and the recipes for third-party components
|
||||
that carry more than one license (`wlconf`, `python3-gfhardware`) declare
|
||||
each of them with a checksum on its license text. The stack
|
||||
is hardware-validated end to end: motion timing, the laser and safety chain,
|
||||
the camera pipeline, both controller modes, and the A/B install path.
|
||||
Design facts (the pulse ring, real-time choices, hardware measurements) are in
|
||||
`docs/BRINGUP.md` ("Hardware facts bank") and
|
||||
`kernel-module-glowforge/UAPI.md`; bench status and open work are
|
||||
`docs/BRINGUP.md`; the dated record is `docs/CAMPAIGN-LOG.md`.
|
||||
|
||||
@@ -10,7 +10,7 @@ DESCRIPTION = "OpenGlow/ForgeFIRM image for Glowforge"
|
||||
# (gfui-client connects to Glowforge's servers). Removing it here (override
|
||||
# only; the shared glowforge-image base is untouched) also sidesteps its
|
||||
# do_package failure. Its role is filled locally by forgectrl and the
|
||||
# controllers below (kas/README.md backlog #6).
|
||||
# controllers below.
|
||||
IMAGE_INSTALL:remove = "gfui-client"
|
||||
|
||||
# grblhal-glowforge: the grblHAL motion controller (Grbl over TCP:23).
|
||||
@@ -46,7 +46,7 @@ IMAGE_INSTALL:append = " libegl-mesa libgles2-mesa libgbm mesa-megadriver"
|
||||
IMAGE_INSTALL:append = " firmware-imx-lic"
|
||||
|
||||
# The release rootfs must fit a 200 MiB factory eMMC slot (409600 blocks).
|
||||
# Sizing: content + 40 MiB working space, hard-capped at the slot size —
|
||||
# Sizing: content + 40 MiB working space, hard-capped at the slot size:
|
||||
# the build fails rather than emit an unflashable image. The raw ext4 is
|
||||
# deployed alongside the wic; scripts/mkfw.sh packs it into the signed
|
||||
# .fw release artifact.
|
||||
|
||||
Reference in New Issue
Block a user