Chapter 11

ISP·DSP·코덱: 전용 하드웨어

휴대폰 카메라 앱을 켜면 화면에 바로 세상이 비친다. 별일 아닌 것 같지만, 그 사이 칩 안에서는 이미지 센서가 보낸 이상한 격자무늬 숫자들이 열 단계 넘는 처리를 거쳐 사진이 되고, 초당 30~60장씩 화면으로, 동시에 영상 압축기로 흘러간다. 이 일을 CPU에게 시키면 큰 코어 여러 개가 수 와트를 쓰고도 따라가지 못한다. 그런데 SoC 구석의 손톱만 한 회로들, 즉 ISP(Image Signal Processor), 비디오 코덱, DSP(Digital Signal Processor), 디스플레이 엔진은 같은 일을 수백 mW로 해낸다. 이 장에서는 이 "한 가지 일만 하는 회로"들이 무슨 계산을 하는지 단계마다 직접 켜고 끄며 확인하고, 왜 그렇게 효율적인지 따져 본다.

한 가지 일만 하는 회로가 이기는 이유

CPU가 덧셈 하나를 할 때 실제 덧셈기의 에너지는 전체의 몇 %에 불과하다는 것을 1장에서 보았다. 나머지는 명령어를 가져오고, 해독하고, 레지스터 파일을 읽고 쓰고, 파이프라인을 제어하는 데 쓰인다. 2010년 스탠퍼드의 Hameed 등은 H.264 인코더를 범용 프로세서에서 돌린 것과 전용 회로(ASIC)로 만든 것을 비교했는데, 에너지 차이가 약 500배였다. 범용 프로세서에 SIMD 확장과 몇 가지 맞춤 명령을 더해도 차이를 수십 배까지밖에 줄이지 못했다.

전용 회로가 이기는 이유는 세 가지다. ① 명령어가 없다. 무엇을 할지가 배선으로 정해져 있다. ② 데이터가 흐르는 길이 짧다. 한 단계의 출력이 바로 옆 단계의 입력 레지스터로 간다. 캐시도, 레지스터 파일도 거치지 않는다. ③ 필요한 만큼의 비트만 쓴다. 화소가 10비트면 10비트 덧셈기를, 계수가 상수면 곱셈기 대신 시프트와 덧셈을 쓴다. 아래에서 작업을 골라 블록마다 같은 일을 하는 데 드는 전력을 비교해 보자.

CPU 대비 전용 회로—
전용 회로로 배터리(19 Wh) 시간—
CPU로 배터리 시간—
그림 11-1. 만져 보기막대는 각 블록이 그 일을 지속할 때의 전력(로그 눈금, 블록 자체만, 대략값)이다. CPU로 4K60 HEVC를 소프트웨어 디코딩하면 큰 코어 여러 개가 수 와트를 쓰고, 12 MP 카메라 처리는 아예 실시간이 불가능하다(빗금). 공개된 측정과 논문(Hameed 2010 등)을 바탕으로 한 교육용 어림값이다.

대가는 유연성이다. 전용 회로는 설계할 때 정한 일만 한다. H.264 디코더는 AV1을 풀지 못한다. 그래서 전용 블록은 "자주, 오래, 많이" 하는 일, 그리고 표준이 정해져 오래 쓰이는 일에만 만든다. 카메라, 영상 압축, 화면 표시, 오디오가 대표적이다. 그 사이의 중간 지대를 프로그램 가능한 DSP가 메운다.

카메라 ISP: 빛이 사진이 되기까지

이미지 센서의 화소는 색을 모른다. 들어온 빛의 양만 잴 뿐이다. 그래서 화소마다 빨강·초록·파랑 중 하나만 통과시키는 작은 색 필터를 올린다. 가장 흔한 배열은 2×2 칸에 R, G, G, B를 놓는 베이어 패턴(Bayer pattern)(Bryce Bayer, 1976)이다. 초록이 두 개인 이유는 사람 눈이 밝기를 주로 초록 파장으로 느끼기 때문이다. 결국 센서가 보내는 원시 데이터(RAW)는 화소마다 10~14비트 숫자 하나씩이고, 각 화소에서 빠진 두 색은 이웃에게서 추측해야 한다.

