Chapter 15

인터럽트·DMA·고속 I/O

화면을 손가락으로 톡 치면, 터치 칩은 1초에 수백 번 좌표를 재다가 "눌렸다"는 사실을 SoC에 알려야 한다. 사진을 찍으면 수십 MB가 저장장치로 흘러가야 하고, 충전기를 꽂으면 USB 선 위로 초당 수십억 비트가 오간다. 그 모든 순간에 CPU가 일일이 장치를 들여다보고 바이트를 하나씩 옮긴다면 CPU는 다른 일을 할 수 없다. 이 장에서는 CPU가 장치와 대화하는 기본 방식(메모리 맵 I/O), 장치가 CPU를 부르는 방법(인터럽트), CPU 대신 데이터를 나르는 일꾼(DMA), 그리고 칩 밖으로 Gb/s를 내보내는 고속 직렬 링크(SerDes)를 차례로 만져 본다.

장치도 주소를 가진다: 메모리 맵 I/O

CPU가 아는 동작은 사실상 두 가지, 주소에서 읽기(load)와 주소에 쓰기(store)다. 그래서 SoC는 장치를 제어하는 레지스터들에도 주소를 붙인다. CPU가 0x1003_0000번지에 값을 쓰면 그 store는 DRAM이 아니라 NoC(8장)를 타고 GPIO 블록으로 가서 핀의 전압을 바꾼다. 이것이 메모리 맵 I/O(MMIO, Memory-mapped I/O)다. 어느 주소가 어느 블록인지 정한 표를 메모리 맵(Memory map)이라 한다. 아래 지도에서 영역을 눌러 보고, GPIO 레지스터의 비트를 눌러 LED를 켜 보자.

그림 15-1. 만져 보기왼쪽은 SB-1의 (단순화한) 물리 주소 공간이다. 실제 크기 비율대로 그리면 DRAM 말고는 보이지 않으므로 영역마다 높이를 바꿔 그렸다. 영역을 누르면 아래에 설명과 레지스터가 나온다. 오른쪽 아래 GPIO 데이터 레지스터의 비트 8개를 누르면 그 비트를 쓰는 store 한 번이 LED 하나를 켜고 끈다.

MMIO 주소는 일반 메모리와 다르게 다뤄야 한다. 장치 레지스터는 읽기만 해도 상태가 바뀔 수 있고(예: UART 수신 레지스터를 읽으면 다음 바이트로 넘어간다), 쓰기 순서가 바뀌면 안 된다. 그래서 운영체제는 이 영역을 페이지 테이블에서 캐시 불가·순서 보장(Device memory)으로 표시하고, C 코드에서는 volatile로 컴파일러의 최적화를 막는다. 장치 레지스터 한 번 읽기는 NoC를 왕복해야 하므로 수십~수백 ns가 걸린다. CPU 입장에서는 캐시 적중의 수백 배다.

기다리며 묻기, 아니면 불러 주기: 폴링과 인터럽트

장치에 새 데이터가 왔는지 CPU가 알려면 두 가지 방법이 있다. 폴링(Polling)은 CPU가 주기적으로 장치의 상태 레지스터를 읽어 보는 것이다. 인터럽트(Interrupt)는 장치가 신호선을 올려 CPU를 부르는 것이다. CPU는 하던 일을 멈추고 레지스터를 저장한 뒤, 정해진 인터럽트 처리기(ISR, Interrupt service routine)로 뛰어간다.

폴링: CPU 사용률—
폴링: 평균 지연—
인터럽트: CPU 사용률—
인터럽트: 평균 지연—
그림 15-2. 만져 보기위 줄은 폴링, 아래 줄은 인터럽트 방식에서 CPU가 I/O에 쓴 시간이다(빨간 눈금 = 장치에 이벤트가 생긴 순간). 가정: 상태 레지스터 한 번 읽기 0.3 µs, 이벤트 처리 2 µs, 인터럽트 진입·복귀 오버헤드 3 µs. 이벤트가 드물면 인터럽트가 압도적으로 낫다. 이벤트가 초당 수십만 번으로 잦아지면 오히려 인터럽트 오버헤드가 CPU를 잡아먹는다(인터럽트 폭주).

