Skip to content

tooling: Add make daplink-firmware and make daplink-deploy targets.#387

Merged
nedseb merged 6 commits intomainfrom
tooling/daplink-firmware
Apr 13, 2026
Merged

tooling: Add make daplink-firmware and make daplink-deploy targets.#387
nedseb merged 6 commits intomainfrom
tooling/daplink-firmware

Conversation

@nedseb
Copy link
Copy Markdown
Contributor

@nedseb nedseb commented Apr 13, 2026

Closes #377

Summary

Add Makefile targets to build and flash the DAPLink interface firmware from source. The DAPLink firmware runs on the STM32F103 interface chip and provides the I2C bridge, mass-storage, and CMSIS-DAP debug interface.

DAPLink has two parts

  • Bootloader (first stage, 0x08000000) — installed once at the factory, rarely updated. Provides the MAINTENANCE mode used to update the interface firmware. Updating the bootloader requires an external SWD probe and is not covered by these targets.
  • Interface firmware (second stage, 0x08002000) — the routine update target. This is what these targets manage.

A future daplink-deploy-bootloader target could be added if needed, with pyocd/openocd flash via external probe.

New targets

Target Description
make daplink-firmware Clone steamicc/DAPLink and build stm32f103xb_steami32_if
make daplink-update Refresh the DAPLink clone
make daplink-deploy Flash DAPLink interface firmware (default: usb mass-storage)
make daplink-deploy-usb Flash interface firmware via MAINTENANCE volume
make daplink-clean Clean DAPLink build artifacts

Maintenance mode

To flash the DAPLink interface firmware, the board must be in maintenance mode:

  1. Power on the board with the RESET button held
  2. A MAINTENANCE USB volume appears (instead of the usual STeaMi volume)
  3. make daplink-deploy-usb copies the firmware to that volume
  4. The board reboots automatically with the new interface firmware

Implementation

Source repo

steamicc/DAPLink (branch release_letssteam).

Build

make daplink-firmware clones the repo into .build/DAPLink/, creates a Python virtualenv inside it, installs requirements.txt, then runs:

python tools/progen_compile.py -t make_gcc_arm stm32f103xb_steami32_if

The output stm32f103xb_steami32_if_crc.bin lands in projectfiles/make_gcc_arm/stm32f103xb_steami32_if/build/.

deploy_usb.py refactoring

scripts/deploy_usb.py is now generic over volume label. It accepts --label (default STeaMi) and provides a contextual error message when the MAINTENANCE volume is not found, instructing the user to enter maintenance mode.

Devcontainer

Added ccache and ninja-build to the Dockerfile (required by progen for the DAPLink build).

Documentation

  • New "DAPLink firmware" subsection in CONTRIBUTING.md with the bootloader/interface distinction
  • Updated prerequisites (ccache, ninja-build, dual usage of arm-none-eabi-gcc)
  • Updated devcontainer description
  • Documented maintenance mode procedure

Test plan

  • make help shows all 5 daplink-* targets
  • make test — 349 mock tests pass
  • scripts/deploy_usb.py --label MAINTENANCE shows the maintenance mode error message
  • make daplink-firmware builds successfully (requires ccache/ninja-build)
  • make daplink-deploy-usb flashes correctly on hardware in MAINTENANCE mode

Copilot AI review requested due to automatic review settings April 13, 2026 03:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a DAPLink firmware build/flash workflow to the repo’s tooling so contributors can rebuild and deploy the STM32F103 “interface chip” firmware from source, similar to the existing MicroPython firmware targets.

Changes:

  • Introduce daplink-* Makefile targets to clone/build/update/deploy/clean DAPLink firmware artifacts.
  • Add DAPLink build configuration variables in env.mk.
  • Update developer docs and devcontainer packages (ccache, ninja-build) to support the DAPLink build toolchain.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
Makefile Adds daplink-firmware, daplink-update, daplink-deploy*, daplink-clean targets and clone/build logic.
env.mk Adds DAPLINK_* variables and moves BUILD_DIR earlier for shared use.
CONTRIBUTING.md Documents new prerequisites and the new DAPLink firmware workflow/targets.
.devcontainer/Dockerfile Installs ccache and ninja-build required for DAPLink progen builds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Makefile Outdated
daplink-deploy: daplink-deploy-usb ## Flash DAPLink firmware (default: usb mass-storage)

.PHONY: daplink-deploy-usb
daplink-deploy-usb: ## Flash DAPLink firmware via DAPLink USB mass-storage (routine update)
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

daplink-deploy/daplink-deploy-usb have no prerequisites, so on a fresh checkout make daplink-deploy won’t even clone DAPLink (and will fail with a missing .bin path). For parity with micropython-deploy-usb (which depends on $(MPY_DIR)), consider making the deploy targets depend on $(DAPLINK_DIR) and/or the built artifact (e.g., daplink-firmware) so make daplink-deploy works end-to-end.

Suggested change
daplink-deploy-usb: ## Flash DAPLink firmware via DAPLink USB mass-storage (routine update)
daplink-deploy-usb: daplink-firmware ## Flash DAPLink firmware via DAPLink USB mass-storage (routine update)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: daplink-deploy-usb now depends on $(DAPLINK_DIR) (parity with micropython-deploy-usb which depends on $(MPY_DIR)). On a fresh checkout, make daplink-deploy will clone the DAPLink repo first. The script then handles the missing-firmware case with a clear error message pointing to make daplink-firmware.