ISP는 이 원시 데이터를 받아 다음과 같은 단계를 고정 회로로 차례로 처리한다. 아래 시뮬레이터는 가상의 장면을 만든 뒤, 실제 센서처럼 따뜻한 조명(약 3,500 K), 색 필터의 겹침, 렌즈 주변부 어두워짐, 광자 잡음과 읽기 잡음, 결함 화소까지 넣어 원시 데이터를 만든다. 이제 ISP 단계를 하나씩 켜고 끄며 사진이 완성되는 과정을 보자. 그림 위를 누르거나 끌면 오른쪽 돋보기가 따라온다.

SIMULATOR

ISP 파이프라인

ISP 단계 (숫자 = 화소당 연산, 대략)
디모자이크
정답 대비 PSNR—
켠 단계—
화소당 연산 (대략)—
12 MP · 30 fps 연산량—
장면은 192×128 화소로 작게 만들어 매번 브라우저에서 실제로 처리한다. 오른쪽 위는 해, 왼쪽 아래는 색 견본판(맨 아래 줄은 회색 단계), 오른쪽 아래 동심원 무늬는 디모자이크 잘못(지퍼 무늬·가짜 색)을 드러내는 시험 무늬다. PSNR은 정답 장면을 같은 톤 곡선으로 표시한 것과 비교한 값이다. 실제 ISP는 이 밖에도 3A(자동 노출·초점·화이트 밸런스) 통계, 여러 장 합성(HDR), 왜곡 보정, 크기 변환 단계를 갖는다.

몇 가지 실험을 해 보자. 블랙 레벨 보정을 끄면 검은색이 회색으로 뜬다. 센서는 0 근처의 잡음을 표현하려고 일부러 바닥값(여기서는 1023 중 64)을 더해 보내기 때문이다. 화이트 밸런스를 끄거나 색온도를 틀리게 맞추면 회색 견본이 노랗거나 파랗게 물든다. 사람 눈은 조명에 저절로 적응하지만 센서는 그렇지 않다. 디모자이크를 "안 함"으로 두면 원시 격자가 그대로 보이고, "최근접"이면 동심원 무늬 주변에 계단과 가짜 색이 생긴다. 색 보정 행렬(CCM)을 끄면 색 필터끼리 파장이 겹친 탓에 색이 바랜다. 톤 매핑을 끄면 선형 데이터를 그대로 화면에 보내 사진이 어둡고 해가 하얗게 날아간다.

노이즈 제거를 세게 걸면 PSNR은 계속 좋아질까?

노이즈 제거 세기를 0에서 1까지 올리며 PSNR과 돋보기 속 동심원 무늬를 지켜보자.

답 보기

아니다. 처음에는 잡음이 줄어 PSNR이 오르지만, 너무 세면 가는 무늬와 질감까지 뭉개져(과도한 평활화) 다시 나빠진다. 노이즈 제거와 선명화는 늘 이 줄다리기다. 그래서 실제 ISP는 가장자리를 감지해 평평한 곳만 세게 다듬고(가장자리 보존 필터), 밝기에 따라 세기를 바꾸고(어두운 곳은 광자 잡음이 크다), 요즘은 NPU의 신경망 노이즈 제거와 함께 쓴다(10장).

단계마다 화소 하나에 드는 연산은 수~수십 번이지만, 12 MP 센서를 초당 30장 처리하면 화소가 초당 3억 6천만 개다. 단계 전부를 합치면 초당 수천억 번의 연산이 된다. ISP는 단계마다 화소를 클럭당 1~4개씩 처리하는 전용 파이프라인을 깔아 이를 수백 MHz 클럭과 수백 mW로 해낸다.

프레임이 아니라 줄 단위로: 라인 버퍼

ISP의 단계 대부분은 화소 하나를 계산하는 데 주변 \(K\times K\) 이웃만 본다(디모자이크 5×5, 노이즈 제거 5×5~9×9 등). 센서는 화소를 왼쪽 위부터 한 줄씩 보낸다(래스터 순서). 그렇다면 프레임 전체를 저장할 필요가 없다. 지금 들어오는 줄과 바로 위 \(K-1\)줄만 칩 안 SRAM에 갖고 있으면, 창(window)이 미끄러지며 계산할 수 있다. 이 SRAM을 라인 버퍼(Line buffer)라 부른다.