그래서 실제 시스템은 둘을 섞는다. 네트워크 드라이버는 패킷이 뜸할 때는 인터럽트로 깨어나고, 폭주가 시작되면 인터럽트를 끄고 폴링으로 바꾼다(Linux NAPI). 장치 쪽에서 여러 이벤트를 모았다가 한 번에 인터럽트를 거는 인터럽트 병합(Interrupt coalescing)도 흔하다. 반대로 지연이 아주 중요한 고속 저장장치(NVMe)에서는 일부러 폴링을 쓰기도 한다.

인터럽트 교통정리: 인터럽트 컨트롤러

SoC에는 인터럽트 신호가 수백 개 있다. 이것을 모두 CPU에 직접 꽂을 수는 없으니, 중간에 인터럽트 컨트롤러를 둔다. Arm 계열에서는 GIC(Generic Interrupt Controller)가 이 일을 한다. GIC는 인터럽트마다 우선순위(숫자가 작을수록 높다)와 목적지 코어를 정해 두고, 대기 중인 것 가운데 가장 높은 것을 CPU에 알린다. CPU가 처리기 안에 있을 때 더 높은 우선순위가 오면 처리기를 멈추고 그것부터 처리하는 것을 중첩(Nesting, 선점)이라 한다. 아래 버튼으로 인터럽트를 직접 걸어 보자.

SIMULATOR

인터럽트 컨트롤러 타임라인

중첩(선점)
모뎀 최악 지연—
저장장치 최악 지연—
합쳐져 잃은 인터럽트—
CPU가 처리기에 쓴 시간—
가로축은 최근 600 µs, 맨 위 줄은 CPU가 실행 중인 처리기(높이 = 중첩 깊이)다. 각 줄의 ▼는 인터럽트가 걸린 순간, 가는 막대는 대기 중, 굵은 막대는 처리 중이다. 처리기마다 진입·복귀 오버헤드 2 µs를 포함한다. 같은 인터럽트가 아직 대기 중일 때 또 걸리면 GIC의 대기 비트 하나에 합쳐져 하나를 잃는다(에지 트리거 기준). 중첩을 금지하면 모뎀 같은 급한 인터럽트가 긴 저장장치 처리기 뒤에서 기다려야 한다.

GIC는 인터럽트를 종류별로 나눈다. 여러 코어가 공유하는 장치 인터럽트(SPI, Shared Peripheral Interrupt — 직렬 버스 SPI와 다른 뜻이다), 코어마다 따로 있는 타이머 같은 인터럽트(PPI), 코어가 다른 코어를 깨우는 소프트웨어 인터럽트(SGI), 그리고 PCIe 장치가 메모리 쓰기로 보내는 메시지 인터럽트(LPI, MSI)다. 하드웨어가 인터럽트를 코어에 알리는 데는 수십~수백 ns면 되지만, 운영체제가 레지스터를 저장하고 처리기를 찾고 스케줄러를 거쳐 사용자 프로그램까지 깨우는 데는 수~수십 µs가 걸린다. 지연을 줄이려면 처리기는 짧게(급한 일만) 하고 나머지는 나중에 스레드에서 하는 상반부·하반부(Top half / bottom half) 구조를 쓴다.

CPU 대신 나르는 일꾼: DMA

저장장치에서 사진 파일 4 MB를 메모리로 가져온다고 하자. CPU가 장치의 데이터 레지스터를 4바이트씩 읽어 메모리에 쓴다면(PIO, Programmed I/O) 100만 번의 느린 MMIO 읽기를 해야 한다. 대신 DMA(Direct Memory Access) 엔진에게 "장치에서 이 주소로 4 MB를 옮겨라"라고 지시만 하면, DMA 엔진이 버스 마스터가 되어 직접 옮기고 끝나면 인터럽트로 알린다. CPU는 그동안 다른 일을 한다.

