STM32H7 의 QSPI XIP 활용: 외부 메모리 기반의 효율적인 실행 구성 가이드

내부 플래시 한계 극복을 위한 외부 QSPI 실행 전략

STM32H7 시리즈를 사용하는 임베디드 프로젝트에서 GUI 프레임워크나 복잡한 데이터 처리 알고리즘을 도입할 경우, 온보드 내부 플래시 용량이 부족해지는 문제가 빈번히 발생한다.单纯的 코드 최적화나 기능 축소는 근본적인 해결책이 되지 못하는 경우가 많으므로, CPU 가 외부 QSPI Flash 에 직접 접근하여 코드를 실행하는 XIP(Execute In Place) 방식을 도입해야 한다. 이는 단순한 저장소 확장이 아닌 시스템 아키텍처의 개편을 수반하므로 주의 깊은 설계가 요구된다.

1. QSPI Flash 의 제자리 실행 원리 분석

외부 비휘발성 메모리에서 코드를 실행하기 위해서는 일반적 읽기 쓰기 방식 대신 메모리 맵(Memory Map) 모드를 활성화해야 한다. 이 모드에서 MCU 는 QSPI Flash 의 주소를 물리 주소 공간에 매핑하여 마치 내부 메모리를 다루듯 인스트럭션을 페칭할 수 있다.

1.1 메모리 매핑 초기화 설정

HAL 라이브러리를 사용할 때, 명령어 모드와 데이터 라인 설정이 성능에 지대한 영향을 미친다. 다음은 재작성된 초기화 설정 예시이다:


typedef struct {
    uint32_t instr_lines;
    uint32_t addr_lines;
    uint32_t addr_size;
    uint32_t data_lines;
} QSPICmdCfg_t;

// 메모리 매핑 모드 전환을 위한 설정 구조체 정의
QSPICmdCfg_t map_config = {
    .instr_lines = QSPI_MODE_1_LINE,
    .addr_lines  = QSPI_MODE_4_LINES,
    .addr_size   = 32, // 32-bit 어드레스 지원
    .data_lines  = QSPI_MODE_4_LINES
};

void SetupQSPI_XIP(QSPI_HandleTypeDef *h_qspi, QSPICmdCfg_t *cfg) {
    HAL_QSPIMemoryMappedCmd(h_qspi, cfg); 
    // 추가적으로 DDR 모드 및 클록 분배 조정 가능
}

1.2 성능 특성 비교

실제 환경에서의 동작 속도는 내부 플래시보다 저하될 수 있으나, 적절한 캐싱 전략을 통해 최소화할 수 있다. 아래는 일반적인 성능 벤치마크 결과이다.

행 위치 상대 속도 (%) 소비 전류 (mA) 비고
Internal Flash 100% 약 80 기준값
External QSPI (Raw) 약 80~85% 약 95 캐시 미활용 시
External QSPI + Cache 약 95% 이상 약 88 ART 가속기 포함

2. 부트로더와 애플리케이션 분리 아키텍처

시스템을 안정적으로 구동하려면 외부 QSPI 에서 실행될 애플리케이션과 이를 제어하는 부트로더를 명확히 분리해야 한다.

2.1 컨트롤 이전 로직

부트로더가 초기화를 마친 후 메인 애플리케이션으로 제어권을 넘길 때는 스택 포인터와 프로그램 카운터를 정확하게 설정해야 하며, 예측 실행 단계를 방지하기 위한 지연 처리가 권장된다.


; Stack Pointer 를 r0 에 저장된 값으로 변경하고 PC 를 r1 으로 이동
.global JumpToApplication
.type JumpToApplication, %function
JumpToApplication:
    MOV     r0, sp          ; 현재 스택 보존 필요시 고려
    MSR     MSP, r0         ; MSP 레지스터 업데이트
    BX      r1              ; 목표 주소로 브랜치
    NOP                     ; 파이프라인 정리 방지
    B       .               ; 무한 루프以防误入

2.2 링크 스크립트 및 디버깅 구성

컴파일 단계에서 생성되는 실행 파일은 실제 QSPI 영역 (보통 0x90000000 시작) 에 배치되도록 지정되어야 한다. 또한 인터럽트 벡터 테이블(VTOR) 도 외부 메모리 주소로 리맵핑되어야 한다.