단계당 라인 버퍼—
전체 ISP 라인 버퍼—
프레임 버퍼였다면—
출력 지연—
그림 11-2. 만져 보기화소가 래스터 순서로 들어온다(파란 칸 = 라인 버퍼에 저장 중, 연한 칸 = 이미 쓰고 버림, 빈칸 = 아직 안 옴). 빨간 틀이 지금 계산하는 \(K\times K\) 창, 가운데 점이 지금 내보내는 출력 화소다. 출력은 입력보다 \((K-1)/2\)줄 늦다. 계산에서 화소당 2바이트(12비트 원시 데이터를 담는 크기)를 가정했다. 4,032×3,024 센서(12 MP)라면 프레임 버퍼는 약 24 MB지만 5×5 라인 버퍼는 단계당 약 32 KB다.

라인 버퍼 덕분에 ISP는 프레임을 DRAM에 내렸다 올리지 않고 센서 → ISP → (필요하면) NPU·코덱으로 줄 단위로 흘려 보낼 수 있다. DRAM 왕복 한 번에 12 MP 프레임이면 수십 MB, 초당 30장이면 1 GB/s가 넘는다(7장). 이것을 아끼는 것이 카메라 전력의 핵심이다. 대신 이 구조는 "위에서 아래로 한 번" 처리하는 연산에만 맞는다. 여러 장을 합치는 HDR이나 큰 영역을 보는 처리는 결국 DRAM의 프레임 버퍼가 필요하다.

비디오 코덱 ①: 움직임을 찾아라

4K 영상(3840×2160, 4:2:0, 8비트)을 압축 없이 저장하면 초당 30장에 약 3 Gb/s다. 스트리밍 영상은 이것을 10~20 Mb/s, 즉 1/150~1/300로 줄인다. 비결의 첫째는 이웃한 프레임이 거의 같다는 것이다. 현재 프레임을 작은 블록으로 나누고, 각 블록이 이전 프레임의 어디에서 왔는지 찾아 그 움직임 벡터(Motion vector)와 작은 차이(잔차)만 보낸다. 찾는 일을 움직임 추정(Motion estimation)이라 한다.

가장 단순한 방법은 탐색 범위 안의 모든 위치에서 블록을 겹쳐 보고 화소 차이의 절댓값 합, SAD(Sum of Absolute Differences)가 가장 작은 곳을 고르는 것이다.

$$\mathrm{SAD}(d_x,d_y) = \sum_{i,j \in \text{블록}} \big|\,\text{현재}(x+i,\,y+j) - \text{이전}(x+i+d_x,\,y+j+d_y)\,\big|$$
SIMULATOR

블록 움직임 추정

찾은 벡터 (dx, dy)—
SAD 최소 / 제자리—
블록당 SAD 연산—
4K60 전역 탐색이면—
오른쪽(현재 프레임)에서 블록을 누르면 왼쪽(이전 프레임)에서 탐색 창(점선)과 가장 비슷한 위치(실선)를 보여 준다. 아래 지도는 탐색 창 안 모든 후보의 SAD다(어두울수록 작음, ◆ = 최소). 공은 오른쪽 아래로, 상자는 왼쪽으로 움직였다. 탐색 범위가 실제 움직임보다 작으면 엉뚱한 곳을 고른다. 무늬가 없는 평평한 영역(하늘)은 SAD 지도가 밋밋해 벡터가 불안정하다.

블록당 연산은 \((2R+1)^2 \times B^2\)번의 뺄셈·절댓값·덧셈이다. ±16 범위, 16×16 블록이면 블록 하나에 약 28만 번, 4K60이면 초당 수백조 번이다. 그래서 실제 인코더는 전역 탐색 대신 다이아몬드 탐색 같은 빠른 탐색, 축소 영상에서 먼저 찾는 계층 탐색, 이웃 블록의 벡터에서 출발하는 예측을 쓴다. 하드웨어는 SAD 계산기 수백 개를 배열로 깔고, 이전 프레임의 탐색 창을 칩 안 SRAM에 올려 이웃 블록끼리 재사용한다. NPU의 시스톨릭 배열과 닮은 구조다(10장).

비디오 코덱 ②: 변환과 양자화