CPU 복사(PIO): 걸린 시간—
DMA: 걸린 시간—
DMA: CPU가 쓴 시간—
CPU가 아낀 시간—
그림 15-3. 만져 보기막대는 시간(로그 아님)이다. 가정: CPU가 장치 레지스터를 읽어 옮기면 약 200 MB/s, DMA는 장치 속도 2 GB/s로 옮기며 설정에 3 µs, 완료 인터럽트 처리에 3 µs가 든다. 작은 전송에서는 DMA 설정 비용이 아깝지만, 크기가 커지면 DMA가 시간과 CPU를 모두 아낀다. 실제 드라이버도 수백 바이트 이하의 작은 전송은 CPU로 처리하곤 한다.

운영체제가 할당한 버퍼는 가상 주소로는 이어져 있어도 물리 메모리에서는 4 KB 페이지로 흩어져 있다. DMA 엔진은 물리 주소로 일하므로, 조각마다 (주소, 길이, 다음 조각) 정보를 담은 디스크립터(Descriptor)를 사슬로 엮어 건네준다. DMA 엔진은 사슬을 따라가며 조각을 차례로 옮긴다. 이것이 스캐터-개더(Scatter-gather) DMA다.

버퍼 크기—
디스크립터 수—
스캐터-개더 없이 필요한 DMA 설정—
그림 15-4. 만져 보기위 칸들은 물리 메모리의 4 KB 페이지다. 칸을 눌러 버퍼에 넣거나 뺄 수 있다. 버퍼 페이지는 번호 순서(가상 주소 순서)대로 장치로 보내야 한다. 물리적으로 이어진 페이지는 디스크립터 하나로 합쳐진다. "DMA 시작"을 누르면 DMA 엔진이 디스크립터 사슬을 따라가며 조각을 나른다.

DMA에는 위험도 있다. DMA 엔진은 아무 물리 주소에나 쓸 수 있으니, 고장 났거나 악의적인 장치(예: 썬더볼트로 꽂은 장치)가 커널 메모리를 덮어쓸 수 있다. 그래서 장치와 메모리 사이에 장치용 MMU, 곧 IOMMU(Arm에서는 SMMU)를 둔다. 장치가 쓰는 주소(IOVA)를 운영체제가 허락한 물리 페이지로만 번역해 주고, 허락하지 않은 주소는 막는다.

그림 15-5. 만져 보기왼쪽은 장치가 보는 주소(IOVA), 가운데는 IOMMU의 변환표, 오른쪽은 물리 메모리다. IOMMU를 끄면 장치 주소가 곧 물리 주소가 되어 아무 곳에나 쓸 수 있다.

센서와 속삭이는 선 두 가닥: I²C, SPI, UART

휴대폰에는 가속도계, 자이로, 조도 센서, 충전 IC, 터치 컨트롤러, 카메라 모듈 설정 포트처럼 데이터는 적지만 수가 많은 칩이 수십 개 있다. 이들에게 고속 링크를 하나씩 줄 수는 없으니 핀을 적게 쓰는 저속 버스로 묶는다. 대표가 I²C(Inter-Integrated Circuit)다. 선은 클럭(SCL)과 데이터(SDA) 두 가닥뿐이고, 여러 장치가 같은 두 선을 나눠 쓴다. 각 장치는 7비트 주소를 가진다.

I²C 선은 저항으로 1(High)에 끌어올려져 있고, 장치는 선을 0으로 끌어내리기만 한다(오픈 드레인). 그래서 둘이 동시에 말해도 단락되지 않는다. 규칙은 이렇다. SCL이 1인 동안 SDA는 바뀌면 안 된다. 단 두 예외가 시작(SCL=1일 때 SDA가 1→0)과 정지(SCL=1일 때 SDA가 0→1)다. 바이트마다 받는 쪽이 SDA를 끌어내려 ACK로 답한다.

