iceflashprog

Firmware

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

File Description
iceflashprog.elf ELF binary with debug symbols
iceflashprog.elf.map Linker map file
iceflashprog.bin Raw binary image
iceflashprog.hex Intel HEX format
iceflashprog.dfu DFU 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.

Region Start address Size
Flash 0x08000000 16 KB
RAM 0x20000000 6 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

Peripheral Function
GPIOA PA4 SPI1 chip select (software-controlled)
GPIOA PA5 SPI1 SCK
GPIOA PA6 SPI1 MISO
GPIOA PA7 SPI1 MOSI
GPIOA PA15 LED output
GPIOB PB0 CRST (FPGA reset, active low)
SPI1 SPI master, DMA-driven flash communication
DMA1 Ch2 SPI1 RX
DMA1 Ch3 SPI1 TX
TIM3 SPI flash status register polling (1 ms period)
IWDG Independent 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

File Purpose
main.c USB HID report handling, clock initialization, main loop
descriptors.c USB device, configuration, HID report, and string descriptors
spi.c SPI1 peripheral driver with DMA transfers
spi_flash.c SPI flash command layer (read, write, erase, JEDEC ID, power management)
watchdog.c Independent 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

Field Value
USB version 2.0 Full-Speed
Device class HID (interface-level)
VID 0x16c0
PID 0x05df
Manufacturer rgm.io
Product iceflashprog
Serial number STM32 internal unique ID
Max power 100 mA (bus-powered)
HID version 1.11

Endpoints

Direction Type Max packet size Interval
IN (EP1) Interrupt 64 bytes 1 ms
OUT (EP1) Interrupt 64 bytes 1 ms

HID reports

The device defines two report IDs with different purposes:

Report ID Direction Size (bytes) Purpose
1 Input (device to host) 256 Flash page data
1 Output (host to device) 259 Flash page write (3-byte address + 256-byte data)
2 Input (device to host) 4 Command response (1 status + 3 data)
2 Output (host to device) 4 Command 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 ID Name Request data Response data
1 Power Up (unused) (none)
2 Power Down (unused) (none)
3 JEDEC ID (unused) manufacturer ID (1 byte) + device ID (2 bytes)
4 Read 3-byte address Flash page returned via report ID 1
5 Erase Sector 3-byte address (none)
6 Erase Block 3-byte address (none)
7 Erase 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

Value Name Description
0 OK Operation completed successfully
1 Unpowered Flash chip is not powered (Power Up not sent)
2 Invalid Request Malformed request
3 Invalid Command ID Unknown command ID
4 Invalid Flash Page Read Flash page read returned unexpected length
5 Invalid Flash Page Write Write verification failed
6 Locked Device is busy with another operation

Vendor usage page

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

Usage ID Name
0x0001 iceflashprog (application collection)
0x0002 Flash Page
0x0003 Request
0x0004 Response
0x0011 Address
0x0012 Data
0x0013 Command ID
0x0015 Status