움직임으로 예측하고 남은 잔차(또는 예측 없이 압축하는 I 프레임의 블록)는 이산 코사인 변환(DCT, Discrete Cosine Transform)으로 주파수 성분으로 바꾼다. 자연 영상의 에너지는 대부분 낮은 주파수(왼쪽 위 계수)에 몰려 있다. 그다음 양자화(Quantization)로 계수를 큰 간격으로 나눠 반올림하면, 높은 주파수 계수 대부분이 0이 된다. 0이 길게 이어지면 엔트로피 부호화로 아주 적은 비트로 표현할 수 있다. 정보를 버리는 단계는 양자화 하나뿐이고, 압축률과 화질은 양자화 간격, 즉 QP(Quantization Parameter)로 조절한다. H.264·HEVC에서 QP가 6 오를 때마다 양자화 간격이 두 배가 된다.

0이 아닌 계수—
추정 비트—
압축률 (512비트 대비)—
블록 PSNR—
그림 11-3. 만져 보기왼쪽 그림에서 8×8 블록을 눌러 고른다. 가운데 격자는 양자화된 DCT 계수(숫자 = 양자화 단계 수, 빈칸 = 0)이고, 오른쪽은 그 계수로 복원한 블록이다. 평평한 블록은 QP가 낮아도 계수가 한두 개뿐이고, 가장자리나 무늬가 있는 블록은 계수가 많다. QP를 40 넘게 올리면 블록이 평평한 사각형이 되는 "블록 현상"이 보인다. 비트는 지수 골롬 부호를 흉내 낸 어림값이다.

실제 코덱은 프레임을 세 종류로 섞는다. I 프레임은 다른 프레임을 참조하지 않고 혼자 압축한다(가장 크다). P 프레임은 이전 프레임에서 움직임을 예측한다. B 프레임은 앞뒤 프레임을 모두 참조해 가장 작다. I 프레임 사이의 묶음을 GOP(Group of Pictures)라 한다. GOP를 길게 하면 비트레이트가 줄지만, 중간부터 재생하거나 전송 오류에서 회복하기가 어려워진다.

4K30 평균 비트레이트—
무압축 대비—
I 프레임이 차지하는 비트—
그림 11-4. 만져 보기막대 하나가 프레임 하나의 크기다(2초 분량, 60프레임). I 프레임 하나를 약 0.5 MB(무압축의 1/25)로 잡고, P 프레임은 움직임에 따라 I의 8~43%, B 프레임은 P의 절반으로 어림했다. 무압축 4K30(4:2:0, 8비트)은 약 3 Gb/s다.
세대가 바뀔 때마다 압축률이 오르는 이유

H.264(2003) → HEVC(2013) → AV1(2018)·VVC(2020)로 갈 때마다 같은 화질에서 비트레이트가 대략 30~50% 준다. 블록 크기를 16×16에서 64×64, 128×128까지 다양하게 나누고, 예측 방향을 수십 가지로 늘리고, 더 정교한 필터와 엔트로피 부호화를 쓰기 때문이다. 그 대가로 인코더의 탐색 연산은 몇 배씩 늘었다. 소프트웨어로는 감당할 수 없어 SoC마다 새 표준용 하드웨어 블록이 들어간다.

DSP: 곱셈-누산을 위한 프로세서

DSP(Digital Signal Processor)는 전용 회로와 CPU 사이에 있다. 프로그램을 돌리지만, 그 명령어와 구조가 신호 처리의 단골 연산, 즉 곱셈-누산(MAC)에 맞춰져 있다. 오디오 필터, 음성 호출어 감지, 센서 융합, 모뎀의 일부 처리가 DSP에서 돈다. 신호 처리의 기본 연산은 FIR 필터(Finite Impulse Response)다. 출력은 최근 입력 \(N\)개에 계수를 곱해 더한 것이다.

$$y[n] = \sum_{k=0}^{N-1} h[k]\; x[n-k]$$

탭 수 \(N\)이 곧 샘플당 MAC 수다. 아래에서 저역 통과 필터의 탭 수와 차단 주파수를 바꿔, 낮은 음(220 Hz, 440 Hz)은 남기고 높은 잡음(7 kHz 음과 쉬익 소리)을 지워 보자.

