Skip to content

Commit e46dc7b

Browse files
authored
tooling(daplink_bridge): Add SWD-based DAPLink flash targets. (#394)
Closes #388. New Make targets for flashing the DAPLink chip via an external SWD probe: daplink-bootloader Build stm32f103xb_bl (bootloader, 47 KB at 0x08000000) daplink-deploy-pyocd Flash interface firmware via SWD (pyocd, no MAINTENANCE) daplink-deploy-openocd Flash interface firmware via SWD (openocd) daplink-deploy-bootloader Alias for bootloader-pyocd daplink-deploy-bootloader-pyocd Flash bootloader via SWD (pyocd) daplink-deploy-bootloader-openocd Flash bootloader via SWD (openocd) The OpenOCD sequence mirrors steamicc/DapLink-EasyFlash: ST-Link hla_swd transport, `stm32f1x unlock 0` before `program verify` so locked chips can be recovered. pyOCD uses the built-in `stm32f103rc` target (flash-compatible with the STM32F103CB on the DAPLink chip for the lower 128 KB). Every SWD target prints a bootstrap warning explaining that a board cannot reflash its own on-board DAPLink via its own SWD pins — an EXTERNAL probe is required on the target's SWD header. CONTRIBUTING.md documents the new targets, the bootstrap constraint, and the override variables (DAPLINK_OPENOCD_INTERFACE, DAPLINK_OPENOCD_TRANSPORT, DAPLINK_PYOCD_TARGET) for non-ST-Link probes. Since daplink-deploy-pyocd and daplink-deploy-openocd now exist, the short-name deprecation messages for `deploy-pyocd` / `deploy-openocd` are reverted to the standard DEPRECATED_FIRMWARE form that points to both the micropython- and daplink- variants. The DEPRECATED_MICROPYTHON_ONLY macro is removed. Verified locally: - make daplink-bootloader → builds stm32f103xb_bl_crc.bin (Start 0x8000000, Length 0xbc00, CRC32 0x564aa44f) - make help lists the five new targets - make -n on each SWD target produces the expected pyocd / openocd command with the warning prelude and the correct base address - make lint / make test-mock still pass Hardware verification pending: flashing with a real ST-Link on a STeaMi board must be done before merging.
1 parent a9df44c commit e46dc7b

3 files changed

Lines changed: 106 additions & 23 deletions

File tree

CONTRIBUTING.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,20 +226,34 @@ DAPLink is the firmware running on the STM32F103 interface chip. It provides the
226226

227227
DAPLink consists of **two parts**:
228228

229-
- **Bootloader** (first stage, flashed at `0x08000000`) — installed once at the factory, rarely updated. It 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.
230-
- **Interface firmware** (second stage, flashed at `0x08002000`) — the part that contains the I2C bridge, mass-storage, debug interface, and is updated routinely. This is what the `daplink-*` Makefile targets manage.
229+
- **Bootloader** (first stage, flashed at `0x08000000`) — installed once at the factory, rarely updated. It provides the MAINTENANCE mode used to update the interface firmware. Updating it requires an external SWD probe.
230+
- **Interface firmware** (second stage, flashed at `0x08002000`) — contains the I2C bridge, mass-storage, debug interface. Updated routinely, either via the MAINTENANCE USB volume or via an external SWD probe for recovery.
231231

232232
```bash
233-
make daplink-firmware # Clone steamicc/DAPLink and build stm32f103xb_steami32_if
234-
make daplink-update # Refresh the DAPLink clone
235-
make daplink-deploy # Flash DAPLink interface firmware (default: usb mass-storage)
236-
make daplink-deploy-usb # Flash DAPLink interface firmware via MAINTENANCE volume
237-
make daplink-clean # Clean DAPLink build artifacts
233+
make daplink-firmware # Build the interface firmware (stm32f103xb_steami32_if)
234+
make daplink-bootloader # Build the bootloader (stm32f103xb_bl)
235+
make daplink-update # Refresh the DAPLink clone
236+
make daplink-clean # Clean DAPLink build artifacts
237+
238+
# Routine interface update (no external probe)
239+
make daplink-deploy # Alias for daplink-deploy-usb
240+
make daplink-deploy-usb # Flash interface firmware via MAINTENANCE volume
241+
242+
# External SWD probe required (recovery, CI, bricked boards)
243+
make daplink-deploy-pyocd # Flash interface firmware via SWD (pyocd)
244+
make daplink-deploy-openocd # Flash interface firmware via SWD (openocd)
245+
make daplink-deploy-bootloader # Flash bootloader via SWD (default: pyocd)
246+
make daplink-deploy-bootloader-pyocd # Flash bootloader via SWD (pyocd)
247+
make daplink-deploy-bootloader-openocd # Flash bootloader via SWD (openocd)
238248
```
239249

240250
The DAPLink source is cloned from [steamicc/DAPLink](https://github.com/steamicc/DAPLink) into `.build/DAPLink/` (gitignored). A Python virtualenv is created automatically inside the clone for the progen build tool.
241251

242-
**Maintenance mode:** to flash the DAPLink interface firmware, the board must be in maintenance mode. Power on the board with the RESET button held until a `MAINTENANCE` USB volume appears (instead of the usual `STeaMi` volume). The `make daplink-deploy-usb` target then copies the firmware to that volume and the board reboots automatically with the new interface firmware.
252+
**MAINTENANCE mode (USB path):** power on the board with the RESET button held until a `MAINTENANCE` USB volume appears (instead of the usual `STeaMi` volume). `make daplink-deploy-usb` copies the interface firmware to that volume and the board reboots automatically.
253+
254+
**External SWD probe (bootstrap warning):** the `daplink-deploy-*-pyocd` / `-openocd` and all `daplink-deploy-bootloader*` targets flash the DAPLink chip directly via SWD. They require an **external** probe (ST-Link, J-Link, or another CMSIS-DAP board) connected to the target board's SWD header. **A board cannot reflash its own on-board DAPLink via its own SWD pins** — use another board or a standalone probe. These paths are useful for recovering a bricked interface firmware, installing the bootloader at the factory, or automating CI flashing without manual button presses.
255+
256+
The SWD commands assume an ST-Link probe by default. To use a different probe (another DAPLink board, J-Link, …), override the OpenOCD or pyOCD configuration via `DAPLINK_OPENOCD_INTERFACE`, `DAPLINK_OPENOCD_TRANSPORT`, or `DAPLINK_PYOCD_TARGET` (see `env.mk`).
243257

244258
## Notes
245259

Makefile

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,6 @@ echo " make daplink-$(2) (DAPLink firmware)"; \
151151
exit 1
152152
endef
153153

154-
# Variant for short names whose DAPLink counterpart does not exist yet
155-
# (daplink-deploy-pyocd / daplink-deploy-openocd are tracked in #388).
156-
define DEPRECATED_MICROPYTHON_ONLY
157-
@echo "Error: 'make $(1)' has been renamed. Use:"; \
158-
echo " make micropython-$(2) (MicroPython firmware)"; \
159-
exit 1
160-
endef
161-
162154
.PHONY: firmware firmware-update firmware-clean deploy deploy-pyocd deploy-openocd deploy-usb
163155
firmware:
164156
$(call DEPRECATED_FIRMWARE,firmware,firmware)
@@ -169,9 +161,9 @@ firmware-clean:
169161
deploy:
170162
$(call DEPRECATED_FIRMWARE,deploy,deploy)
171163
deploy-pyocd:
172-
$(call DEPRECATED_MICROPYTHON_ONLY,deploy-pyocd,deploy-pyocd)
164+
$(call DEPRECATED_FIRMWARE,deploy-pyocd,deploy-pyocd)
173165
deploy-openocd:
174-
$(call DEPRECATED_MICROPYTHON_ONLY,deploy-openocd,deploy-openocd)
166+
$(call DEPRECATED_FIRMWARE,deploy-openocd,deploy-openocd)
175167
deploy-usb:
176168
$(call DEPRECATED_FIRMWARE,deploy-usb,deploy-usb)
177169

@@ -202,11 +194,41 @@ micropython-clean: ## Clean MicroPython firmware build artifacts
202194
fi
203195

204196
# --- DAPLink firmware ---
205-
# These targets manage the DAPLink **interface firmware** only (the second
206-
# stage of DAPLink, flashed at 0x08002000). The bootloader (first stage,
207-
# flashed at 0x08000000) is installed once at the factory and is not
208-
# managed here. A future `daplink-deploy-bootloader` target could be added
209-
# if needed, but it requires an external SWD probe and is rarely necessary.
197+
# These targets build and flash the DAPLink firmware that runs on the
198+
# STM32F103 interface chip. DAPLink has two parts:
199+
#
200+
# * Bootloader (first stage, `stm32f103xb_bl`) at 0x08000000
201+
# → rarely updated, requires an external SWD probe.
202+
# * Interface firmware (second stage, `stm32f103xb_steami32_if`) at 0x08002000
203+
# → updated routinely, typically via the MAINTENANCE USB volume,
204+
# or via an external SWD probe for recovery / bricked boards.
205+
#
206+
# SWD targets (`daplink-deploy-pyocd`, `daplink-deploy-openocd`,
207+
# `daplink-deploy-bootloader*`) need an EXTERNAL probe (ST-Link, J-Link, or
208+
# another CMSIS-DAP board) connected to the SWD header of the target board.
209+
# A board cannot reflash its own DAPLink chip via its own SWD pins.
210+
211+
define DAPLINK_SWD_WARNING
212+
@echo "================================================================"
213+
@echo "Warning: this target flashes the DAPLink chip via SWD."
214+
@echo "Requires an EXTERNAL probe (ST-Link / J-Link / CMSIS-DAP)"
215+
@echo "connected to the target board's SWD header. A board cannot"
216+
@echo "reflash its own on-board DAPLink via its own SWD pins."
217+
@echo "================================================================"
218+
endef
219+
220+
define DAPLINK_OPENOCD_FLASH
221+
openocd -f $(DAPLINK_OPENOCD_INTERFACE) \
222+
-f $(DAPLINK_OPENOCD_TARGET) \
223+
-c "transport select $(DAPLINK_OPENOCD_TRANSPORT)" \
224+
-c "reset_config none separate" \
225+
-c "init" \
226+
-c "reset halt" \
227+
-c "stm32f1x unlock 0" \
228+
-c "reset halt" \
229+
-c "program $(1) verify $(2)" \
230+
-c "reset; exit"
231+
endef
210232

211233
$(DAPLINK_DIR):
212234
@echo "Cloning DAPLink into $(CURDIR)/$(DAPLINK_DIR)..."
@@ -248,6 +270,14 @@ daplink-firmware: $(DAPLINK_DIR) $(DAPLINK_GCC_DIR)/bin/arm-none-eabi-gcc $(DAPL
248270
./venv/bin/python tools/progen_compile.py -t make_gcc_arm $(DAPLINK_TARGET)
249271
@echo "DAPLink firmware ready: $(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin"
250272

273+
.PHONY: daplink-bootloader
274+
daplink-bootloader: $(DAPLINK_DIR) $(DAPLINK_GCC_DIR)/bin/arm-none-eabi-gcc $(DAPLINK_DIR)/venv/.installed ## Build DAPLink bootloader for the STeaMi STM32F103
275+
@echo "Building DAPLink target $(DAPLINK_BL_TARGET) with gcc-arm-none-eabi $(DAPLINK_GCC_VERSION)..."
276+
cd $(CURDIR)/$(DAPLINK_DIR) && \
277+
PATH="$(CURDIR)/$(DAPLINK_GCC_DIR)/bin:$(CURDIR)/$(DAPLINK_DIR)/venv/bin:$$PATH" \
278+
./venv/bin/python tools/progen_compile.py -t make_gcc_arm $(DAPLINK_BL_TARGET)
279+
@echo "DAPLink bootloader ready: $(DAPLINK_BL_BUILD_DIR)/$(DAPLINK_BL_TARGET)_crc.bin"
280+
251281
.PHONY: daplink-update
252282
daplink-update: $(DAPLINK_DIR) ## Update the DAPLink clone
253283
@set -e
@@ -268,6 +298,33 @@ daplink-deploy-usb: $(DAPLINK_DIR) ## Flash DAPLink interface firmware via MAINT
268298
--build-target daplink-firmware \
269299
$(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin
270300

301+
.PHONY: daplink-deploy-pyocd
302+
daplink-deploy-pyocd: daplink-firmware ## Flash DAPLink interface firmware via external SWD probe (pyocd)
303+
$(DAPLINK_SWD_WARNING)
304+
$(PYTHON) -m pyocd flash -t $(DAPLINK_PYOCD_TARGET) \
305+
--base-address $(DAPLINK_FLASH_ADDR) \
306+
$(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin
307+
308+
.PHONY: daplink-deploy-openocd
309+
daplink-deploy-openocd: daplink-firmware ## Flash DAPLink interface firmware via external SWD probe (openocd)
310+
$(DAPLINK_SWD_WARNING)
311+
$(call DAPLINK_OPENOCD_FLASH,$(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin,$(DAPLINK_FLASH_ADDR))
312+
313+
.PHONY: daplink-deploy-bootloader
314+
daplink-deploy-bootloader: daplink-deploy-bootloader-pyocd ## Flash DAPLink bootloader via external SWD probe (default: pyocd)
315+
316+
.PHONY: daplink-deploy-bootloader-pyocd
317+
daplink-deploy-bootloader-pyocd: daplink-bootloader ## Flash DAPLink bootloader via external SWD probe (pyocd)
318+
$(DAPLINK_SWD_WARNING)
319+
$(PYTHON) -m pyocd flash -t $(DAPLINK_PYOCD_TARGET) \
320+
--base-address $(DAPLINK_BL_FLASH_ADDR) \
321+
$(DAPLINK_BL_BUILD_DIR)/$(DAPLINK_BL_TARGET)_crc.bin
322+
323+
.PHONY: daplink-deploy-bootloader-openocd
324+
daplink-deploy-bootloader-openocd: daplink-bootloader ## Flash DAPLink bootloader via external SWD probe (openocd)
325+
$(DAPLINK_SWD_WARNING)
326+
$(call DAPLINK_OPENOCD_FLASH,$(DAPLINK_BL_BUILD_DIR)/$(DAPLINK_BL_TARGET)_crc.bin,$(DAPLINK_BL_FLASH_ADDR))
327+
271328
.PHONY: daplink-clean
272329
daplink-clean: ## Clean DAPLink firmware build artifacts
273330
@if [ -d "$(DAPLINK_DIR)" ]; then \

env.mk

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ DAPLINK_BRANCH ?= release_letssteam
1616
DAPLINK_DIR ?= $(BUILD_DIR)/DAPLink
1717
DAPLINK_TARGET ?= stm32f103xb_steami32_if
1818
DAPLINK_BUILD_DIR ?= $(DAPLINK_DIR)/projectfiles/make_gcc_arm/$(DAPLINK_TARGET)/build
19+
DAPLINK_BL_TARGET ?= stm32f103xb_bl
20+
DAPLINK_BL_BUILD_DIR ?= $(DAPLINK_DIR)/projectfiles/make_gcc_arm/$(DAPLINK_BL_TARGET)/build
21+
22+
# SWD flash configuration (external probe). Used by the `daplink-deploy-*`
23+
# SWD targets. The STM32F103CB on the DAPLink chip is flash-compatible with
24+
# pyOCD's built-in stm32f103rc target for the lower 128 KB.
25+
DAPLINK_FLASH_ADDR ?= 0x08002000
26+
DAPLINK_BL_FLASH_ADDR ?= 0x08000000
27+
DAPLINK_PYOCD_TARGET ?= stm32f103rc
28+
DAPLINK_OPENOCD_INTERFACE ?= interface/stlink.cfg
29+
DAPLINK_OPENOCD_TARGET ?= target/stm32f1x.cfg
30+
DAPLINK_OPENOCD_TRANSPORT ?= hla_swd
1931

2032
# DAPLink requires gcc-arm-none-eabi 10.3-2021.10. System toolchains >= 11.3
2133
# produce code that overflows m_text (see DAPLink docs/DEVELOPERS-GUIDE.md and

0 commit comments

Comments
 (0)