기술 가이드

Cortex-M NVIC 우선순위: 논리 값·레지스터·선점 그룹 검증

CMSIS 논리 우선순위와 레지스터 바이트를 구분하고, PRIGROUP의 선점·하위 우선순위 4,032개 조합을 C11로 검증합니다.

목차

UART 수신 인터럽트가 긴 타이머 처리에 밀릴 때는 우선순위의 숫자, 구현 비트 수, 선점 그룹을 함께 확인해야 합니다. Cortex-M3·M4·M7에서 사용할 CMSIS 설정과 PC에서 실행할 계산 모델을 나누어 살펴봅니다. Arm의 NVIC 공식 문서는 작은 우선순위 숫자가 더 높은 우선순위라고 설명합니다.

논리 우선순위와 레지스터 바이트

디바이스 헤더의 __NVIC_PRIO_BITS가 4라면 논리 우선순위는 0~15입니다. 실제 우선순위 레지스터는 상위 4비트를 사용하므로 논리 값 6의 저장 바이트는 0x60입니다. CMSIS Cortex-M4 구현의 __NVIC_SetPriority는 이 이동을 수행합니다. 따라서 NVIC_SetPriority(UART_IRQn, 6)에 다시 이동한 0x60을 넣지 마세요. 이 예에서 0x60을 전달하면 하위 4비트가 0이어서 의도와 다른 최고 우선순위가 저장될 수 있습니다.

입력·관찰 위치 4비트 구현에서 값 의미
CMSIS API 인자 6 논리 우선순위
레지스터 바이트 0x60 상위 비트에 배치한 값
NVIC_GetPriority 반환 6 다시 논리 값으로 환산한 값

비트 수는 제품별 헤더와 데이터시트로 확인합니다. 같은 Cortex-M4라도 항상 4비트라고 가정하지 않습니다. NMI와 HardFault는 고정 우선순위이며 일반 IRQ처럼 설정할 수 없습니다.

PRIGROUP과 선점·하위 우선순위

선점 우선순위가 같은 IRQ끼리는 하위 우선순위 숫자만 다르다고 서로 선점하지 않습니다. 하위 우선순위는 같은 선점 그룹에서 동시에 대기 중인 인터럽트의 선택 순서를 정하는 데 사용합니다. 숫자 값만 보고 선점 관계를 판단하지 마세요. 여기서 그룹 번호는 AIRCR의 PRIGROUP 원시 값입니다. 벤더 HAL의 NVIC_PRIORITYGROUP_* 매크로 이름과 숫자를 혼동하지 않습니다.

CMSIS 계산을 정리하면 선점 비트 수는 min(__NVIC_PRIO_BITS, 7 - PRIGROUP)이고 나머지는 하위 우선순위 비트입니다. 아래 표는 4비트 구현을 가정한 결과입니다.

PRIGROUP 선점 비트 하위 비트 가능한 선점/하위 범위
0~3 4 0 0~15 / 0
4 3 1 0~7 / 0~1
5 2 2 0~3 / 0~3
6 1 3 0~1 / 0~7
7 0 4 0 / 0~15

실제 펌웨어에서는 초기화 단계에서 NVIC_SetPriorityGrouping(5)를 정한 후 NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 1, 2)의 반환 값 6을 NVIC_SetPriority에 전달합니다. 표의 계산은 CMSIS 인코딩·디코딩 함수와 대응합니다. 범위를 벗어난 입력은 CMSIS 내부 마스크로 잘릴 수 있으므로 호출 전에 검증하는 편이 좋습니다. Cortex-M0·M0+에는 이 그룹 설정 예제를 적용하지 않습니다.

C11로 4,032개 조합 검증하기

