홈으로 돌아가기

MBR 324바이트 STM32F407VE: 컴팩트 부트로더

324 bytes 크기의 STM32F407VE용 최소 MBR 부트로더 개발. 0x08060000 주소의 보조 부트로더로 제어권 이전 전에 벡터 테이블 검사. 오류 시 PE13의 LED 깜빡임. 전체 코드, Makefile 및 최적화 단계.

검증 포함 324-byte MBR 부트로더 STM32F407VE
Advertisement 728x90

STM32F407VE용 324바이트 컴팩트 MBR 부트로더

STM32F407VE용 이 기본 부트로더(MBR)는 플래시 첫 번째 섹터(0x08000000)에 위치하며, 0x08060000의 보조 부트로더로 제어를 넘깁니다. 점프 전에 인터럽트 벡터 테이블을 검증합니다: SRAM 범위(0x20000000–0x2001FFFF)의 스택 포인터, 플래시(0x08000000–0x080FFFFF) 내 ResetHandler 주소(Thumb 모드 비트 설정).

검증 실패 시 PE13 핀 LED를 10Hz(주기 100ms, 듀티 50%)로 깜빡여 오류를 표시합니다. 표준 라이브러리, 외부 인터럽트 제거, DWT 코어 타이머로 지연 구현으로 코드를 324바이트로 최적화했습니다.

NOR 플래시 레이아웃:

Google AdInline article slot

| 섹터 | 시작 주소 | 크기, KB | 내용 |

|------|---------------|----------|------------------|

| 0 | 0x08000000 | 16 | 기본 부트로더 |

Google AdInline article slot

| 1–3 | 0x08004000–0x0800C000 | 16×3 | NVRAM |

| 4–6 | 0x08010000–0x08040000 | 64+128+128 | 애플리케이션 |

| 7 | 0x08060000 | 128 | 보조 부트로더 |

Google AdInline article slot

MBR에서 절약한 매 바이트가 첫 섹터 NVRAM 공간을 늘립니다.

최적화 단계

ARM-GCC와 GNU Make로 반복 최적화:

  • 초기 HAL 구현: 25,192 바이트.
  • -O0 디버그 제거: 12,052 바이트.
  • -Os -flto: 9,684 바이트.
  • DeepSeek + -Os -flto: 2,124 바이트.
  • -nostdlib, no libc_init: 652 바이트.
  • 최소 벡터 테이블: 324 바이트.

주요 컴파일러 플래그:

  • -nostdlib -nostartfiles -ffreestanding
  • -ffunction-sections -fdata-sections -Wl,--gc-sections
  • -Os (크기 최적화), -mcpu=cortex-m4 -mthumb

C 구현

단일 main.c 파일로 HAL 없이 레지스터 직접 접근:

// main.c - STM32F407VE 부트로더
#include <stdint.h>

#define APP_BASE        0x08060000UL
#define SRAM_BASE       0x20000000UL
#define SRAM_END        0x2001FFFFUL
#define FLASH_BASE      0x08000000UL
#define FLASH_END       0x080FFFFFUL

// 주변 장치
#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));
}

// DWT 초기화
static void dwt_init(void) {
    *(volatile uint32_t*)0xE000EDF0 |= (1UL << 24);
    DWT_CTRL |= 1;
}

// 168MHz에서 ~ms 지연
static void delay_ms(uint32_t ms) {
    uint32_t start = DWT_CYCCNT;
    uint32_t cycles = ms * 16000;
    while ((DWT_CYCCNT - start) < cycles);
}

// 10Hz로 PE13 LED 깜빡임
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();
}

프로젝트 빌드

디버그 및 패키징 지원 Makefile:

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

startup_stm32f407xx.S에서 bl __libc_init_array 제거하고 벡터 테이블을 필수 항목(Stack, Reset, NMI, HardFault)으로 축소했습니다.

주요 요점

  • 324바이트 크기로 첫 섹터 NVRAM 15+KB 확보.
  • 벡터 테이블 검증으로 손상 코드 방지.
  • DWT 타이머로 SysTick/PLL 없이 정밀 지연.
  • 레지스터 직접 접근으로 오버헤드 최소화.
  • 전체 빌드: ARM-GCC + 커스텀 링커 스크립트, 표준 라이브러리 없음.

— Editorial Team

Advertisement 728x90

다음 읽기