SCL 클럭 수—
400 kHz에서 걸린 시간—
유효 데이터 속도—
그림 15-6. 만져 보기주소(기본값 0x48은 흔한 온도 센서 주소)와 데이터를 고르고 "보내기"를 누르자. 파란 부분은 마스터(SoC)가, 초록 부분은 슬레이브(센서)가 SDA를 구동한다. 읽기에서는 데이터 바이트를 센서가 보내고 마스터가 마지막 바이트에 NACK로 "그만"을 알린다. 장치가 없으면 주소 뒤 ACK 자리가 1(NACK)로 남고 마스터는 곧바로 정지 조건을 건다.
I²CSPIUART
선 수2 (SCL, SDA)4 (SCLK, MOSI, MISO, CS) + 장치마다 CS2 (TX, RX), 클럭 없음
속도 (대략)100 k / 400 k / 1 MHz (I3C 12.5 MHz)수십 MHz (플래시는 100 MHz 이상, 4선 병렬 Quad SPI)115.2 kbaud ~ 수 Mbaud
장치 선택7비트 주소칩 선택(CS) 선점대점 (일대일)
응답 확인바이트마다 ACK없음없음 (패리티 선택)
방향반이중전이중 (보내며 동시에 받음)전이중
주 쓰임센서, 충전 IC, 카메라 설정디스플레이 설정, 플래시, 고속 센서디버그 콘솔, 블루투스·GPS 칩
I²C 400 kHz로 3축 가속도 데이터(6바이트)를 초당 1,000번 읽으면 버스를 얼마나 쓸까?

읽기 한 번에는 레지스터 주소를 쓰는 단계(시작 + 주소·쓰기 + 레지스터 번호)와 다시 시작해 읽는 단계(주소·읽기 + 6바이트)가 필요하다. 바이트마다 ACK 1비트가 붙는다.

답 보기

쓰기 단계 2바이트, 읽기 단계 7바이트(주소 1 + 데이터 6)로 9바이트 × 9비트 = 81비트에 시작·재시작·정지 몇 비트를 더해 약 85클럭, 400 kHz면 약 212 µs다. 초당 1,000번이면 버스 시간의 약 21%를 쓴다. 센서가 대여섯 개면 I²C 하나로는 모자라기 시작한다. 그래서 고속 센서는 SPI나 I3C를 쓰고, 센서 쪽에 FIFO를 두어 여러 샘플을 한 번에 읽는다.

왜 병렬이 아니라 직렬인가: SerDes

예전 PC의 하드디스크와 프린터는 선 수십 가닥의 두꺼운 병렬 케이블로 연결되었다. 한 번에 여러 비트를 보내니 빠를 것 같지만, 속도를 올리자 문제가 생겼다. 선마다 길이와 전기적 특성이 조금씩 달라 비트들이 도착하는 시각이 어긋난다(스큐). 모든 비트를 하나의 공통 클럭으로 잡으려면, 가장 빠른 선과 가장 느린 선의 차이보다 비트 하나의 시간(UI, Unit interval)이 충분히 길어야 한다.

UI (비트 하나의 시간)—
모든 선이 유효한 창—
공통 클럭 방식의 한계 속도—
그림 15-7. 만져 보기8가닥 병렬 버스의 선마다 비트가 바뀌는 시점(X자)이 스큐만큼 어긋나 있다. 지터는 선마다 ±10 ps로 두었다. 초록 띠는 공통 클럭으로 표본을 잡을 수 있는 구간이다. 속도를 올리면 UI가 줄어 띠가 사라진다. "레인마다 CDR"을 켜면 각 선이 자기 데이터에서 클럭을 뽑아내 각자의 한가운데에서 표본을 잡으므로 스큐가 문제되지 않는다. 이것이 직렬 링크의 방식이다.

