Zwarty bootloader MBR dla STM32F407VE: zaledwie 324 bajty kodu
Pierwotny bootloader (MBR) dla STM32F407VE znajduje się w pierwszym sektorze Flash pod adresem 0x08000000 i przekazuje sterowanie drugorzędnemu bootloaderowi pod adresem 0x08060000. Przed skokiem sprawdzana jest poprawność tabeli wektorów przerwań: wskaźnik stosu w zakresie SRAM (0x20000000–0x2001FFFF), adres ResetHandler w Flash (0x08000000–0x080FFFFF) z ustawionym bitem trybu Thumb.
Jeśli weryfikacja nie powiedzie się, aktywowana jest sygnalizacja błędu – miganie LED na pinie PE13 z częstotliwością 10 Hz (okres 100 ms, współczynnik wypełnienia 50%). Kod został zoptymalizowany do 324 bajtów przez usunięcie biblioteki standardowej, zewnętrznych przerwań i użycie rdzeniowego timera DWT do opóźnień.
Mapowanie pamięci NOR Flash:
| Sektor | Adres początkowy | Rozmiar, KB | Zawartość |
|--------|------------------|-------------|-----------|
| 0 | 0x08000000 | 16 | Pierwotny bootloader |
| 1–3 | 0x08004000–0x0800C000 | 16×3 | NVRAM |
| 4–6 | 0x08010000–0x08040000 | 64+128+128 | Aplikacja |
| 7 | 0x08060000 | 128 | Drugorzędny bootloader |
Każdy bajt zwolniony z MBR zwiększa pojemność NVRAM.
Etapy optymalizacji rozmiaru
Optymalizacja przeprowadzana była etapowo z użyciem ARM-GCC i GNU Make:
- Naiwna implementacja na HAL: 25192 bajtów.
- -O0 bez debugowania: 12052 bajtów.
- -Os -flto: 9684 bajtów.
- DeepSeek + -Os -flto: 2124 bajty.
- -nostdlib, usunięcie libc_init: 652 bajty.
- Przycięta tabela wektorów: 324 bajty.
Kluczowe flagi kompilacji:
-nostdlib -nostartfiles -ffreestanding-ffunction-sections -fdata-sections -Wl,--gc-sections-Osdla rozmiaru,-mcpu=cortex-m4 -mthumb
Implementacja w C
Główny kod w pojedynczym pliku main.c wykorzystuje bezpośredni dostęp do rejestrów bez HAL:
// main.c - Bootloader dla STM32F407VE
#include <stdint.h>
#define APP_BASE 0x08060000UL
#define SRAM_BASE 0x20000000UL
#define SRAM_END 0x2001FFFFUL
#define FLASH_BASE 0x08000000UL
#define FLASH_END 0x080FFFFFUL
// Periferia
#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));
}
// Inicjalizacja DWT
static void dwt_init(void) {
*(volatile uint32_t*)0xE000EDF0 |= (1UL << 24);
DWT_CTRL |= 1;
}
// Opóźnienie ~ms przy 168 MHz
static void delay_ms(uint32_t ms) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = ms * 16000;
while ((DWT_CYCCNT - start) < cycles);
}
// Miganie 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();
}
Kompilacja projektu
Makefile z obsługą debugowania i pakowania:
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
W startup_stm32f407xx.S usunięto linię bl __libc_init_array i przycięto tabelę wektorów – pozostawiono tylko podstawowe wektory (Stack, Reset, NMI, HardFault).
Co jest ważne
- Rozmiar 324 bajty umożliwia rozszerzenie NVRAM o ponad 15 KB w pierwszym sektorze.
- Weryfikacja tabeli wektorów zapobiega zawieszeniu się na uszkodzonym kodzie.
- Timer DWT zapewnia precyzyjne opóźnienia bez SysTick/PLL.
- Bezpośredni dostęp do rejestrów minimalizuje narzuty.
- Pełna kompilacja: ARM-GCC + niestandardowy skrypt linkera bez biblioteki standardowej.
— Editorial Team
Brak komentarzy.