제8부 실시간 시스템 응용
8.1 MCU의 실시간성 이해 — 결정론적 타이밍이란
제8부는 UNO Q의 두 브레인 중 MCU(STM32U585) 쪽을 실시간 제어의 대상으로 삼아 심층적으로 다룬다. 이 장은 그 출발점이다 — "왜 MCU를 별도로 두었는가", "결정론적이라는 말이 정확히 무엇을 뜻하는가"를 개념부터 정리한다. 2.1(두 프로세서 아키텍처)과 3.2(Zephyr 기반 Arduino Core)에서 다룬 내용을 실시간성이라는 각도로 다시 읽는 장이라고 보면 된다.
8.1.1 되짚기 — 두 브레인, 두 개의 역할
UNO Q는 하나의 보드에 성격이 전혀 다른 프로세서 두 개를 탑재했다.
- MPU — Qualcomm Dragonwing QRB2210, 쿼드코어 Arm Cortex-A53, Debian Linux 구동 (2.1, 3.1)
- MCU — STMicroelectronics STM32U585, Cortex-M33 160MHz, 2MB 플래시 · 786KB SRAM, Zephyr 기반 Arduino Core 구동 (2.1, 3.2)
두 브레인이 왜 필요한가에 대한 가장 흔한 오해는 "MCU는 저사양이라 보조용"이라는 생각이다. 이는 사실이 아니다. MCU가 여기 있는 이유는 사양이 낮아서가 아니라 다른 종류의 보장을 제공하기 때문이다. Linux는 "평균적으로 빠르다"에 강점이 있고, Zephyr 기반 MCU는 "이 작업은 항상 이 시간 안에 끝난다"에 강점이 있다. 실시간 제어가 필요로 하는 것은 후자다.
이 장부터 8.5까지는 오른쪽(E) 분기를 심층적으로 다룬다.
8.1.2 실시간 시스템의 기초 개념
"실시간(real-time)"이라는 말은 일상어로는 "빠르다"는 뜻으로 쓰이지만, 임베디드·제어 분야에서는 다른 뜻이다. 빠른 것이 아니라 예측 가능한 것이 실시간이다. 아무리 평균 응답이 빨라도 가끔 한 번씩 크게 늦어지면 실시간 시스템이 아니다.
8.1.2.1 지연과 지터
두 값을 구분해야 한다.
- 지연(latency): 이벤트 발생부터 반응까지 걸리는 시간 그 자체. "인터럽트가 들어오고 5μs 후에 ISR이 시작됐다"의 5μs.
- 지터(jitter): 그 지연이 매번 얼마나 불규칙한가. 지연이 항상 정확히 5μs면 지터는 0에 가깝다. 어떤 때는 5μs, 어떤 때는 40μs, 어떤 때는 3ms면 지터가 크다.
제어 루프 입장에서는 지연 자체보다 지터가 더 위험한 경우가 많다. 지연이 일정하면 그만큼을 보상해서 설계할 수 있지만, 지터는 예측이 안 되므로 보상이 불가능하다. 예를 들어 모터 PID 루프가 "1ms마다 정확히" 실행되어야 하는데 어떤 사이클은 0.9ms, 어떤 사이클은 3ms가 걸리면 적분항이 왜곡되고 제어가 진동하거나 발산할 수 있다.
8.1.2.2 하드·소프트·펌 실시간
| 구분 | 정의 | 데드라인을 놓치면 | UNO Q 맥락의 예 |
|---|---|---|---|
| Hard real-time | 데드라인이 시스템 정의의 일부 | 시스템 실패로 간주 (안전 문제로 직결될 수 있음) | 모터 PID 제어 주기(8.4), 드론 외부 제어기의 setpoint 스트리밍(8.5) |
| Soft real-time | 데드라인을 넘겨도 동작은 계속되지만 품질이 떨어짐 | 사용자 경험 저하 (끊김·지연) | 화면 UI 갱신, 로그 업로드, 웹 대시보드(MPU 쪽) |
| Firm real-time | 늦은 결과는 쓸모가 없어져 버려지지만, 시스템 자체는 안전 | 그 결과만 폐기 | 센서 프레임 하나를 놓쳐도 다음 프레임으로 넘어가는 비전 파이프라인 |
UNO Q에서 이 세 등급은 자연스럽게 두 브레인으로 나뉜다. hard real-time에 가까운 일은 MCU에, soft/firm에 가까운 일은 MPU에 놓는 것이 이 책 제8부 전체의 기본 설계 원칙이다. 구체적인 배치 기준은 8.2.4에서 표로 정리한다.
8.1.3 MPU(Debian Linux)가 결정론적이지 않은 이유
Debian Linux 자체가 나쁜 운영체제라서가 아니다. 범용 운영체제는 애초에 결정론을 목표로 설계되지 않는다 — 목표는 여러 프로세스 사이의 처리량과 응답성 균형이다. 구체적으로 지터가 생기는 지점들:
- 스케줄러: Linux 기본 스케줄러(CFS 계열)는 "공평한 시분할"이 목적이지 "이 태스크는 반드시 t 시간 안에 깨어난다"를 보장하지 않는다. 우선순위를 높여도(
nice,chrt) 다른 커널 작업·인터럽트 처리와 경합한다. - 다중 프로세스 환경: 같은 MPU 위에서 App Lab 런타임, 네트워크 스택, 로깅, 다른 Python 프로세스가 동시에 실행된다. 이들이 CPU·메모리 대역폭·캐시를 공유한다.
- 가상 메모리: 페이지 폴트, 스와핑 가능성이 실행 시간을 불규칙하게 만든다.
- 인터럽트 공유와 소프트IRQ: 네트워크·USB·디스플레이 등 여러 주변장치가 인터럽트 처리 큐를 공유한다.
- PREEMPT_RT 패치의 부재(일반적인 경우): Linux에 실시간성을 크게 개선하는
PREEMPT_RT패치가 있지만, 이는 커널을 다시 빌드해야 적용되는 별도 옵션이다. UNO Q 출고 Debian 이미지가 이 패치를 기본 포함하는지는 확인 필요 — 포함하지 않는다면 표준 Linux 스케줄링 지터(부하 상황에 따라 수 ms~수십 ms 단위로 커질 수 있음)의 영향을 그대로 받는다.
정리하면, MPU 쪽 지연은 "보통 빠르지만 가끔 크게 늦어질 수 있는" 분포를 가진다. 이 분포의 꼬리(worst case)가 얼마나 긴지는 부하·커널 설정에 따라 달라지고 UNO Q 실측 수치는 검증 필요다(8.3에서 측정 방법론을 다룬다).
8.1.4 MCU(Zephyr 기반 Arduino Core)가 결정론적인 이유
STM32U585 + Zephyr 조합이 다른 종류의 보장을 주는 이유는 구조 자체가 다르기 때문이다.
- NVIC(Nested Vectored Interrupt Controller): Cortex-M33에 내장된 인터럽트 컨트롤러로, 각 인터럽트마다 고정 우선순위와 고정 벡터 주소를 갖는다. 인터럽트가 들어오면 소프트웨어 개입 없이 하드웨어가 곧바로 해당 ISR로 분기한다. Zephyr의 Arduino UNO Q 보드 포트는 이 NVIC와 STM32U585의 타이머 페리퍼럴(범용 타이머 11개 인스턴스, 저전력 타이머 LPTIM 4개 인스턴스, ARMv8-M SysTick)을 디바이스 트리로 직접 기술한다.
- 가상 메모리도 페이징도 없음: 메모리 주소가 곧 물리 주소다. 페이지 폴트로 인한 예측 불가능한 지연 자체가 존재하지 않는다.
- 정적이고 단일한 애플리케이션: MCU에는 사용자 스케치 하나가 Zephyr 커널 위에서 실행된다. 임의의 다른 프로세스가 개입해 CPU를 선점할 우려가 없다 — 경쟁하는 주체가 애초에 적다.
- 우선순위 기반 선점형 스케줄링(Zephyr 커널): Zephyr는 실시간 운영체제(RTOS)로서 스레드 우선순위와 인터럽트 우선순위를 명시적으로 설계할 수 있게 해준다. 높은 우선순위 인터럽트는 낮은 우선순위 작업을 즉시 선점한다.
- 예측 가능한 코드 경로: Arduino Core API(
attachInterrupt(),analogWrite(),millis()등, 8.2에서 자세히)는 Zephyr의 네이티브 드라이버를 얇게 감싸는 형태라, 호출 하나하나의 실행 시간이 MPU 쪽 시스템 콜보다 훨씬 예측하기 쉽다.
정확한 인터럽트 레이턴시 수치(예: "인터럽트 발생부터 ISR 첫 명령까지 정확히 몇 사이클")는 STM32U585 데이터시트·Zephyr 커널 설정에 의존하며, 이 책에서 일반적인 마이크로초 이하 단위 수준이라고 개략적으로 추정할 수는 있어도 UNO Q 보드에서의 실측치는 검증 필요로 남긴다. 중요한 것은 절대값보다 "이 지연이 워크로드와 무관하게 일정하다"는 성질 — 즉 결정론 그 자체다.
8.1.5 나란히 놓고 보기
| 항목 | MPU (QRB2210, Debian Linux) | MCU (STM32U585, Zephyr 기반 Arduino Core) |
|---|---|---|
| 설계 목표 | 처리량·응답성의 균형 | 예측 가능한 타이밍 |
| 스케줄링 | 범용 시분할 스케줄러(CFS 계열) | 우선순위 기반 선점형(RTOS) |
| 인터럽트 처리 | 커널 인터럽트 서브시스템, 다른 장치와 공유 | NVIC 고정 벡터, 전용 |
| 메모리 | 가상 메모리, 페이징 가능 | 물리 주소 직결, 페이징 없음 |
| 동시 실행 주체 | 다수의 프로세스·스레드 | 사용자 스케치 하나(+ Zephyr 커널 내부 스레드) |
| 강점 | 비전·AI·네트워크·파일시스템 | 센서 폴링·PWM 생성·인터럽트 응답 |
| 최악 지연(worst-case) 예측 | 어렵다(부하 의존) | 설계 시점에 상한을 추정하기 쉽다 |
| UNO Q에서의 역할 | "무엇을 할지" 결정(고수준 로직) | "언제 정확히 실행할지" 보장(저수준 제어) |
8.1.6 다음 장으로
이 장은 개념을 정리했다. 다음 장들은 이를 실전으로 옮긴다.
- 8.2: STM32U585의 인터럽트·타이머를 Arduino Core API로 실제로 어떻게 쓰는지, 어떤 작업을 MCU/MPU 중 어디에 배치할지의 판단 기준.
- 8.3: 두 브레인이 통신할 때(Bridge, 3.4) 생기는 지연·지터를 어떻게 측정하고 설계에 반영할지.
- 8.4: MCU 위에서 실행되는 결정론적 PID 모터 제어 루프를 실제 스케치로 구현.
- 8.5: MCU의 결정론적 타이밍을 외부 비행 컨트롤러(PX4/ArduPilot)와의 MAVLink 실시간 통신에 응용 — 단, STM32U585 자체가 flight controller가 될 수 없다는 하드웨어 경계를 명확히 규정한다.