iceflashprog

Firmware

Table of contents

The iceflashprog firmware runs on an STM32F042 microcontroller (ARM Cortex-M0) on the stm32f0-usbd-devboard. It is a bare-metal application with no HAL or RTOS -- all peripheral access uses direct CMSIS register operations. The firmware implements a USB HID interface for communicating with the host software and drives SPI flash operations using DMA. A single binary runs on all STM32F042K variants supported by the devboard.

Building from source

Prerequisites

  • CMake 3.25 or later
  • ARM GNU Toolchain (arm-none-eabi)
  • Ninja (recommended) or Make

The following dependencies are fetched automatically via CMake FetchContent:

Configure and build

cmake -B build -DCMAKE_BUILD_TYPE=Release -S firmware -G Ninja
cmake --build build

Output artifacts

FileDescription
iceflashprog.elfELF binary with debug symbols
iceflashprog.elf.mapLinker map file
iceflashprog.binRaw binary image
iceflashprog.hexIntel HEX format
iceflashprog.dfuDFU image with suffix (for dfu-util)

Memory layout

The linker script uses a 16 KB flash region to ensure compatibility with all STM32F042K variants, including the smallest STM32F042K4.

RegionStart addressSize
Flash0x0800000016 KB
RAM0x200000006 KB

Flashing

Using USB DFU

A prebuilt DFU binary is available from the latest release (v0.1.0) as iceflashprog-firmware-0.1.0.zip. Extract the archive and flash the iceflashprog.dfu file. See the Hardware Build Manual for detailed DFU flashing instructions.

Note

If the microcontroller is empty (fresh from the factory), it boots directly into the DFU bootloader and is ready to flash without any pin jumper.

A dedicated CMake target is available for ST-Link flashing:

cmake --build build --target iceflashprog-stlink-write

See the Hardware Build Manual for detailed ST-Link instructions.

Architecture overview

Clock configuration

By default, the firmware runs the system clock at 48 MHz from the internal HSI48 oscillator, which also serves as the USB clock source. This results in an SPI clock of 12 MHz (HSI48 / 4).

An alternative SPI_MAX_FREQ compile-time option reconfigures the PLL to produce a 36 MHz system clock (HSI48 / 4 * 3), yielding an SPI clock of 18 MHz (36 MHz / 2). The HSI48 oscillator remains active in both modes and is used as the USB clock source.

Peripheral map

PeripheralFunction
GPIOA PA4SPI1 chip select (software-controlled)
GPIOA PA5SPI1 SCK
GPIOA PA6SPI1 MISO
GPIOA PA7SPI1 MOSI
GPIOA PA15LED output
GPIOB PB0CRST (FPGA reset, active low)
SPI1SPI master, DMA-driven flash communication
DMA1 Ch2SPI1 RX
DMA1 Ch3SPI1 TX
TIM3SPI flash status register polling (1 ms period)
IWDGIndependent watchdog (~1 s default, ~15 s during chip erase)

Main loop

The firmware runs a cooperative polling loop. On each iteration, spi_flash_task() is called first to process any pending SPI flash operations (including DMA completion and status register polling via TIM3). If no flash work is pending, usbd_task() processes USB events. The watchdog is reloaded on every USB SOF frame (1 ms interval) as long as no write-in-progress flag is set, ensuring the device resets if the host stops communicating.

Source files

FilePurpose
main.cUSB HID report handling, clock initialization, main loop
descriptors.cUSB device, configuration, HID report, and string descriptors
spi.cSPI1 peripheral driver with DMA transfers
spi_flash.cSPI flash command layer (read, write, erase, JEDEC ID, power management)
watchdog.cIndependent watchdog initialization and reload management

USB HID protocol

The device uses a vendor-specific USB HID protocol. This is not a standard HID device (keyboard, mouse, etc.) -- it requires custom host software.

Device identity

FieldValue
USB version2.0 Full-Speed
Device classHID (interface-level)
VID0x16c0
PID0x05df
Manufacturerrgm.io
Producticeflashprog
Serial numberSTM32 internal unique ID
Max power100 mA (bus-powered)
HID version1.11

Endpoints

DirectionTypeMax packet sizeInterval
IN (EP1)Interrupt64 bytes1 ms
OUT (EP1)Interrupt64 bytes1 ms

HID reports

The device defines two report IDs with different purposes:

Report IDDirectionSize (bytes)Purpose
1Input (device to host)256Flash page data
1Output (host to device)259Flash page write (3-byte address + 256-byte data)
2Input (device to host)4Command response (1 status + 3 data)
2Output (host to device)4Command request (1 command ID + 3 data)

Reports larger than the 64-byte endpoint size are transferred in multiple USB transactions.

Commands (report ID 2)

Command IDNameRequest dataResponse data
1Power Up(unused)(none)
2Power Down(unused)(none)
3JEDEC ID(unused)manufacturer ID (1 byte) + device ID (2 bytes)
4Read3-byte addressFlash page returned via report ID 1
5Erase Sector3-byte address(none)
6Erase Block3-byte address(none)
7Erase Chip(unused)(none)

The Power Up command also asserts the FPGA configuration reset (CRST), holding the FPGA in reset while the flash is accessed. Power Down de-asserts CRST, releasing the FPGA to configure from flash. The host must send Power Up before any flash operations.

Flash page write (report ID 1)

To write a flash page, the host sends report ID 1 with 3 bytes of address followed by 256 bytes of data (259 bytes total). The firmware performs the write and then automatically reads back the page to verify correctness. The result is returned as a report ID 2 response with the appropriate status code.

Status codes

ValueNameDescription
0OKOperation completed successfully
1UnpoweredFlash chip is not powered (Power Up not sent)
2Invalid RequestMalformed request
3Invalid Command IDUnknown command ID
4Invalid Flash Page ReadFlash page read returned unexpected length
5Invalid Flash Page WriteWrite verification failed
6LockedDevice is busy with another operation

Vendor usage page

The HID report descriptor uses vendor usage page 0xFF00 with the following usage IDs:

Usage IDName
0x0001iceflashprog (application collection)
0x0002Flash Page
0x0003Request
0x0004Response
0x0011Address
0x0012Data
0x0013Command ID
0x0015Status