그래서 고속 I/O는 선 하나(실제로는 잡음에 강한 차동 쌍 한 쌍)에 비트를 몰아 보내고, 받는 쪽에서 신호 자체로부터 클럭을 복원한다. 송신기의 직렬화기와 수신기의 역직렬화기를 합쳐 SerDes(Serializer/Deserializer)라 한다. 더 빠르게 하고 싶으면 이런 레인(Lane)을 여러 개 묶되, 레인마다 따로 클럭을 복원하고 레인 사이의 어긋남은 버퍼로 맞춘다(디스큐).

클럭 복원 회로(CDR)가 일하려면 신호가 자주 바뀌어야 한다. 0이 1,000개 이어지면 언제가 비트 경계인지 알 수 없다. 또 교류 결합 커패시터를 지나는 링크는 1과 0의 수가 평균적으로 같아야(DC 균형) 기준선이 흔들리지 않는다. 이를 위해 데이터를 그대로 보내지 않고 바꿔 보내는 것이 라인 코딩(Line coding)이다. 8b/10b는 8비트를 10비트 부호로 바꿔 같은 비트가 최대 5개까지만 이어지고 DC 균형을 맞춘다. 대신 20%를 버린다. 128b/130b는 데이터를 의사 난수로 뒤섞고(스크램블) 128비트마다 2비트 헤더만 붙여 오버헤드를 1.5%로 줄였다.

코딩 효율—
한 방향 유효 대역폭—
스크램블 전 최장 연속—
스크램블 후 최장 연속—
그림 15-8. 만져 보기위 파형은 원래 데이터 64비트, 아래는 16비트 LFSR(\(x^{16}+x^5+x^4+x^3+1\), PCIe 1·2세대의 스크램블 다항식)로 뒤섞은 결과다. 0만 이어지는 데이터도 스크램블하면 0과 1이 고르게 섞인다. 받는 쪽은 같은 LFSR로 다시 XOR해 원래 데이터를 되찾는다. 위 계산기는 레인 수 × 전송률 × 코딩 효율로 한 방향 대역폭을 계산한다(FLIT은 PCIe 6.0의 242B/256B 방식, FEC·CRC 포함 대략값).

아이 다이어그램: 신호가 살아서 도착했는가

칩 밖의 배선(패키지, 기판, 커넥터, 케이블)은 높은 주파수일수록 신호를 크게 깎아 먹는 저역 통과 필터다. 비트가 짧아지면 한 비트의 에너지가 다음 비트들 위로 번져 겹친다. 이를 심볼 간 간섭(ISI, Inter-symbol interference)이라 한다. 여기에 잡음과 지터가 더해진다. 수신 신호를 UI 두 개 길이로 잘라 겹쳐 그리면 눈 모양이 나오는데, 이것이 아이 다이어그램(Eye diagram)이다. 눈이 크게 뜨여 있을수록 0과 1을 구분하기 쉽다.

수신기가 깎인 고주파를 되살리는 방법이 이퀄라이저(Equalizer)다. 송신기의 FFE(Feed-forward equalizer)는 앞뒤 비트를 조금씩 빼서 보내, 채널이 남길 꼬리를 미리 상쇄한다(디엠퍼시스). 아래 시뮬레이터에서 속도와 채널 손실을 올려 눈을 감기게 한 뒤, FFE 탭으로 다시 뜨게 해 보자.

SIMULATOR

SerDes 아이 다이어그램

변조
나이퀴스트 주파수 · 손실—
눈 높이 (±3σ)—
눈 폭 (Q≥3)—
추정 BER—
무작위 심볼 1,500개를 송신 진폭 ±400 mV로 보내 2극 저역 통과 채널(손실은 주파수에 대략 비례해 커진다), 가우스 잡음, 랜덤 지터를 거친 뒤 겹쳐 그렸다. 아래 막대는 비트 하나만 보냈을 때의 펄스 응답을 UI 간격으로 잰 값(커서)이다. h₀이 신호, h₋₁·h₁·h₂…가 이웃 비트로 번지는 ISI다. 후 탭을 음수로 키우면 h₁이 줄어든다. BER은 가장 좋은 표본 위치에서 0과 1(PAM4는 이웃 레벨) 분포의 \(Q=(\mu_1-\mu_0)/(\sigma_1+\sigma_0)\)로 \(\tfrac12\operatorname{erfc}(Q/\sqrt2)\)를 계산한 추정치다. 실제 PCIe·USB 수신기는 여기에 CTLE와 DFE를 더한다.
NRZ 32 Gb/s에서 눈이 감겼다. PAM4로 바꾸면 나아질까?

