임베디드 시스템 개발을 시작하는 순간, STM32 마이크로컨트롤러의 LED가 처음 깜빡일 때 우리는 성취감을 느낀다. 하지만 전원이 인가되고 첫 번째 명령어가 실행되기까지 칩 내부에서 어떤 일이 벌어질까? 이 글에서는 STM32가 전원을 받은 후 부트로더를 통해 어떻게 소프트웨어를 안정적으로 구동하는지 단계별로 살펴본다.
1. 전원 인가와 초기 하드웨어 준비
마이크로컨트롤러는 단순한 프로세서가 아니라 통합된 시스템이다. 전원이 공급되면 다음 순서로 하드웨어 상태를 정립한다:
- 전압 안정화: LDO 및 전력 관리 회로가 3.3V 출력을 생성하고, 모든 전원 도메인이 안정될 때까지 대기한다.
- 내장 클럭 활성화: 외부 크리스털 없이도 작동 가능한 HSI(High Speed Internal) 발진기가 최초 클럭 소스로 선택된다 (보통 8MHz).
- 리셋 해제: NRST 핀의 저전압 상태가 해제되면, 칩은 리셋 해제 지연(RESET RELEASE DELAY)을 거쳐 초기 실행 흐름으로 진입한다.
이 시점에서 CPU는 아직 메모리에 저장된 사용자 코드를 실행하지 않는다. 대신, 현재 선택된 부팅 모드에 따라 시작 주소를 결정한다. 이 설정은 BOOT0과 BOOT1 핀의 조합으로 제어된다.
| 부팅 소스 | BOOT1 | BOOT0 | 시작 주소 | 용도 |
|---|---|---|---|---|
| 주 플래시 메모리 | X | 0 | 0x08000000 | 일반 애플리케이션 실행 |
| 시스템 메모리 | X | 1 | 0x1FFF0000 | UART를 통한 펌웨어 다운로드 (ISP) |
| SRAM | 1 | 1 | 0x20000000 | 디버깅 또는 동적 로딩 |
2. 부트로더의 역할과 초기화 절차
가장 일반적인 경우인 주 플래시 메모리에서 부팅하는 시나리오를 기준으로 설명한다.
2.1 초기 스택 및 예외 벡터 설정
CPU는 첫 번째 4바이트를 초기 스택 포인터(SP)로, 두 번째 4바이트를 리셋 핸들러 주소로 읽어들인다. 이 정보는 벡터 테이블에 정의되어 있으며, 링커 스크립트를 통해 .isr_vector 섹션에 배치된다.
__attribute__((section(".isr_vector")))
void (* const vector_table[])(void) = {
(void*)(&__main_stack_end), // 초기 스택 최상위 주소
reset_handler_entry, // 리셋 발생 시 점프할 함수
nmi_interrupt, // NMI 처리기
hard_fault_handler, // 하드폴트 예외 처리
mem_manage_handler,
bus_fault_handler,
usage_fault_handler,
// ... 기타 예외 벡터
};
2.2 시스템 클럭 구성
HSI 클럭은 불안정할 수 있으므로, 대부분의 애플리케이션은 PLL을 사용해 더 높고 정확한 시스템 클럭을 생성한다. 예를 들어:
- HSI → PLL 입력
- PLL ×6 → 48MHz 시스템 클럭
- 클럭 분배: AHB, APB1, APB2 버스에 적절히 분배
2.3 애플리케이션 무결성 검사 (옵션)
보안 부트 또는 신뢰성 요구사항이 높은 시스템에서는 다음 검사를 수행할 수 있다:
- Firmware CRC 또는 SHA-256 해시 검증
- 디지털 서명 확인 (RSA/ECDSA 기반)
- 플래시 메모리의 보호 비트 상태 확인
3. 사용자 애플리케이션으로 제어 이양
모든 준비가 완료되면, 부트로더는 최종적으로 사용자 코드로 점프한다. 이 과정은 어셈블리 레벨에서 다음과 같이 구현된다:
; 사용자 애플리케이션 시작 주소 정의
.equ APP_START_ADDR, 0x08000000
; 스택 포인터 및 프로그램 카운터 설정
ldr r0, =APP_START_ADDR
ldr sp, [r0] ; 첫 번째 워드: 스택 포인터 값
ldr pc, [r0, #4] ; 두 번째 워드: 리셋 핸들러 주소
이 순간 PC(Program Counter)는 0x08000000 위치 이후의 reset_handler_entry로 이동하며, C 런타임 환경 초기화(__libc_init_array) 및 main() 함수 호출이 시작된다.
결국 LED가 깜빡이는 것은 단순한 출력이 아니라, 수십 개의 하드웨어 블록과 소프트웨어 단계가 정밀하게 협업한 결과물이다. 이러한 이해는 디버깅이나 부트로더 커스터마이징 시 큰 도움이 된다.