샘플당 MAC—
48 kHz에서 MAC/s—
DSP 부하 (4 MAC/클럭, 300 MHz)—
7 kHz 감쇠—
그림 11-5. 만져 보기위: 입력 신호(연한 선)와 필터 출력(진한 선) 10 ms. 출력은 \((N-1)/2\) 샘플 늦게 나온다. 아래: 필터의 주파수 응답(dB). 세로 점선은 신호 성분(220 Hz, 440 Hz, 7 kHz)이다. 탭이 많을수록 경계가 가팔라지고 감쇠가 깊어지지만 MAC과 지연이 는다. 사각 창은 경계가 가파른 대신 바깥에 물결(새는 성분)이 크다.

DSP는 이런 루프를 빠르게 돌리도록 특별한 장치를 갖는다. 한 클럭에 MAC 여러 개와 메모리 읽기 두 개, 주소 갱신을 동시에 하는 VLIW(Very Long Instruction Word) 명령어(컴파일러가 병렬로 실행할 명령을 미리 한 묶음으로 짠다), 오버헤드 없는 하드웨어 반복문, 원형 버퍼 주소 지정, 넘치면 최댓값에 멈추는 포화 연산이다. CPU의 비순차 실행 엔진이 실행 중에 하는 일을 컴파일러가 미리 해 두니, 하드웨어가 단순하고 전력이 낮다. 대신 분기가 많고 예측하기 어려운 코드에는 맞지 않는다. 휴대폰의 저전력 DSP는 화면이 꺼진 동안에도 수 mW로 마이크 신호를 듣고 있다가, 호출어가 들리면 그때 CPU를 깨운다(1장).

디스플레이 파이프라인: 초당 120번의 합성

화면에 보이는 것은 그림 한 장이 아니라 여러 층(Layer)이다. 배경화면, 앱 화면, 동영상, 상태 표시줄, 알림이 각자 따로 그려진다. 이 층들을 겹쳐 한 장으로 만드는 일을 합성(Composition)이라 한다. GPU로 합성할 수도 있지만, SoC의 디스플레이 엔진(Display processing unit)은 층들을 DRAM에서 읽으며 화소 단위로 바로 섞어 패널로 내보낼 수 있다(하드웨어 오버레이). 합성한 결과를 DRAM에 다시 쓰지 않으니 대역폭이 준다.

패널은 정해진 박자로 화면을 갱신한다. 그 박자 신호가 VSync(Vertical sync)다. 120 Hz 화면이면 8.33 ms마다 새 프레임이 준비되어 있어야 하고, 앱이 늦으면 이전 프레임이 한 번 더 표시된다. 이것이 화면이 "버벅이는" 끊김(Jank)이다.

SIMULATOR

디스플레이 합성과 VSync

해상도 · 압축
프레임 예산—
실제 표시 fps—
끊김 (같은 프레임 반복)—
합성 DRAM 대역폭—
위 왼쪽: 겹쳐지는 층(화면 대비 면적), 위 오른쪽: 합성 방식별 DRAM 트래픽(층 읽기, 합성 결과 쓰기·다시 읽기). 화소당 4바이트, 압축을 켜면 약 1/2로 잡았다. 아래: 시간 축. 세로선이 VSync, 막대가 앱의 프레임 렌더링(평균 ±35% 흔들림), 위쪽 칸이 실제로 화면에 나간 프레임 번호다. 빨간 칸은 새 프레임이 없어 이전 것을 반복한 VSync다. 렌더링은 두 개의 버퍼를 번갈아 쓴다고 가정했다.

주사율을 120 Hz로 올리면 부드러워지지만 앱이 프레임을 8.3 ms 안에 만들어야 하고, 합성 대역폭과 패널 구동 전력도 두 배가 된다. 그래서 최근 패널(LTPO OLED)은 화면이 멈춰 있을 때 1~10 Hz까지 주사율을 낮추고, 스크롤할 때만 120 Hz로 올린다. 디스플레이 엔진은 이 밖에도 색 공간 변환, HDR 톤 매핑, 화면 크기 변환, 패널 압축(DSC)을 고정 회로로 한다. 사진 한 장이 화면에 나타나기까지 ISP → NPU → 코덱 → 디스플레이 엔진을 거치는 긴 여행은 1장에서 따라가 보았다.