PAM4는 한 심볼에 2비트를 싣는다. 같은 비트 속도에서 나이퀴스트 주파수와 레벨 간격이 어떻게 변하는지 생각해 보자.

답 보기

PAM4는 심볼 속도가 절반(16 GBd)이라 나이퀴스트 주파수가 8 GHz로 내려가 채널 손실이 크게 준다. 대신 진폭을 세 칸으로 나누므로 눈 하나의 높이가 1/3(약 −9.5 dB)이 되어 잡음에 약하다. 손실이 큰 채널에서는 PAM4가 이기고, 손실은 작은데 잡음이 큰 경우에는 NRZ가 낫다. PCIe 6.0(64 GT/s)과 최신 이더넷이 PAM4로 간 이유가 손실 때문이다. 그 대신 BER이 나빠지는 것을 FEC(전방 오류 정정)로 메운다.

$$ \text{BER} \approx \tfrac12\,\operatorname{erfc}\!\left(\frac{Q}{\sqrt2}\right), \qquad Q = \frac{\mu_1-\mu_0}{\sigma_1+\sigma_0}, \qquad Q=7 \Rightarrow \text{BER}\approx 10^{-12} $$
고속 링크의 목표 BER은 보통 \(10^{-12}\) 이하다. 16 Gb/s 링크라면 1분에 한 번 남짓 오류가 나는 수준이고, 오류는 CRC로 찾아 재전송한다.

숫자로 보는 고속 I/O

SB-1의 가장자리에는 저장장치(UFS), 외부 연결(USB), 카메라·디스플레이(MIPI), 그리고 노트북·자동차용 칩이라면 PCIe를 위한 SerDes가 늘어서 있다(1장의 다이 아래쪽 띠). 세대가 바뀔 때마다 레인당 속도가 대략 두 배가 되었고, 그 속도를 위해 라인 코딩과 변조가 바뀌었다.

규격레인당 전송률코딩·변조유효 대역폭 (대략)연도
PCIe 3.08 GT/s128b/130b, NRZ레인당 약 0.98 GB/s2010
PCIe 4.016 GT/s128b/130b, NRZ레인당 약 1.97 GB/s2017
PCIe 5.032 GT/s128b/130b, NRZ레인당 약 3.94 GB/s2019
PCIe 6.064 GT/sPAM4, FLIT + FEC레인당 약 7.6 GB/s2022
UFS 3.1 (M-PHY HS-G4)11.6 Gb/s × 2레인8b/10b약 2.3 GB/s (이론)2020
UFS 4.0 (M-PHY HS-G5)23.3 Gb/s × 2레인128b/129b순차 읽기 약 4.2 GB/s2022
USB 3.2 Gen 210 Gb/s128b/132b약 1.2 GB/s2013
USB4 (v1 / v2)20 Gb/s × 2 / PAM3128b/132b 등40 / 80 Gb/s 링크2019 / 2022
MIPI D-PHY v2.5 (카메라)약 4.5 Gb/s × 4레인소스 동기 클럭약 2 GB/s2019
I²C Fast-mode400 kb/s오픈 드레인수십 kB/s1992
비트 하나를 칩 밖으로 보내는 값

고속 SerDes는 비트당 수 pJ의 에너지를 쓴다(채널이 길수록 이퀄라이저가 커진다). 16 GT/s × 4레인 링크를 꽉 채우면 SerDes만 수백 mW다. 1장에서 본 "칩 밖은 비싸다"가 여기서도 그대로다. 그래서 링크는 쉴 때 재빨리 저전력 상태(PCIe L1, UFS Hibern8)로 들어가고, 깨어날 때 다시 클럭을 맞추는 시간이 지연에 더해진다(12장).

