Skip to content

AGR_BOOT V2 — XM10 Bootloader Guide

📌 What you'll learn: Two firmware upload methods — STM32CubeIDE SWD debugging vs. PhAI Studio USB FTP — plus how to avoid corrupting the bootloader and how the automatic rollback mechanism works. ⏱️ Estimated reading time: 30 minutes (60 minutes including hands-on) 🧰 Prerequisites: STM32CubeIDE installed (docs/getting-started/02-software-setup.md) 🎯 Key point ①: Debug Configuration Start address = 0x08040000 (leaving it at the default 0x08000000 destroys the bootloader) 🎯 Key point ②: Specify vector table (hex) = 0x08040400 (a different value from Start address — get it wrong and the debugger halts in a signal handler right after boot)

⚠️ Never do this: STM32CubeIDE Debug → Start address 0x08000000 → bootloader corrupted. Always use 0x08040000.

Version: v1.1.0 | Target module: XM10 Extension Module (STM32H743XI) | Created: 2026-04-02


Table of Contents

  1. Overview
  2. Flash Memory Map
  3. Initial Installation (starting from a blank board)
  4. App Firmware Upload — STM32CubeIDE (SWD Debugging)
  5. App Firmware Upload — PhAI Studio (USB FTP)
  6. Post-Build Binary Generation
  7. How the Bootloader Works
  8. Troubleshooting
  9. Important Notes

1. Overview

The XM10 Extension Module ships with the AGR_BOOT V2 bootloader, which enables firmware updates over USB.

What the bootloader does

  • Validates the app firmware at power-on (signature + CRC-32 + HW revision check)
  • Receives app firmware uploads via the USB CDC FTP protocol
  • Automatically rolls back to the backup firmware if an update fails (after 3 consecutive failed boots)
  • Stops starting the app and waits for an upload if the board keeps resetting (from v1.1.0, with Rev 2.0 apps from v2.8.1 — see Section 8)
  • Supports both Rev1.1 and Rev2.0 hardware from a single bootloader binary (GPIO auto-detection)

Required tools

Tool Purpose Download
STM32CubeProgrammer Initial bootloader installation (SWD/JTAG) st.com
STM32CubeIDE App firmware build + SWD debugging st.com
PhAI Studio USB FTP firmware upload studio.onephai.com
ST-Link V3 SWD debugging probe ST-Link V2/V3 compatible

2. Flash Memory Map

The STM32H743XI has 2 MB of internal Flash (Bank1 + Bank2).

┌───────────────────────────────────────────────────────────────────────────┐
│ Address         │ Size    │ Region         │ Description                  │
├───────────────────────────────────────────────────────────────────────────┤
│ 0x08000000      │ 256 KB  │ Bootloader     │ AGR_BOOT V2                  │
│                 │         │                │ (Bank1 S0-S1)                │
├───────────────────────────────────────────────────────────────────────────┤
│ 0x08040000      │ 1 KB    │ FW Header      │ Firmware header              │
│                 │         │                │ (signature + CRC + version)  │
├───────────────────────────────────────────────────────────────────────────┤
│ 0x08040400      │ 767 KB  │ App (Active)   │ User app firmware            │
│                 │         │                │ (Bank1 S2-S7)                │
├───────────────────────────────────────────────────────────────────────────┤
│ 0x08100000      │ 768 KB  │ Backup Slot    │ Previous firmware backup     │
│                 │         │                │ (Bank2 S0-S5)                │
├───────────────────────────────────────────────────────────────────────────┤
│ 0x081C0000      │ 128 KB  │ Boot Config    │ Boot settings                │
│                 │         │                │ (Bank2 S6)                   │
├───────────────────────────────────────────────────────────────────────────┤
│ 0x081E0000      │ 128 KB  │ Reserved       │ Reserved                     │
│                 │         │                │ (Bank2 S7)                   │
└───────────────────────────────────────────────────────────────────────────┘

Important: The bootloader (0x08000000) and the app (0x08040000) occupy completely separate regions.
Flashing the app over SWD does not damage the bootloader.


3. Initial Installation (starting from a blank board)