다음 코드는 MCU 레지스터에 접근하지 않는 계산 모델입니다. 그룹 값·입력 범위를 먼저 검사하고, 구현 비트 수 3~8과 PRIGROUP 0~7의 모든 유효 조합에서 인코딩 후 원래 선점·하위 값이 복원되는지 확인합니다. 잘못된 그룹, 비트 수, 우선순위도 거부합니다. nvic_priority.c로 저장해 gcc -std=c11 -Wall -Wextra -Werror nvic_priority.c -o nvic_priority로 빌드하세요. Linux에서는 ./nvic_priority, Windows에서는 ./nvic_priority.exe를 실행합니다.

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

/* Host model: no MCU registers are accessed. */
static bool encode(unsigned bits, unsigned group, unsigned pre,
unsigned sub, unsigned *logical, unsigned *reg)
{
if (bits == 0u || bits > 8u || group > 7u)
return false;
const unsigned pre_bits = bits < 7u - group ? bits : 7u - group;
const unsigned sub_bits = bits - pre_bits;
if (pre >= (1u << pre_bits) || sub >= (1u << sub_bits))
return false;
*logical = (pre << sub_bits) | sub;
*reg = *logical << (8u - bits);
return true;
}

int main(void)
{
unsigned logical = 0u, reg = 0u;
assert(encode(4u, 5u, 1u, 2u, &logical, &reg));
assert(logical == 6u && reg == 0x60u);
assert(!encode(4u, 5u, 4u, 0u, &logical, &reg));
assert(!encode(4u, 5u, 0u, 4u, &logical, &reg));
assert(!encode(4u, 8u, 0u, 0u, &logical, &reg));
assert(!encode(0u, 0u, 0u, 0u, &logical, &reg));
assert(!encode(9u, 0u, 0u, 0u, &logical, &reg));
unsigned count = 0u;
for (unsigned bits = 3u; bits <= 8u; ++bits) {
for (unsigned group = 0u; group <= 7u; ++group) {
unsigned pb = bits < 7u - group ? bits : 7u - group;
unsigned sb = bits - pb;
for (unsigned pre = 0u; pre < (1u << pb); ++pre) {
for (unsigned sub = 0u; sub < (1u << sb); ++sub) {
assert(encode(bits, group, pre, sub, &logical, &reg));
assert((logical >> sb) == pre);
assert((logical & ((1u << sb) - 1u)) == sub);
assert((reg >> (8u - bits)) == logical);
++count;
}
}
}
}
assert(count == 4032u);
puts("NVIC priority: 4032 round trips passed; group5 priority=6 register=0x60");
return 0;
}

검증 출력:

NVIC priority: 4032 round trips passed; group5 priority=6 register=0x60

이 결과는 계산과 입력 경계를 검증합니다. 실제 인터럽트 응답 시간, 마스킹, RTOS 임계 구역까지 실측한 결과는 아닙니다.

보드에서 확인할 순서

  1. 사용 중인 MCU 헤더의 __NVIC_PRIO_BITS를 확인합니다.
  2. 시작 코드·HAL·RTOS가 마지막으로 설정한 그룹을 NVIC_GetPriorityGrouping()으로 읽습니다. 초기화 뒤 다른 코드가 그룹을 바꾸면 같은 숫자의 선점 의미도 바뀝니다.
  3. NVIC_GetPriority()와 NVIC_DecodePriority()로 최종 값을 확인하고 설계한 선점 관계와 비교합니다.
  4. GPIO 토글이나 타임스탬프로 ISR 시작 지연을 측정합니다. 긴 ISR, 전역 인터럽트 마스크, 같은 선점 그룹은 우선순위를 높여도 지연 원인이 될 수 있습니다.
  5. RTOS API를 호출하는 ISR은 해당 포트의 허용 우선순위 규칙을 별도로 적용합니다. 이 글의 논리 값과 RTOS 설정에 쓰는 이동된 레지스터 값을 구분하세요.

공식 자료 확인: 2026-10-11. 예제는 계산 모델을 실제 컴파일·실행해 확인했습니다.

관련 글

댓글

첫 댓글을 남겨 보세요.

관리자 확인 후 게시됩니다.

처리 중입니다...

잠시만 기다려주세요.