임베디드 면접 용어 사전
MCU·ARM스택 포인터

MSP / PSP

메인 스택 포인터와 프로세스 스택 포인터. RTOS는 커널에 MSP, 태스크에 PSP를 쓴다.

Cortex-M이 갖는 두 개의 스택 포인터. Main Stack Pointer와 Process Stack Pointer다.

왜 두 개인가

MSP : 커널·예외 처리용 스택 PSP : 애플리케이션(태스크)용 스택

하나만 있으면 태스크가 스택을 넘치게 했을 때 커널의 스택까지 함께 망가진다. 분리하면 커널은 자기 스택을 계속 안전하게 쓸 수 있어 오류를 처리하고 복구할 여지가 생긴다.

언제 무엇이 쓰이나

  • 리셋 직후 — → MSP (항상)
  • Handler 모드 — → MSP (항상. 선택 불가)
  • Thread 모드 — → CONTROL 레지스터의 SPSEL 비트로 선택
CONTROL.SPSEL = 0 → Thread 모드에서 MSP 사용 (기본값, 베어메탈)
CONTROL.SPSEL = 1 → Thread 모드에서 PSP 사용 (RTOS 태스크)

리셋 시 MSP는 벡터 테이블의 0번 항목에서 자동으로 로드된다. 그래서 startup 코드가 스택 포인터를 설정할 필요가 없다.

RTOS에서의 구조 — 이것이 존재 이유다

[커널 / 예외 핸들러]  ── MSP ──► 커널 스택 (하나, 크기 고정)

[태스크 A] ─┐
[태스크 B] ─┼── PSP ──► 각 태스크마다 자기 스택
[태스크 C] ─┘           (전환할 때 PSP 값을 바꿔치기)

컨텍스트 스위칭이란 결국 PSP를 다른 태스크의 스택으로 바꾸는 것이다.

// PendSV 핸들러의 핵심 (개념)
__asm volatile (
    "MRS   R0, PSP        \n"   // 현재 태스크의 스택 포인터를 읽어
    "STMDB R0!, {R4-R11}  \n"   // 하드웨어가 저장 안 한 레지스터를 저장
    "STR   R0, [현재TCB]   \n"   // TCB에 스택 포인터 기록
    "…다음 태스크 선택…    \n"
    "LDR   R0, [다음TCB]   \n"
    "LDMIA R0!, {R4-R11}  \n"   // 다음 태스크의 레지스터 복원
    "MSR   PSP, R0        \n"   // PSP를 다음 태스크 스택으로
);

R4~R11만 수동으로 저장하는 이유 — R0~R3, R12, LR, PC, xPSR은 예외 진입 시 하드웨어가 자동 스태킹해 주기 때문이다.

EXC_RETURN — 복귀할 때 무엇을 쓸지 결정

예외에 진입하면 LR에 일반 주소가 아닌 특수 값이 들어간다.

0xFFFFFFF9 → Handler 모드에서 왔다. MSP로 복귀 0xFFFFFFFD → Thread 모드 + PSP 사용. PSP로 복귀 ← RTOS 태스크 0xFFFFFFE9 / 0xFFFFFFED → FPU 컨텍스트 포함

이 값으로 분기하면 CPU가 어느 스택에서 복원할지 판단한다. RTOS 포팅 코드에서 이 값을 조작하는 것을 볼 수 있다.

스택 크기 산정

MSP : 가장 깊은 인터럽트 중첩 × (자동 스태킹 32바이트 + 핸들러 지역변수)

    • 중첩 깊이만큼 PSP : 태스크별로 개별 산정

인터럽트가 중첩되면 MSP를 더 쓴다는 점을 놓치기 쉽다. 우선순위가 다른 인터럽트 5개가 겹칠 수 있다면 그만큼 여유가 필요하다.

베어메탈에서는

CONTROL.SPSEL = 0 (기본) → 전부 MSP 하나로 동작

PSP를 쓸 이유가 없다. 그래서 베어메탈 코드에서는 이 개념을 만날 일이 없고, RTOS를 도입하는 순간 반드시 이해해야 하는 주제가 된다.

확인 방법

uint32_t msp = __get_MSP();
uint32_t psp = __get_PSP();
uint32_t ctrl = __get_CONTROL();     // bit1 = SPSEL, bit0 = nPRIV

디버거에서 HardFault를 분석할 때 어느 스택에 프레임이 쌓였는지 판단하려면 EXC_RETURN 값(LR)을 먼저 봐야 한다. 잘못된 스택을 뒤지면 엉뚱한 주소가 나온다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — MCU·ARM Cortex-M — 레지스터·버스·동작모드·NVIC·부팅