작업데이터 속도 (대략)전용 블록 전력 (대략)비고
12 MP 카메라 미리보기·촬영 30 fps원시 약 0.5 GB/s수백 mW (ISP)여러 장 합성(HDR) 시 몇 배
4K60 HEVC 디코드출력 약 0.75 GB/s약 0.1~0.3 W입력은 수십 Mb/s
4K60 HEVC 인코드 (녹화)입력 약 0.75 GB/s약 0.3~0.6 W움직임 추정이 대부분
QHD+ 120 Hz 디스플레이 합성약 1~4 GB/s약 0.1~0.3 W (엔진)패널 자체 전력은 별도로 수백 mW~1 W 이상
음성 호출어 감지 (상시)16 kHz × 16비트 = 32 KB/s약 1 mW 안팎 (DSP)2020년대 저전력 DSP·전용 회로

핵심 정리

  1. 전용 회로는 명령어 처리가 없고, 데이터가 이웃 단계로 바로 흐르고, 필요한 비트만 써서 CPU보다 수십~수백 배 에너지 효율이 좋다. 대가는 유연성이다.
  2. ISP는 베이어 원시 데이터를 블랙 레벨 → 결함 화소 → 렌즈 음영 → 디모자이크 → 화이트 밸런스 → 색 보정 → 노이즈 제거 → 톤 매핑 → 선명화 순으로 처리한다.
  3. 이웃 K×K만 보는 처리는 K−1줄의 라인 버퍼로 스트리밍할 수 있어 프레임 버퍼와 DRAM 왕복이 필요 없다.
  4. 비디오 압축 = 움직임 추정(SAD 탐색) + DCT + 양자화 + 엔트로피 부호화. 정보를 버리는 것은 양자화뿐이고 QP로 화질과 비트레이트를 맞바꾼다.
  5. DSP는 MAC과 VLIW, 하드웨어 반복문으로 FIR 같은 신호 처리를 저전력으로 돌린다. 디스플레이 엔진은 층을 하드웨어로 합성하고 VSync 박자에 맞춰 내보낸다.

확인 퀴즈

Q1. 베이어 패턴 센서에서 디모자이크가 필요한 이유는?

색 필터 배열 때문에 각 화소에는 한 색의 값만 있다. 디모자이크는 이웃 화소의 같은 색 값을 이용해 나머지 두 색을 추정한다. 잘못하면 가는 무늬에서 지퍼 무늬와 가짜 색이 생긴다.

Q2. 가로 4,000화소, 화소당 2바이트인 영상에 7×7 필터를 스트리밍으로 적용할 때 필요한 라인 버퍼는 대략?

(7 − 1)줄 × 4,000화소 × 2바이트 = 48,000바이트. 현재 줄은 들어오는 대로 쓰므로 K−1줄이면 된다. 프레임 전체(수십 MB)에 비하면 아주 작다.

Q3. 16×16 블록, 탐색 범위 ±8의 전역 탐색에서 블록 하나에 계산하는 후보 위치 수는?

가로·세로 각각 −8~+8로 17개씩, 17² = 289개 위치. 위치마다 256화소의 SAD를 계산하므로 블록 하나에 약 7만 4천 번의 절댓값 차이 누적이 든다.

Q4. 비디오 압축에서 화질 손실이 실제로 일어나는 단계는?

DCT와 엔트로피 부호화, 움직임 예측은 모두 되돌릴 수 있다. 계수를 큰 간격으로 나눠 반올림하는 양자화만 정보를 버린다. QP가 클수록 많이 버린다.

Q5. 63탭 FIR 필터를 48 kHz 오디오 두 채널에 적용할 때 필요한 MAC/s는?

63 × 48,000 × 2 = 6,048,000. 클럭당 MAC 4개인 300 MHz DSP라면 부하는 0.5% 남짓이다. 오디오 필터는 DSP에게 가벼운 일이다.

Q6. 하드웨어 오버레이(디스플레이 엔진 합성)가 GPU 합성보다 DRAM 대역폭을 덜 쓰는 이유는?

GPU 합성은 층 읽기 + 결과 프레임 쓰기 + 디스플레이 엔진이 그 프레임을 다시 읽기가 필요하다. 오버레이는 층 읽기만 하면 된다.