클럭은 어디서?

SerDes 송신기는 수십 GHz의 깨끗한 클럭이 필요하다. 보통 100 MHz 기준 클럭에서 LC-VCO를 쓰는 저지터 PLL로 만들고(14장), 수신기의 CDR은 들어오는 데이터의 천이를 보며 표본 위치를 계속 미세 조정하는 또 하나의 PLL이다. 앞 시뮬레이터의 랜덤 지터가 바로 이 PLL들의 잡음이다.

핵심 정리

  1. 장치 레지스터는 물리 주소 공간에 매핑되고, CPU는 load/store로 장치를 다룬다(MMIO). 이 영역은 캐시하지 않고 순서를 지킨다.
  2. 폴링은 이벤트가 잦을 때, 인터럽트는 드물 때 유리하다. 인터럽트 컨트롤러가 우선순위·목적지·중첩을 관리하며, 지연을 줄이려면 처리기를 짧게 한다.
  3. DMA는 CPU 대신 데이터를 나르고, 스캐터-개더 디스크립터로 흩어진 페이지를 한 번에 처리한다. IOMMU는 장치의 메모리 접근을 허락된 페이지로 제한한다.
  4. I²C는 선 두 가닥에 주소·ACK를 쓰는 저속 공유 버스, SPI는 칩 선택 선을 쓰는 빠른 전이중 버스, UART는 클럭 없는 점대점 링크다.
  5. 병렬 버스는 선 사이 스큐에 막혀 직렬 SerDes에 자리를 내줬다. 라인 코딩은 클럭 복원과 DC 균형을 위한 것이며, 채널 손실·잡음·지터가 아이를 닫고 이퀄라이저가 다시 연다. 손실이 커지면 PAM4와 FEC로 간다.

확인 퀴즈

Q1. MMIO 영역을 일반 메모리처럼 캐시하면 안 되는 이유로 가장 알맞은 것은?

상태 레지스터는 장치가 바꾸고, 데이터 레지스터는 읽을 때마다 다음 값이 나온다. 캐시하면 CPU가 옛 값을 보거나 쓰기가 늦게·합쳐져 나간다.

Q2. 초당 50만 번 이벤트가 생기는 네트워크 장치에서 인터럽트 대신 폴링(또는 병합)을 쓰는 이유는?

오버헤드 2 µs × 50만 번 = 1초. CPU 하나가 통째로 인터럽트 진입·복귀에만 쓰인다. 이벤트가 잦으면 어차피 매번 할 일이 있으므로 폴링이 낫다.

Q3. GIC에서 중첩(선점)을 허용하면 좋아지는 것은?

중첩은 급한 인터럽트의 지연을 줄여 주지만, 선점당한 처리기는 그만큼 늦게 끝난다. 총 일의 양은 같다.

Q4. 물리적으로 흩어진 4 KB 페이지 여러 개로 된 버퍼를 DMA 한 번으로 보내게 해 주는 것은?

디스크립터마다 (물리 주소, 길이, 다음)을 담아 사슬로 엮으면 DMA 엔진이 차례로 따라가며 옮긴다.

Q5. PCIe 3.0이 8b/10b 대신 128b/130b를 쓰면서 얻은 이득은?

PCIe 2.0: 5 GT/s × 0.8 = 4 Gb/s, PCIe 3.0: 8 GT/s × 128/130 ≈ 7.88 Gb/s. 전송률은 1.6배만 올리고 실효 대역폭은 두 배로 만들었다. 대신 천이 밀도는 스크램블러로 확보한다.

Q6. 아이 다이어그램에서 송신 FFE의 후 탭(디엠퍼시스)을 키우면 일어나는 일은?

FFE는 고주파를 상대적으로 키우는 필터다. 채널의 저역 통과 특성을 보상하지만, 송신 진폭 한계 때문에 신호 크기 자체는 줄어든다. 너무 키우면 과보상으로 다시 눈이 닫힌다.