인터럽트 신호가 발생한 시점부터 핸들러의 첫 명령이 실행될 때까지의 시간 (interrupt latency).
구성 요소
① 하드웨어 인식 — 신호가 NVIC에 도달
② 현재 명령 완료 대기 — 실행 중이던 명령이 끝날 때까지
③ 자동 스태킹 — 레지스터 8개 저장
④ 벡터 인출 — 핸들러 주소 읽기
⑤ 파이프라인 채우기
─────────────────────────
Cortex-M3/M4 : 12 사이클 (③④가 병렬로 진행되어 짧다)
Cortex-M0 : 16 사이클
Cortex-M7 : 12 사이클
168MHz에서 12사이클 ≈ 71ns. 매우 빠르다.
하지만 실제 지연은 훨씬 길 수 있다 — 이것이 핵심
최악의 지연 = 12 사이클
-
- 인터럽트가 차단된 시간 ★ 가장 큰 요인
-
- 더 높은 우선순위 ISR 실행 시간
-
- 같은 우선순위 ISR이 실행 중이면 그것이 끝날 때까지
-
- 긴 명령의 완료 대기
-
- 플래시 웨이트 스테이트
-
- 버스 경합 (DMA와 충돌)
★ 임계 구역에서 인터럽트를 오래 막는 것이 지연의 최대 원인이다. 12사이클을 아끼려고 최적화해도, 어딘가에서 100μs 동안 인터럽트를 막고 있으면 아무 의미가 없다.
긴 명령 — 무시 못 할 요인
-
LDM / STM (다중 레지스터 로드/스토어)
-
레지스터 16개를 옮기면 16사이클 이상
-
Cortex-M3부터는 중단 가능(abandon & restart)하지만
- M0에서는 완료를 기다려야 한다
-
정수 나눗셈 SDIV/UDIV → 2~12 사이클
측정 방법
// GPIO 토글로 오실로스코프 측정 — 가장 정확
void EXTI0_IRQHandler(void) {
GPIOA->BSRR = PIN_DEBUG; // 진입 즉시 High
handle();
GPIOA->BSRR = PIN_DEBUG << 16; // 종료 시 Low
EXTI->PR = EXTI_PR_PR0;
}
트리거 신호와 디버그 핀의 시간 차이가 곧 지연이다. 소프트웨어로는 측정할 수 없는 값이라 오실로스코프나 로직 애널라이저가 필요하다.
// DWT 사이클 카운터 — 핸들러 내부 실행 시간 측정용
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
uint32_t t0 = DWT->CYCCNT;
// …
uint32_t cycles = DWT->CYCCNT - t0;
지연을 줄이는 방법
| 방법 | 설명 |
|---|---|
| 임계 구역을 짧게 | 가장 효과가 크다. 몇 줄 이내로 |
| BASEPRI 사용 | 전체 차단 대신 낮은 우선순위만 차단 |
| ISR을 짧게 | 다른 인터럽트의 대기를 줄인다 |
| 우선순위 설계 | 긴급한 것을 높게 |
| ISR을 RAM/TCM에 | 플래시 웨이트 스테이트 회피 |
| 핸들러 코드 캐시 워밍 | M7에서 I-Cache 미스 방지 |
지터 — 지연보다 중요할 때가 있다
- 지연(latency) — : 평균적으로 얼마나 걸리는가
- 지터(jitter) — : 그 값이 얼마나 흔들리는가
모터 제어나 샘플링에서는 지터가 더 치명적이다. 매번 100ns 늦는 것은 보정할 수 있지만, 50~500ns로 흔들리면 보정이 불가능해 진동이나 노이즈로 나타난다.
지터를 줄이려면
- 인터럽트 중첩을 없앤다 (우선순위를 같게)
- 임계 구역 길이를 일정하게
- 캐시·분기 예측의 영향을 줄인다 (TCM 배치)
- 타이머 하드웨어로 직접 트리거 (소프트웨어 개입 제거)
하드 실시간에서의 분석
데드라인 = 최악 지연 + 최악 ISR 실행 시간 ≤ 요구 시간
평균이 아니라 최악(WCET)으로 계산해야 한다. 평균 1μs여도 최악이 200μs면 그것이 설계 기준이다.
임계 구역, 인터럽트 중첩, 플래시 대기, DMA 경합을 모두 더한 최악의 경로를 찾아야 하며, 이것이 실시간 시스템 설계의 핵심 작업이다.