Chargeur d'amorçage MBR compact pour STM32F407VE : Seulement 324 octets
Ce chargeur d'amorçage principal (MBR) pour le STM32F407VE occupe le premier secteur Flash à l'adresse 0x08000000 et transmet le contrôle au chargeur secondaire à 0x08060000. Avant le saut, il valide la table des vecteurs d'interruption : pointeur de pile dans la plage SRAM (0x20000000–0x2001FFFF), adresse de ResetHandler en Flash (0x08000000–0x080FFFFF) avec le bit de mode Thumb activé.
En cas d'échec de validation, il signale l'erreur en clignotant la LED sur la broche PE13 à 10 Hz (période de 100 ms, rapport cyclique de 50 %). Le code est réduit à 324 octets en supprimant la bibliothèque standard, les interruptions externes, et en utilisant le temporisateur DWT du cœur pour les délais.
Schéma de la Flash NOR :
| Secteur | Adresse de début | Taille, Ko | Contenu |
|---------|------------------|------------|---------|
| 0 | 0x08000000 | 16 | Chargeur principal |
| 1–3 | 0x08004000–0x0800C000 | 16×3 | NVRAM |
| 4–6 | 0x08010000–0x08040000 | 64+128+128 | Application |
| 7 | 0x08060000 | 128 | Chargeur secondaire |
Chaque octet économisé sur le MBR libère de l'espace pour la NVRAM.
Étapes d'optimisation
L'optimisation a été itérative avec ARM-GCC et GNU Make :
- Implémentation HAL naïve : 25 192 octets.
- -O0 sans debug : 12 052 octets.
- -Os -flto : 9 684 octets.
- DeepSeek + -Os -flto : 2 124 octets.
- -nostdlib, sans libc_init : 652 octets.
- Table de vecteurs minimale : 324 octets.
Principaux drapeaux du compilateur :
-nostdlib -nostartfiles -ffreestanding-ffunction-sections -fdata-sections -Wl,--gc-sections-Ospour la taille,-mcpu=cortex-m4 -mthumb
Implémentation en C
Le code principal dans un seul fichier main.c accède directement aux registres, sans HAL :
// main.c - Chargeur d'amorçage pour STM32F407VE
#include <stdint.h>
#define APP_BASE 0x08060000UL
#define SRAM_BASE 0x20000000UL
#define SRAM_END 0x2001FFFFUL
#define FLASH_BASE 0x08000000UL
#define FLASH_END 0x080FFFFFUL
// Périphériques
#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));
}
// Initialisation DWT
static void dwt_init(void) {
*(volatile uint32_t*)0xE000EDF0 |= (1UL << 24);
DWT_CTRL |= 1;
}
// Délai ~ms à 168 MHz
static void delay_ms(uint32_t ms) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = ms * 16000;
while ((DWT_CYCCNT - start) < cycles);
}
// Clignotement 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();
}
Construction du projet
Makefile avec support debug et emballage :
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
Dans startup_stm32f407xx.S, nous avons supprimé bl __libc_init_array et réduit la table de vecteurs aux essentiels (Pile, Reset, NMI, HardFault).
Points clés
- Taille de 324 octets libère plus de 15 Ko pour la NVRAM dans le premier secteur.
- Validation de la table de vecteurs évite les blocages sur code corrompu.
- Temporisateur DWT pour des délais précis sans SysTick ni PLL.
- Accès direct aux registres minimise les surcoûts.
- Build complet : ARM-GCC + script de lien personnalisé, sans bibliothèque standard.
— Editorial Team
Aucun commentaire pour le moment.