Files
forgefirm/kas
ScottW514 fcf183eefd release.sh: the release pipeline
Gates (clean tree, version single-source across FORGEFIRM_RELEASE /
rootfs stamp / .fw meta-version / tag, rootfs-vs-slot size with early
warning, installer-embedded pubkey must match the signing key,
factory-era fwup verification of the packed archive), then build,
pack, sign, checksum, and stage forgefirm.fw + sha256sums.txt +
forgefirm-image-glowforge.rootfs.wic.gz with the gh publish command
(--publish runs it where gh is authenticated). release.sh --dev packs
a dev-key-signed forgefirm-dev.fw from the release rootfs for the GUI
upload path. Signing keys are always passed explicitly - no defaults.
kas/README release order and the plan doc updated to match.
2026-08-08 13:28:17 -04:00
..
2026-08-08 13:28:17 -04:00

Building ForgeFIRM with kas

The forgefirm repo is the base of the project: it controls the build, the resulting firmware images land here, and all build/install docs live here. It uses kas to manage Yocto layers and drive the build.

Baseline

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)
Distro forgefirm
Image forgefirm-image

Layout

openglow-forgefirm/
├── forgefirm/                 ← THIS repo, the base
│   ├── kas/
│   │   ├── forgefirm-glowforge.yml   ← build entry point
│   │   └── README.md                 ← this file
│   ├── meta-forgefirm/        ← the forgefirm layer (this repo)
│   ├── BUILD.md / INSTALL.md / SERIAL.md
│   ├── layers/                ← kas-cloned upstreams (gitignored)
│   ├── build/                 ← bitbake output incl. images (gitignored)
│   ├── downloads/ sstate-cache/       ← caches (gitignored)
│   └── .gitignore
├── meta-openglow/             ← Glowforge BSP layers (local sibling, under migration)
└── kernel-module-glowforge/   ← glowforge.ko sources (pulled by recipe SRC_URI)

meta-openglow is referenced as a local sibling (../meta-openglow) while we migrate it to Scarthgap, so its in-place edits are what gets built. Once that migration is committed/pushed, flip it to a kas-cloned + pinned repo (commented block in forgefirm-glowforge.yml) and the forgefirm repo becomes fully self-contained.

Prerequisites

A Linux build host, or WSL2 on Windows (officially supported by Yocto).

WSL2 note: keep this whole tree on the WSL2 native ext4 filesystem (e.g. ~/dev/openglow-forgefirm), not under /mnt/c/.... The Windows mount breaks case-sensitivity/permissions and is very slow for Yocto. Give the WSL2 VM plenty of RAM and disk in .wslconfig.

pipx install kas      # or: pip install kas

Build

Run from the forgefirm repo root so outputs land inside it:

cd forgefirm
kas build  kas/forgefirm-glowforge.yml     # fetch layers + full build
kas shell  kas/forgefirm-glowforge.yml     # interactive bitbake environment
kas dump   kas/forgefirm-glowforge.yml     # print the resolved config

The bootable image lands in build/tmp/deploy/images/glowforge/. Flashing / dual-boot install steps are in ../INSTALL.md.

Container build (optional, reproducible host)

cd forgefirm
kas-container build kas/forgefirm-glowforge.yml

Pinning exact versions (reproducible builds)

The config tracks the scarthgap branch of each upstream layer. To lock every layer to an exact commit:

kas lock kas/forgefirm-glowforge.yml      # writes kas/forgefirm-glowforge.lock.yml

kas auto-loads the lockfile on subsequent runs. Commit it; refresh deliberately.

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. kernel-module-glowforge (029dfb6) and python3-gfhardware (9bf31fd) pushed to GitHub master; both recipes in meta-openglow pin those exact SRCREVs (no AUTOREV anywhere). Whenever either repo changes: push it, then bump the recipe SRCREV in meta-openglow deliberately.
  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);
    • drop meta-openglow's kernel-module-glowforge.bbappend (externalsrc to the local sibling — its perl-native DEPENDS is already carried in the base recipe) so a fresh clone is fully self-contained;
    • 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), 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. The release tag v<version> = FORGEFIRM_RELEASE = the rootfs /etc/forgefirm-version = the .fw meta-version; release.sh enforces the agreement.

For gfhardware development, either bump the recipe pin per iteration or add a tracked externalsrc bbappend mirroring the kernel-module pattern.

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). Note: meta-openglow-bsp (the separate OpenGlow_std board, not built here) was intentionally left unconverted.

  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 (builds clean; runtime needs hardware). 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. Built from the local sibling via an externalsrc bbappend during migration.
      • 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. glowforge,imx-pwm-audio (buzzer) deferred.
    • Camera — DONE (builds clean; runtime needs hardware). 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 modelled with the mainline video-mux (gpio-mux on gpio7 10): both ov5648s → video-mux → mipi_csi → IPU CSI. Sensor xvclk (25 MHz fixed-clock) + avdd/dovdd/dvdd rails in the DT. All drivers build as modules and glowforge.dtb compiles with the full pipeline. HW bring-up: confirm the real supply rails, CSI-2 lane count/order and CAM_SEL polarity, then validate with media-ctl + a v4l2 capture.
  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 — revalidate the glowforge .dts against the linux-fslc 6.12 DT bindings (paired with the kernel forward-port in #2).

  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 SDMA ring is 128 MiB draining at 1 byte per EPIT tick — ≤200–400 KB/s even at the 200 kHz ceiling — so a full ring holds ~5–11 minutes of stream and a modest 1 MiB of queued data already rides out ~3–5 s of scheduling latency, orders of magnitude beyond anything PREEMPT exhibits. Deep buffering + SCHED_FIFO for the feeder is the design; revisit RT only if the underrun bench ever contradicts this arithmetic. (Bench: 5 s of continuous feed at a 1 s buffer depth, zero underruns.)

  6. gfui-client → forgectrl — the Glowforge cloud client is excluded from forgefirm-image (IMAGE_INSTALL:remove = "gfui-client" in meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb) — it connected to Glowforge's servers, the dependency ForgeFIRM exists to cut. Its slot is filled by forgectrl (github.com/ScottW514/forgectrl — the ForgeFIRM control daemon; camera MJPEG service today, hardware status/control and GRBL-vs-cloud mode selection planned) plus the grblhal-glowforge motion controller (both in the images with boot autostart).


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) and the kernel default in glowforge.conf. Everything compiles; on-hardware bring-up (motion timing, laser/safety chain, camera pipeline) is the remaining validation and needs a real board.