Follow these steps when the board has never had a bootloader installed, or when you are installing the bootloader for the first time on a board that was previously used without one.

Step 1: Full Chip Erase

Why is this necessary? If the existing firmware was a single binary starting at 0x08000000, its address range will conflict with the bootloader. Always perform a full erase before proceeding.

  1. Launch STM32CubeProgrammer
  2. Connect ST-Link to the board
  3. Click Connect in the left panel (SWD or JTAG)
  4. Select Erasing & Programming (download icon) from the left menu
  5. Click the Full chip erase button at the top
  6. Confirm the "Erase complete" message

Step 2: Upload the Bootloader Binary

  1. In the Erasing & Programming tab:
  2. File path: select AGR_Bootloader.bin (downloaded from the Release page)
  3. Start address: 0x08000000 (leave as default)
  4. Skip flash erase before programming: unchecked (default)
  5. Verify programming: checked
  6. Click Start Programming
  7. Confirm the "Download complete" + "Verification OK" messages

Using a Hex file: Select AGR_Bootloader.hex and the address is set automatically.

Step 3: Verify

  1. Reset the board (or power-cycle it)
  2. The bootloader starts and:
  3. Because no valid app firmware exists, it enters FTP mode
  4. A USB CDC port appears on the PC (COM port)
  5. Proceed to Step 4 or Step 5 to upload the app firmware

Replacing only the bootloader (board that already has one)

To update a board that already has a bootloader, skip Step 1 (Full chip erase) — it would erase the app firmware too.

  1. Write AGR_Bootloader.bin at 0x08000000 as in Step 2 (only the bootloader area is erased and rewritten)
  2. Reset the board — if your existing app starts as before, you are done

4. App Firmware Upload — STM32CubeIDE (SWD Debugging)

This is the most common method during development. It writes directly to Flash over SWD.

One-time Setup

Configure the Debug Configuration:

  1. Open Run → Debug Configurations → STM32 C/C++ Application
  2. Create a new configuration or select an existing one
  3. Under the Debugger tab:
  4. Download: ✅ checked
  5. Start address: 0x08040000 (⚠️ not the default 0x08000000! — the .bin download address)
  6. Size: 0x000C0000 (768 KB = Active Slot)
  7. Specify vector table (hex): 0x08040400 (⚠️ a different value from Start address! — the actual app vector table location)
  8. Reset behaviour: Software Reset

⚠️ Critical 1: Set Start address (the download address) to 0x08040000.
Leaving it at the default (0x08000000) overwrites the bootloader.

⚠️ Critical 2: Specify vector table (hex) must be 0x08040400 (a different value from Start address).
The two addresses play different roles: - Start address 0x08040000 = where the .bin is written to Flash. The .bin is 1 KB FW Header + app, so this is the header start address. - Vector table 0x08040400 = the app's actual vector table (initial SP + Reset_Handler), located after the 1 KB header.

If you leave the vector table at 0x08040000, the debugger reads the FW Header bytes (AGRBOOT…) as the initial stack pointer / reset vector, causing a HardFault right after boot → the session keeps halting in a signal handler (e.g. SIGTRAP). The SDK ZIP's Extension_Module.launch already ships 0x08040400 as the default.

Build & Debug

  1. Build the project in STM32CubeIDE (Ctrl+B)
  2. Post-build steps run automatically:
  3. Memory usage report
  4. Version info generated from the git tag
  5. Size and CRC written into the firmware header
  6. Packaged binary for FTP upload created
  7. Click Debug (F11) → the app firmware is written to 0x08040000 over SWD
  8. Debug normally (breakpoints, variable watch, etc.)

Note — halting at main() is normal. When the debug session starts, the debugger stopping at Thread #1 suspended: breakpoint main() is not an error — it is CubeIDE's default behavior (Debug Configuration → Startup tab → Set breakpoint at: main, checked by default). Press Resume(F8) to run. If you don't want it to stop every time, uncheck Set breakpoint at: main. (This is a normal halt, different from the signal-handler halt caused by a wrong vector table — see the vector-table note in Section 4.)

