Skip to content

The alloy CLI

One command drives the whole workflow — scaffold, configure, build, flash, monitor, emulate, test, update. You never write CMake; it is generated into a throwaway .alloy/build-tree/.

Every command that takes a board accepts --board <id> to override alloy.toml for that invocation, and --project <dir> to work on a project other than the current directory. Commands marked JSON emit a versioned envelope on stdout for editors and scripts.

Start a project

Command What it does
alloy new <name> --board <id> scaffold from a curated board (the scaffold is the CI-built blink)
alloy new <name> --chip <id> [--clock <profile>] scaffold a clean, editable board for any MCU in the database
alloy boardsJSON list curated boards
alloy chips [--vendor st]JSON list every MCU you can scaffold for
alloy set-board <id> change the board in alloy.toml
alloy setup [--check] [--family <f>] verify / install cross-toolchains into ~/.alloy/tools
$ alloy new blinky --board rp2040_zero && cd blinky
$ alloy run                 # build → flash → open the monitor

Build and run

Command What it does
alloy build generate + render CMake + compile
alloy flash build + program the board
alloy monitor [--json] bidirectional serial monitor; --json streams NDJSON and needs no terminal
alloy run flash + monitor
alloy gen regenerate .alloy/generated from the device database
alloy clean [--all] remove per-board build trees
alloy test [--no-sanitize] build + run the host unit tests (ASan/UBSan on by default)

build, flash, run and gen share two placement flags:

  • --ram links every section into RAM and loads it with the debugger — no flash erase, for fast iteration.
  • --slot bl|a|b links into the bootloader region or an A/B firmware slot. See Firmware update.

Retarget with a single flag — same source, different silicon:

$ alloy build --board nucleo_g071rb
$ alloy build --board esp32_devkit

Inspect what you built

Command What it does
alloy sizeJSON flash/RAM of the last build against the chip's real memories, and whether the image fits its update slot
alloy matrix [--boards a,b]JSON build this project for every board and table the result
alloy frame-audit coroutine frame sizes vs the task_storage<N> you declared
alloy svd [--chip <id>] [-o out] write a CMSIS-SVD file so a debugger can show peripheral registers
alloy debug-infoJSON debug-server facts for an IDE launch config
$ alloy size
flash      2340 /   131072 B  (1.8%)
ram        2184 /    36864 B  (5.9%)
slot_a          47104 B  image 2852 B — fits
slot_b          47104 B  image 2852 B — fits

alloy matrix is the portability claim, executable — one table, one src/, no #ifdef:

$ alloy matrix --boards nucleo_g071rb,nucleo_f722ze,same70_xplained
board                       flash               ram  time
nucleo_g071rb       2.3K / 128.0K      2.1K / 36.0K  8.1s
nucleo_f722ze       2.6K / 512.0K     2.4K / 256.0K  1.9s
same70_xplained     2.5K / 2048.0K     2.3K / 384.0K  1.3s

3 of 3 boards — the same src/, no #ifdef

A board that fails to build does not end the sweep — it gets a row with the reason, so one table tells you what fits where.

Configure a board

Command What it does
alloy board-info [<id>]JSON roles, capabilities, used pins and problems of any board
alloy board-validate [<id>] [--file f\|-]JSON every problem located, with the pins that would work
alloy board-clone <src> <new> copy a curated board into your project as an editable one
alloy chip-info <chip> — JSON clock profiles, pin map with alternate functions, peripherals, role catalogue
alloy clock --chip <id> --mhz <n> [--hse <mhz>] — JSON solve a PLL for a target frequency
alloy clock --chip <id> --graph [--profile p]JSON the whole clock: sources, buses, and what each peripheral is fed

board-validate --file - reads a candidate board.json from stdin, which is how an editor checks a configuration before writing it. These verbs are what the VS Code configurator is built on — it holds no chip knowledge of its own.

The whole clock, not just the PLL

--graph answers the question that comes after "what frequency": where the clock goes. Bus prescalers, and the kernel clock each peripheral is actually fed — with what that implies.

$ alloy clock --chip st/stm32f767 --graph --profile pll_180mhz
SYSCLK 180 MHz → AHB ÷1 180 MHz → eth
                 APB ÷4  45 MHz → usart3   115200 baud → 115089 (0.10% error)
                 APB2 ÷2 90 MHz

The baud error is computed with the driver's own rounding, so it is the error you will measure, not a different one. Peripherals whose feed the chip data does not state (an independent watchdog on its own oscillator) are listed as unplaceable rather than left out.

Emulate

Command What it does
alloy emulate [--emit-only] run the firmware headless in Renode on a data-generated platform
$ alloy emulate --board nucleo_f722ze
alloy uart_echo ready

The platform is generated from the same chip data the firmware compiles against, so the emulated memory map and UART cannot drift from the real target. See Emulation.

Update a device in the field

Command What it does
alloy keygen [-o key] generate an Ed25519 update-signing keypair
alloy image <app.elf\|bin> --set-version <n> [--sign key] pack an update image
alloy portsJSON list serial ports
alloy update [--image-a a.img] [--image-b b.img] --port <p> stream an image into the device's inactive slot
$ alloy image build/app.elf --set-version 3 --sign keys/update.key -o app_b.img
$ alloy update --image-b app_b.img --port /dev/ttyUSB0

The full story — slots, trial boots, rollback, signing — is in Firmware update.

Give the project its own CI

Command What it does
alloy ci-init [--boards a,b] [--all] [--force] write a GitHub Actions workflow for this project

The framework's CI is what keeps every board building; a project built on alloy starts with nothing. This writes the equivalent — validate the boards it defines, then build its sources for each board it targets:

$ alloy ci-init
wrote .github/workflows/alloy.yml
  builds on: nucleo_g071rb

It targets what the project targets, not every supported board; --all widens it. Toolchain steps follow the same rule (an ARM-only project gets no ESP download) and are pinned to the versions alloy itself is tested against.

Libraries

Command What it does
alloy lib listJSON browse the driver registry
alloy lib search <text> filter by name, summary or category
alloy lib info <name> manifest + required concepts
alloy lib add <name> vendor into ./libs and wire the build

See Driver libraries.

Editor integration

  • The --json envelopes are versioned (alloy.boards.v1, alloy.board_info.v1, alloy.chip_info.v1, alloy.size.v1, …). Tools check the schema string and fail loudly rather than misreading a newer CLI.
  • .alloy/build-tree/ carries compile_commands.json, so clangd and IntelliSense work with no setup.
  • The VS Code extension wraps all of the above.