Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,20 +226,34 @@ DAPLink is the firmware running on the STM32F103 interface chip. It provides the

DAPLink consists of **two parts**:

- **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.
- **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.
- **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.
- **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.

```bash
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 DAPLink interface firmware via MAINTENANCE volume
make daplink-clean # Clean DAPLink build artifacts
make daplink-firmware # Build the interface firmware (stm32f103xb_steami32_if)
make daplink-bootloader # Build the bootloader (stm32f103xb_bl)
make daplink-update # Refresh the DAPLink clone
make daplink-clean # Clean DAPLink build artifacts

# Routine interface update (no external probe)
make daplink-deploy # Alias for daplink-deploy-usb
make daplink-deploy-usb # Flash interface firmware via MAINTENANCE volume

# External SWD probe required (recovery, CI, bricked boards)
make daplink-deploy-pyocd # Flash interface firmware via SWD (pyocd)
make daplink-deploy-openocd # Flash interface firmware via SWD (openocd)
make daplink-deploy-bootloader # Flash bootloader via SWD (default: pyocd)
make daplink-deploy-bootloader-pyocd # Flash bootloader via SWD (pyocd)
make daplink-deploy-bootloader-openocd # Flash bootloader via SWD (openocd)
```

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.

**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.
**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.

**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.

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`).

## Notes

Expand Down
87 changes: 72 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,6 @@ echo " make daplink-$(2) (DAPLink firmware)"; \
exit 1
endef

# Variant for short names whose DAPLink counterpart does not exist yet
# (daplink-deploy-pyocd / daplink-deploy-openocd are tracked in #388).
define DEPRECATED_MICROPYTHON_ONLY
@echo "Error: 'make $(1)' has been renamed. Use:"; \
echo " make micropython-$(2) (MicroPython firmware)"; \
exit 1
endef

.PHONY: firmware firmware-update firmware-clean deploy deploy-pyocd deploy-openocd deploy-usb
firmware:
$(call DEPRECATED_FIRMWARE,firmware,firmware)
Expand All @@ -169,9 +161,9 @@ firmware-clean:
deploy:
$(call DEPRECATED_FIRMWARE,deploy,deploy)
deploy-pyocd:
$(call DEPRECATED_MICROPYTHON_ONLY,deploy-pyocd,deploy-pyocd)
$(call DEPRECATED_FIRMWARE,deploy-pyocd,deploy-pyocd)
deploy-openocd:
$(call DEPRECATED_MICROPYTHON_ONLY,deploy-openocd,deploy-openocd)
$(call DEPRECATED_FIRMWARE,deploy-openocd,deploy-openocd)
deploy-usb:
$(call DEPRECATED_FIRMWARE,deploy-usb,deploy-usb)

Expand Down Expand Up @@ -202,11 +194,41 @@ micropython-clean: ## Clean MicroPython firmware build artifacts
fi

# --- DAPLink firmware ---
# These targets manage the DAPLink **interface firmware** only (the second
# stage of DAPLink, flashed at 0x08002000). The bootloader (first stage,
# flashed at 0x08000000) is installed once at the factory and is not
# managed here. A future `daplink-deploy-bootloader` target could be added
# if needed, but it requires an external SWD probe and is rarely necessary.
# These targets build and flash the DAPLink firmware that runs on the
# STM32F103 interface chip. DAPLink has two parts:
#
# * Bootloader (first stage, `stm32f103xb_bl`) at 0x08000000
# → rarely updated, requires an external SWD probe.
# * Interface firmware (second stage, `stm32f103xb_steami32_if`) at 0x08002000
# → updated routinely, typically via the MAINTENANCE USB volume,
# or via an external SWD probe for recovery / bricked boards.
#
# SWD targets (`daplink-deploy-pyocd`, `daplink-deploy-openocd`,
# `daplink-deploy-bootloader*`) need an EXTERNAL probe (ST-Link, J-Link, or
# another CMSIS-DAP board) connected to the SWD header of the target board.
# A board cannot reflash its own DAPLink chip via its own SWD pins.

define DAPLINK_SWD_WARNING
@echo "================================================================"
@echo "Warning: this target flashes the DAPLink chip via SWD."
@echo "Requires an EXTERNAL probe (ST-Link / J-Link / CMSIS-DAP)"
@echo "connected to the target board's SWD header. A board cannot"
@echo "reflash its own on-board DAPLink via its own SWD pins."
@echo "================================================================"
endef

define DAPLINK_OPENOCD_FLASH
openocd -f $(DAPLINK_OPENOCD_INTERFACE) \
-f $(DAPLINK_OPENOCD_TARGET) \
-c "transport select $(DAPLINK_OPENOCD_TRANSPORT)" \
-c "reset_config none separate" \
-c "init" \
-c "reset halt" \
-c "stm32f1x unlock 0" \
-c "reset halt" \
-c "program $(1) verify $(2)" \
-c "reset; exit"
endef

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

.PHONY: daplink-bootloader
daplink-bootloader: $(DAPLINK_DIR) $(DAPLINK_GCC_DIR)/bin/arm-none-eabi-gcc $(DAPLINK_DIR)/venv/.installed ## Build DAPLink bootloader for the STeaMi STM32F103
@echo "Building DAPLink target $(DAPLINK_BL_TARGET) with gcc-arm-none-eabi $(DAPLINK_GCC_VERSION)..."
cd $(CURDIR)/$(DAPLINK_DIR) && \
PATH="$(CURDIR)/$(DAPLINK_GCC_DIR)/bin:$(CURDIR)/$(DAPLINK_DIR)/venv/bin:$$PATH" \
./venv/bin/python tools/progen_compile.py -t make_gcc_arm $(DAPLINK_BL_TARGET)
@echo "DAPLink bootloader ready: $(DAPLINK_BL_BUILD_DIR)/$(DAPLINK_BL_TARGET)_crc.bin"

.PHONY: daplink-update
daplink-update: $(DAPLINK_DIR) ## Update the DAPLink clone
@set -e
Expand All @@ -268,6 +298,33 @@ daplink-deploy-usb: $(DAPLINK_DIR) ## Flash DAPLink interface firmware via MAINT
--build-target daplink-firmware \
$(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin

.PHONY: daplink-deploy-pyocd
daplink-deploy-pyocd: daplink-firmware ## Flash DAPLink interface firmware via external SWD probe (pyocd)
$(DAPLINK_SWD_WARNING)
$(PYTHON) -m pyocd flash -t $(DAPLINK_PYOCD_TARGET) \
--base-address $(DAPLINK_FLASH_ADDR) \
$(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin

.PHONY: daplink-deploy-openocd
daplink-deploy-openocd: daplink-firmware ## Flash DAPLink interface firmware via external SWD probe (openocd)
$(DAPLINK_SWD_WARNING)
$(call DAPLINK_OPENOCD_FLASH,$(DAPLINK_BUILD_DIR)/$(DAPLINK_TARGET)_crc.bin,$(DAPLINK_FLASH_ADDR))

.PHONY: daplink-deploy-bootloader
daplink-deploy-bootloader: daplink-deploy-bootloader-pyocd ## Flash DAPLink bootloader via external SWD probe (default: pyocd)

.PHONY: daplink-deploy-bootloader-pyocd
daplink-deploy-bootloader-pyocd: daplink-bootloader ## Flash DAPLink bootloader via external SWD probe (pyocd)
$(DAPLINK_SWD_WARNING)
$(PYTHON) -m pyocd flash -t $(DAPLINK_PYOCD_TARGET) \
--base-address $(DAPLINK_BL_FLASH_ADDR) \
$(DAPLINK_BL_BUILD_DIR)/$(DAPLINK_BL_TARGET)_crc.bin

.PHONY: daplink-deploy-bootloader-openocd
daplink-deploy-bootloader-openocd: daplink-bootloader ## Flash DAPLink bootloader via external SWD probe (openocd)
$(DAPLINK_SWD_WARNING)
$(call DAPLINK_OPENOCD_FLASH,$(DAPLINK_BL_BUILD_DIR)/$(DAPLINK_BL_TARGET)_crc.bin,$(DAPLINK_BL_FLASH_ADDR))

.PHONY: daplink-clean
daplink-clean: ## Clean DAPLink firmware build artifacts
@if [ -d "$(DAPLINK_DIR)" ]; then \
Expand Down
12 changes: 12 additions & 0 deletions env.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ DAPLINK_BRANCH ?= release_letssteam
DAPLINK_DIR ?= $(BUILD_DIR)/DAPLink
DAPLINK_TARGET ?= stm32f103xb_steami32_if
DAPLINK_BUILD_DIR ?= $(DAPLINK_DIR)/projectfiles/make_gcc_arm/$(DAPLINK_TARGET)/build
DAPLINK_BL_TARGET ?= stm32f103xb_bl
DAPLINK_BL_BUILD_DIR ?= $(DAPLINK_DIR)/projectfiles/make_gcc_arm/$(DAPLINK_BL_TARGET)/build

# SWD flash configuration (external probe). Used by the `daplink-deploy-*`
# SWD targets. The STM32F103CB on the DAPLink chip is flash-compatible with
# pyOCD's built-in stm32f103rc target for the lower 128 KB.
DAPLINK_FLASH_ADDR ?= 0x08002000
DAPLINK_BL_FLASH_ADDR ?= 0x08000000
DAPLINK_PYOCD_TARGET ?= stm32f103rc
DAPLINK_OPENOCD_INTERFACE ?= interface/stlink.cfg
DAPLINK_OPENOCD_TARGET ?= target/stm32f1x.cfg
DAPLINK_OPENOCD_TRANSPORT ?= hla_swd

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