Why SWD Debugging is Compatible with the Bootloader

  • The linker script (STM32H743XIHX_FLASH.ld) places app code at 0x08040400
  • The firmware header is placed at 0x08040000 so the bootloader can read the signature
  • The SDK embeds the signature in the firmware, so the bootloader recognises the app even after an SWD flash

5. App Firmware Upload — PhAI Studio (USB FTP)

This method uploads firmware over a USB cable alone, with no SWD debugger required.
It is the recommended approach for production environments, field updates, and end users.

Required File

After a successful build, the Debug/ folder contains:

Debug/
├── Extension_Module.elf        ← For SWD debugging
├── Extension_Module.bin        ← For SWD flash (includes FW Header)
├── Extension_Module.hex        ← For SWD flash
├── Extension_Module_app.bin    ← For FTP upload (no FW Header)
└── XM10_X_X_X_X.bin           ← Packaged binary for FTP upload ★

File to use in PhAI Studio: XM10_X_X_X_X.bin (e.g. XM10_2_0_1_0.bin)
This file is created automatically at the end of the build and consists of a 1 KB firmware header followed by the app binary.

Upload Procedure

  1. Open PhAI Studio: studio.onephai.com
  2. Connect the XM10 board to your PC via USB cable
  3. Confirm that PhAI Studio automatically detects the device
  4. Select the FW Upload menu
  5. Choose the XM10_X_X_X_X.bin file
  6. Start the Upload
  7. Monitor the progress bar → the board reboots automatically when complete
  8. The bootloader validates the new firmware → app launches

Forcing FTP Mode

To switch to FTP mode while the app is already running: - Send the Enter Bootloader command from PhAI Studio


6. Post-Build Binary Generation

When you run Build in STM32CubeIDE, the following post-build steps run automatically, in order:

[Build]   GCC compile + link → Extension_Module.elf
           │
[Step 1]  memory usage report (console output)
           │
[Step 2]  git tag → auto-generate version.h
           │
[Step 3]  write size and CRC into the firmware header
           │                  → generate .bin, .hex, _app.bin
           │
[Step 4]  _app.bin + firmware header → XM10_X_X_X_X.bin

Details of Each Step

Step Input Output Description
1 .elf Console Visualises FLASH/RAM usage
2 git tag version.h Defines FW_VER_MAJOR/MINOR/PATCH/DEBUG
3 .elf .bin, .hex, _app.bin Writes size and CRC into the firmware header
4 _app.bin XM10_X_X_X_X.bin 1 KB header + app binary = FTP-ready package

Python 3.x required: The post-build steps run on Python. Run pip install pyyaml.


7. How the Bootloader Works

Boot Sequence

Power ON → Bootloader starts (0x08000000)
    │
    ├─ Repeated-reset check (v1.1.0, Rev 2.0 apps from v2.8.1)
    │   └─ 10 boots in a row where the app did not run for 5 s: do not start the app, wait in FTP mode (all 3 LEDs blink together)
    │
    ├─ Read boot settings (Bank2 S6)
    │   └─ First boot: auto-create settings with default values
    │
    ├─ Rollback check (3 or more failed boots in a row?)
    │   └─ Yes: restore from Backup Slot
    │
    ├─ Validate app firmware
    │   ├─ Check signature
    │   ├─ Verify CRC-32 (FTP uploads only)
    │   └─ Check HW revision compatibility
    │
    ├─ Validation passed → jump to app (0x08040400)
    │   ├─ Once the app starts normally, the SDK notifies the bootloader → failed-boot count reset
    │   └─ Once the app has run normally for 5 s → repeated-reset count reset (v1.1.0, Rev 2.0)
    │
    └─ Validation failed → enter FTP wait mode
        └─ Wait to receive new firmware over USB CDC

FTP Update Sequence

FW Upload initiated from PhAI Studio
    │
    ├─ [1] App switches to the bootloader (reset)
    ├─ [2] Copy current Active FW → Backup Slot
    ├─ [3] Erase Active Slot
    ├─ [4] Receive new FW + write to Active Slot
    ├─ [5] Verify CRC-32
    ├─ [6] Update boot settings (waiting for the new firmware to confirm)
    └─ [7] Reset → new app starts → normal start confirmed → done