Comment thread Makefile Outdated
Comment on lines +226 to +227
daplink-deploy-usb: ## Flash DAPLink firmware via DAPLink USB mass-storage (routine update)
@$(PYTHON) scripts/deploy_usb.py $(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

daplink-deploy-usb calls scripts/deploy_usb.py, which is written for MicroPython deploys (hardcoded volume label STeaMi, and on missing firmware it prints “Run 'make micropython-firmware' first.”). When used for DAPLink, failures will surface misleading instructions. Consider parameterizing the script (label + “run make … first” hint) or adding a DAPLink-specific USB deploy script.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: scripts/deploy_usb.py is now generic. It accepts --label (default STeaMi, MAINTENANCE for daplink) and --build-target (default micropython-firmware, can be daplink-firmware for daplink) so the missing-firmware error message points to the right make target depending on the context.

@nedseb nedseb force-pushed the tooling/daplink-firmware branch from dba854a to 35a319d Compare April 13, 2026 03:47
nedseb added 4 commits April 13, 2026 06:13
…build.

The DAPLink build failed on systems without a `python` symlink (Debian/Ubuntu
ship only `python3`) because the generated Makefile invokes
`tools/pre_build_script.py` via its `#!/usr/bin/env python` shebang, which
silently skips `version_git.h` generation. It also failed on modern toolchains
(arm-none-eabi-gcc >= 11) because the firmware overflows the m_text region;
DAPLink's docs pin gcc-arm-none-eabi to 10.3-2021.10.

Prepend the DAPLink venv to PATH so `python` resolves, and download the pinned
toolchain into .build/ on first build, using it only for daplink-firmware.
…ses.

Mirror the existing micropython-firmware release job: build the DAPLink
interface firmware after each semantic-release publish and upload the
CRC-signed .bin / .hex (the artifacts users flash via MAINTENANCE mode).
…uilds.

Caches the pinned gcc-arm-none-eabi 10.3-2021.10 toolchain (~150 MB) and
the DAPLink Python virtualenv (~250 MB) between release runs. The toolchain
key is fixed (the version is pinned), and the venv key is derived from
requirements.txt so it invalidates when DAPLink upstream bumps a dependency.
Three issues raised on PR #387:

1. Toolchain download was hard-coded to x86_64-linux. Detect uname and pick
   the matching ARM-published archive (Linux x86_64, Linux aarch64, Intel
   macOS). Fail with a clear message — and a pointer to the dev container —
   on Apple Silicon and Windows, where 10.3-2021.10 is not published.

2. Deprecated `deploy-pyocd` / `deploy-openocd` advertised non-existent
   `daplink-deploy-pyocd` / `daplink-deploy-openocd` targets. Suggest only
   the `micropython-` variant until the SWD-based DAPLink targets land
   (tracked in #388).

3. The DAPLink venv was created once and never refreshed, so a
   `daplink-update` that bumped requirements.txt would silently reuse stale
   dependencies. Replace the manual check with a sentinel file that depends
   on requirements.txt, so pip install re-runs whenever upstream bumps a dep.

Also document the platform constraint in CONTRIBUTING.md.
@nedseb nedseb requested a review from Copilot April 13, 2026 04:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Makefile
Comment on lines +231 to +239
# Sentinel: re-runs pip install whenever DAPLink's requirements.txt changes
# (e.g. after `make daplink-update`).
$(DAPLINK_DIR)/venv/.installed: $(DAPLINK_DIR)/requirements.txt
@echo "Setting up DAPLink Python virtualenv..."
@if [ ! -x "$(DAPLINK_DIR)/venv/bin/python" ]; then \
$(PYTHON) -m venv $(DAPLINK_DIR)/venv; \
fi
$(DAPLINK_DIR)/venv/bin/pip install -r $(DAPLINK_DIR)/requirements.txt
@touch $@
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$(DAPLINK_DIR)/venv/.installed depends on $(DAPLINK_DIR)/requirements.txt, but there’s no rule that produces requirements.txt and it won’t exist until after $(DAPLINK_DIR) is cloned. With parallel builds (make -j) or different prerequisite evaluation order on a fresh checkout, this can fail with “No rule to make target '.../requirements.txt'”. Add an explicit (preferably order-only) dependency on $(DAPLINK_DIR) for the .installed sentinel (and/or for $(DAPLINK_DIR)/requirements.txt) so the clone always happens before venv setup.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e8e001f: added an order-only prerequisite | $(DAPLINK_DIR) on the .installed sentinel so the clone runs before make checks requirements.txt, even on a fresh checkout or under make -j.

On a fresh checkout, $(DAPLINK_DIR)/venv/.installed depended on
requirements.txt without any rule to produce it before the clone, so make
would fail with "No rule to make target requirements.txt" (especially under
parallel builds). Add an order-only prerequisite on $(DAPLINK_DIR) so the
clone always runs first and the file exists when make resolves the sentinel.
@nedseb nedseb merged commit 118cf2a into main Apr 13, 2026
9 checks passed
@nedseb nedseb deleted the tooling/daplink-firmware branch April 13, 2026 04:38
@semantic-release-updater
Copy link
Copy Markdown

🎉 This PR is included in version 0.16.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

nedseb added a commit that referenced this pull request Apr 13, 2026
Rename the firmware job to micropython-firmware so it does not clash with the daplink-firmware job introduced in #387.
nedseb added a commit that referenced this pull request Apr 13, 2026
Rename the firmware job to micropython-firmware so it does not clash with the daplink-firmware job introduced in #387.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tooling: Add make daplink-firmware and make daplink-deploy targets.

2 participants