# Bootloader MBR Compacto para STM32F407VE: Solo 324 Bytes
Este bootloader principal (MBR) para el STM32F407VE se ubica en el primer sector de Flash en la dirección 0x08000000 y transfiere el control al bootloader secundario en 0x08060000. Antes de saltar, valida la tabla de vectores de interrupción: puntero de pila en el rango de SRAM (0x20000000–0x2001FFFF), dirección de ResetHandler en Flash (0x08000000–0x080FFFFF) con el bit de modo Thumb activado.
Si la validación falla, indica un error parpadeando el LED en el pin PE13 a 10 Hz (período de 100 ms, ciclo de trabajo del 50%). El código se ha reducido a 324 bytes eliminando la biblioteca estándar, interrupciones externas y usando el temporizador DWT del núcleo para retardos.
Distribución de la memoria NOR Flash:
| Sector | Dirección inicial | Tamaño, KB | Contenido |
|--------|-------------------|------------|-----------|
| 0 | 0x08000000 | 16 | Bootloader principal |
| 1–3 | 0x08004000–0x0800C000 | 16×3 | NVRAM |
| 4–6 | 0x08010000–0x08040000 | 64+128+128 | Aplicación |
| 7 | 0x08060000 | 128 | Bootloader secundario |
Cada byte ahorrado en el MBR amplía el espacio de NVRAM.
Etapas de Optimización
La optimización se realizó de forma iterativa con ARM-GCC y GNU Make:
- Implementación HAL ingenua: 25.192 bytes.
- -O0 sin depuración: 12.052 bytes.
- -Os -flto: 9.684 bytes.
- DeepSeek + -Os -flto: 2.124 bytes.
- -nostdlib, sin libc_init: 652 bytes.
- Tabla de vectores mínima: 324 bytes.
Banderas clave del compilador:
-nostdlib -nostartfiles -ffreestanding-ffunction-sections -fdata-sections -Wl,--gc-sections-Ospara tamaño,-mcpu=cortex-m4 -mthumb
Implementación en C
El código principal en un único archivo main.c accede directamente a los registros, sin HAL:
// main.c - Bootloader para STM32F407VE
#include <stdint.h>
#define APP_BASE 0x08060000UL
#define SRAM_BASE 0x20000000UL
#define SRAM_END 0x2001FFFFUL
#define FLASH_BASE 0x08000000UL
#define FLASH_END 0x080FFFFFUL
// Periféricos
#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));
}
// Inicializar DWT
static void dwt_init(void) {
*(volatile uint32_t*)0xE000EDF0 |= (1UL << 24);
DWT_CTRL |= 1;
}
// Retardo ~ms a 168 MHz
static void delay_ms(uint32_t ms) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = ms * 16000;
while ((DWT_CYCCNT - start) < cycles);
}
// Parpadear LED PE13 a 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();
}
Compilación del Proyecto
Makefile con soporte para depuración y empaquetado:
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
En startup_stm32f407xx.S, eliminamos bl __libc_init_array y recortamos la tabla de vectores a lo esencial (Pila, Reset, NMI, HardFault).
Lecciones Clave
- Tamaño de 324 bytes libera más de 15 KB para NVRAM en el primer sector.
- La validación de la tabla de vectores evita bloqueos por código corrupto.
- El temporizador DWT ofrece retardos precisos sin SysTick ni PLL.
- Acceso directo a registros minimiza el overhead al máximo.
- Compilación completa: ARM-GCC + script de enlace personalizado, sin biblioteca estándar.
— Editorial Team
Aún no hay comentarios.