LED Indications

LEDs Meaning
Light up one by one, then all off (once, briefly) Bootloader starting
One at a time, rotating FTP wait — waiting for a firmware upload
All three blink together Stopped after repeated resets (v1.1.0) — waiting for an upload (Section 8)
Fast alternating blink Bootloader error; it cannot continue

8. Troubleshooting

"I installed the bootloader but the app won't run"

Cause: No app firmware is present, or the firmware is invalid. 1. Build the project in STM32CubeIDE and upload the app via Debug (F11) 2. Or upload XM10_X_X_X_X.bin from PhAI Studio

"After SWD debugging, the bootloader is gone"

Cause: The Debug Configuration Start address was set to 0x08000000. 1. Change it to 0x08040000 (see Section 4) 2. Reinstall the bootloader (see Section 3)

"Execution halts in a signal handler (e.g. SIGTRAP) the moment I click Debug (F11)"

Cause: The Debug Configuration's Specify vector table (hex) is not 0x08040400 (e.g. it was set to 0x08040000, the same as the Start address). The debugger reads the 1 KB FW Header as the vector table, so the initial SP/PC are garbage. - Under the Debugger tab, set Specify vector table (hex) to 0x08040400 (see Section 4) - Keep the Download Start address at 0x08040000 — the two values are different. - If you import the SDK ZIP as-is, Extension_Module.launch already defaults to 0x08040400, so this problem does not occur.

"PhAI Studio cannot find the device"

  1. Check the USB cable connection
  2. Verify the COM port in Windows Device Manager
  3. Confirm that the bootloader is in FTP mode (if the app is running, send "Enter Bootloader" from PhAI Studio first)

"After uploading firmware, the board keeps rebooting and reverts to the previous version"

Cause: The new firmware never told the bootloader that it started normally, triggering an automatic rollback after 3 boot attempts. - The default SDK code sends this notification automatically right after boot - Verify that you have not modified the SDK startup code

Cause (bootloader v1.1.0): the board restarted 10 times in a row without the app running for 5 seconds, so the bootloader stopped starting the app and is waiting for a firmware upload. 1. First check why the board kept resetting (app code, power, etc.). 2. Upload the firmware again with PhAI Studio to clear it (see Section 5). The board is already in FTP wait mode. - If a board that ran a v2.8.1 Rev 2.0 app gets an app built with SDK v2.8.0 or earlier through ST-LINK, it can end up in this state after 10 restarts (older apps do not do the 5-second check). Uploading once with PhAI Studio clears it here too.

"The build succeeds but XM10_X_X_X_X.bin is not generated"

  1. Check Python 3.x installation: python --version
  2. Review the post-build output in the Build Console for error messages
  3. Confirm that the SDK's tools/build/ folder is still in place (it holds the post-build files)

"STM32CubeProgrammer shows no data at 0x08040000 when I read Flash"

Cause: The Start address was set incorrectly when uploading a .bin file directly with STM32CubeProgrammer. - When uploading a .bin file, set Start address to 0x08040000 - When uploading a .hex file, the address is embedded in the file and set automatically


9. Important Notes

Things you must never do

Prohibited action Consequence
Set Start address to 0x08000000 in STM32CubeIDE Debug Bootloader corrupted
Full chip erase in STM32CubeProgrammer, then upload app only No bootloader → board cannot boot
Write directly to the bootloader region (0x08000000–0x0803FFFF) Bootloader corrupted
Arbitrarily erase the Boot Config region (0x081C0000) Boot settings reset (auto-recovery will run)

Common notes for Rev1.1 / Rev2.0

  • Both revisions use the same bootloader binary (HW revision is detected automatically via GPIO)
  • Both revisions use the same Flash memory map
  • App firmware is built with a separate libXM_Lib.a for each revision, but the bootloader is shared

Python Environment Setup

To run the post-build steps:

# After installing Python 3.x
pip install pyyaml

If STM32CubeIDE's Build Console cannot find the Python interpreter: - Windows: add the Python installation path to the system PATH environment variable - Restart STM32CubeIDE