Kompaktní MBR bootloader pro STM32F407VE: 324 bajtů kódu
Primární zavaděč (MBR) pro STM32F407VE je umístěn v prvním sektoru Flash na adrese 0x08000000 a předává řízení sekundárnímu zavaděči na adrese 0x08060000. Před skokem je ověřena platnost tabulky vektorů přerušení: ukazatel zásobníku v rozsahu SRAM (0x20000000–0x2001FFFF), adresa ResetHandler ve Flash (0x08000000–0x080FFFFF) s nastaveným bitem Thumb-režimu.
Pokud ověření neprojde, aktivuje se indikace chyby – blikání LED na pinu PE13 s frekvencí 10 Hz (perioda 100 ms, střída 50 %). Kód je optimalizován na 324 bajtů odstraněním standardní knihovny, externích přerušení a použitím jádrového timeru DWT pro zpoždění.
Rozložení NOR Flash:
| Sektor | Počáteční adresa | Velikost, Kb | Obsah |
|--------|------------------|--------------|-------|
| 0 | 0x08000000 | 16 | Primární zavaděč |
| 1–3 | 0x08004000–0x0800C000 | 16×3 | NVRAM |
| 4–6 | 0x08010000–0x08040000 | 64+128+128 | Aplikace |
| 7 | 0x08060000 | 128 | Sekundární zavaděč |
Každý bajt uvolněný z MBR zvětšuje objem NVRAM.
Fáze optimalizace velikosti
Optimalizace byla provedena postupně s použitím ARM-GCC a GNU Make:
- Naivní implementace na HAL: 25192 bajtů.
- -O0 bez ladění: 12052 bajtů.
- -Os -flto: 9684 bajtů.
- DeepSeek + -Os -flto: 2124 bajtů.
- -nostdlib, odstranění libc_init: 652 bajtů.
- Ořezaná tabulka vektorů: 324 bajtů.
Klíčové příznaky kompilace:
-nostdlib -nostartfiles -ffreestanding-ffunction-sections -fdata-sections -Wl,--gc-sections-Ospro velikost,-mcpu=cortex-m4 -mthumb
Implementace v C
Hlavní kód v jediném souboru main.c používá přímý přístup k registrům bez HAL:
// main.c - Bootloader pro STM32F407VE
#include <stdint.h>
#define APP_BASE 0x08060000UL
#define SRAM_BASE 0x20000000UL
#define SRAM_END 0x2001FFFFUL
#define FLASH_BASE 0x08000000UL
#define FLASH_END 0x080FFFFFUL
// Periferie
#define RCC_BASE 0x40023800UL
#define GPIOE_BASE 0x40021000UL
#define DWT_BASE 0xE0001000UL
#define RCC_AHB1ENR (*((volatile uint32_t*)(RCC_BASE + 0x30)))
#define GPIOx_MODER (*((volatile uint32_t*)(GPIOE_BASE + 0x00)))
#define GPIOx_ODR (*((volatile uint32_t*)(GPIOE_BASE + 0x14)))
#define DWT_CYCCNT (*((volatile uint32_t*)(DWT_BASE + 0x04)))
#define DWT_CTRL (*((volatile uint32_t*)(DWT_BASE + 0x00)))
typedef struct {
uint32_t stack_ptr;
uint32_t reset_handler;
} VectorTable_t;
static inline int is_valid_sram(uint32_t addr) {
return ((SRAM_BASE <= addr) && (addr <= SRAM_END));
}
static inline int is_valid_flash(uint32_t addr) {
return ((FLASH_BASE <= addr) && (addr <= FLASH_END));
}
// Inicializace DWT
static void dwt_init(void) {
*(volatile uint32_t*)0xE000EDF0 |= (1UL << 24);
DWT_CTRL |= 1;
}
// Zpoždění ~ms při 168 MHz
static void delay_ms(uint32_t ms) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = ms * 16000;
while ((DWT_CYCCNT - start) < cycles);
}
// Blikání LED PE13 10 Hz
static void blink_led(void) __attribute__((noreturn));
static void blink_led(void) {
RCC_AHB1ENR |= (1 << 4);
GPIOx_MODER &= ~(3 << 26);
GPIOx_MODER |= (1 << 26);
dwt_init();
while(1) {
GPIOx_ODR |= (1 << 13);
delay_ms(50);
GPIOx_ODR &= ~(1 << 13);
delay_ms(50);
}
}
static int is_valid_vector_table(const VectorTable_t *app_vec) {
if (!is_valid_sram(app_vec->stack_ptr)) return 0;
uint32_t reset_addr = app_vec->reset_handler;
if (!is_valid_flash(reset_addr & ~1)) return 0;
if ((reset_addr & 1) == 0) return 0;
return 1;
}
typedef void (*pFunction)(void);
int main(void) {
const VectorTable_t *app_vec = (const VectorTable_t*)APP_BASE;
if (is_valid_vector_table(app_vec)) {
pFunction jump = (pFunction)app_vec->reset_handler;
jump();
}
blink_led();
}
Sestavení projektu
Makefile s podporou ladění a balení:
CFLAGS += -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
CFLAGS += -Os -ffunction-sections -fdata-sections -nostdlib
CFLAGS += -nostartfiles -fno-builtin -ffreestanding
LDFLAGS += -T gcc_arm_mbr.ld -Wl,--gc-sections
SOURCES = main.c system_stm32f4xx.c
OBJECTS = $(SOURCES:.c=.o) startup_stm32f407xx.o
all: $(TARGET).bin
V startup_stm32f407xx.S byla odstraněna řádka bl __libc_init_array a ořezána tabulka vektorů – zůstaly pouze základní vektory (Stack, Reset, NMI, HardFault).
Co je důležité
- Velikost 324 bajtů umožňuje rozšířit NVRAM o 15+ Kb v prvním sektoru.
- Validace tabulky vektorů zabraňuje zaseknutí na poškozeném kódu.
- DWT-timer zajišťuje přesná zpoždění bez SysTick/PLL.
- Přímý přístup k registrům minimalizuje režii.
- Kompletní sestavení: ARM-GCC + vlastní linker script bez standardní knihovny.
— Editorial Team
Zatím žádné komentáře.