; S-Record 또는 ELF 를 위한 메모리 영역 정의
RAM  ORIGIN = 0x24000000 LENGTH = 0x080000
QSPI_ORGIN = 0x90000000 LENGTH = 0x2000000

; 스캐터 로드 파일 예시
IROM1 0x90000000 0x1000000 {
  *(.text*)
  *(.rodata*)
}
RW_IRAM1 0x20000000 0x080000 {
  *(.data*)
  *(.bss*)
}

3. 툴체인 통합 및 다운로드 알고리즘 설정

Keil MDK 환경에서 외부 QSPI 에 펌웨어를 프로그래밍하려면 전용 플래시 알고리즘 (.FLM) 이 필요하다. 표준 알고리즘은 내부 플래시에만 적용되므로 커스텀 개발이 필수적이다.

3.1 커스텀 드라이버 구현 사항

플래시 알고리즘 내에서는 하드웨어 초기화, 섹터 지우기, 쓰기 연산을 담당하는 함수를 정의해야 한다.


int Driver_Init(uint32_t start_addr, uint32_t clk, int fn) {
    // GPIO 설정 및 QSPI 핀 모드 활성화
    ConfigureQSPI_GPIO();
    Init_QSPI_HalInstance();
    return 0;
}

int Block_Erase(uint32_t target_addr) {
    // 해당 주소의 블록 지우기 명령 전송
    ExecuteEraseSequence(target_addr);
    WaitUntilReady();
    return 0;
}

3.2 디버깅 중 발생할 수 있는 문제점

개발 과정 중에 마주칠 수 있는 장애물과 대응 방안을 정리했다.

  • 알고리즘 로드 실패: 디버거가 사용하는 IRAM 영역이 부족하다. RAM 할당 크기를 확대한다.
  • 다운로드 후 부팅 불가: VTOR (Vector Table Offset Register) 가 초기화되지 않았다. 시스템 초기화 루틴에서 수동 설치가 필요하다.
  • 단조작점 충돌: D-Cache 와 메모리 내용 불일치. 캐시 무효화(Clean/Invalid) 를 수행하거나 디버그 시 캐시 사용을 제한한다.

4. 성능 최적화 및 고급 기법

XIP 모드에서도 최대한의 성능을 끌어내기 위해 하드웨어 기능을 적극적으로 활용할 수 있다.

4.1 하이브리드 메모리 배치

시간 민감도가 높은 ISR(Interrupt Service Routine) 은 내부 플래시에 잔류시키면서 나머지 로직은 QSPI 로 분리하는 전략을 사용한다. 컴파일러 속성을 이용해 섹션을 제어한다.


// 내부 플래시 강제 배치 속성
__attribute__((section(".fast_code")))
void HighPrio_ISR_Handler(void) {
    // 처리 시간 보장が必要な 코드
}

4.2 하드웨어 가속기 활용

ARM 코어의 ART Accelerator 와 I-Cache 를 적절히 구성하면 QSPI 접근 지연을 상쇄할 수 있다.

  1. CORE_CPACR 레지스터를 수정하여 FPU 및 ART 액세스 권한 부여.
  2. QUADSPI 제어기 내에서 CSHT(Chip Select Hold Time) 값을 최소한의 사이클로 조정하여 오버헤드 감소.
  3. 주요 실행 루프 영역의 I-Cache 라인 고정(Lockdown) 을 통해 페이지아웃 방지.

5. 양산 및 업데이트 프로세스 구축

제품 출시 시 대량 서명을 위한 자동화 스크립트와 필드에서의 펌웨어 업그레이드(FOTA) 메커니즘이 필요하다.

5.1 안전한 업데이트 검증

새로운 펌웨어를 외부 플래시의 다른 뱅크(Bank) 로 작성한 후, 체크섬이나 CRC 검증을 통과한 경우에만 활성 뱅크로 스위칭하도록 설계한다. 검증 로직은 내부 RAM 에서 실행되어야 안전성이 확보된다.


bool CheckFirmwareIntegrity(uint32_t firmware_base) {
    uint32_t stored_crc = ReadLastWord(firmware_base);
    // 전체 이미지 크기의 CRC 계산
    uint32_t calc_crc = CalculateCRC(firmware_base, IMAGE_SIZE); 
    
    return (stored_crc == calc_crc);
}

태그: STM32H7 QSPI XIP Embedded_C Keil_MDK

5월 23일 06:10에 게시됨