온칩 인터커넥트와 NoC
카메라 셔터를 누르는 순간, SB-1 안에서는 ISP가 초당 수 GB의 화소를 DRAM으로 밀어내고, NPU는 가중치를 끌어오고, 디스플레이 엔진은 1/120초마다 화면 한 장을 빠짐없이 읽어 가야 하며, 그 와중에 CPU는 앱 코드를 실행한다. 스무 개가 넘는 블록이 하나의 메모리를 향해 동시에 달려드는 셈이다. 이 교통을 정리하는 것이 온칩 인터커넥트(On-chip interconnect)다. 이 장에서는 두 블록이 데이터 한 덩어리를 주고받는 약속(핸드셰이크)에서 출발해, 공유 버스와 크로스바를 지나, 칩 전체에 깔린 도로망인 NoC(Network-on-Chip)까지 올라간다. 마지막에는 직접 부하를 올려 가며 도로가 막히는 순간을 찾아본다.
- valid/ready 핸드셰이크로 데이터가 언제 넘어가는지 파형에서 읽을 수 있다.
- AXI의 다섯 채널, 버스트, 다중 미결 트랜잭션과 ID가 대역폭을 어떻게 살리는지 설명한다.
- 고정 우선순위와 라운드 로빈 중재를 비교하고 기아가 생기는 조건을 찾는다.
- 버스·링·크로스바·메시의 비용과 대역폭이 노드 수에 따라 어떻게 커지는지 비교한다.
- 패킷·플릿·웜홀 라우팅, 머리 막힘, 교착과 XY 라우팅, QoS를 시뮬레이터로 확인한다.
데이터 한 덩어리를 건네는 약속: valid와 ready
인터커넥트의 가장 작은 단위는 두 블록 사이의 한 줄짜리 통로다. 보내는 쪽(송신자, 마스터나 상류)이 데이터를 실어 두고, 받는 쪽(수신자, 하류)이 그것을 가져간다. 문제는 둘의 사정이 다르다는 것이다. 송신자는 데이터가 준비되지 않았을 수 있고, 수신자는 버퍼가 꽉 차서 지금은 받을 수 없을 수 있다. 그래서 두 개의 신호로 약속을 한다.
- valid: 송신자가 "지금 데이터 선에 올린 값은 유효하다"고 알리는 신호.
- ready: 수신자가 "지금 받을 수 있다"고 알리는 신호.
규칙은 단 하나다. 클럭 상승 에지에서 valid와 ready가 둘 다 1이면 그 순간 데이터 하나가 넘어간다. 이를 핸드셰이크(Handshake)라 부른다. 수신자가 ready를 내려 송신자를 기다리게 하는 것을 백프레셔(Backpressure)라 한다. 아래 파형에서 valid와 ready 칸을 눌러 바꿔 보자.
이 약속이 좋은 이유는 양쪽이 서로의 속도를 몰라도 된다는 것이다. 송신자는 valid만 올려 두고 기다리면 되고, 수신자는 형편이 될 때 ready를 올리면 된다. 그래서 같은 인터페이스로 빠른 SRAM도, 느린 DRAM 컨트롤러도, 중간에 끼운 버퍼나 클럭 변환기도 똑같이 연결할 수 있다. 파이프라인 단계마다 이 핸드셰이크를 두면 막힘이 한 단계씩 뒤로 전파되면서 아무 데이터도 잃지 않는다. 다만 valid가 ready를 기다리고 ready가 valid를 기다리는 식의 조합 회로 고리가 생기면 회로가 멈추므로, AXI는 "송신자는 ready를 보고 valid를 정하면 안 된다"고 못박아 둔다.
AXI: 다섯 갈래 길과 기다리지 않는 요청
Arm이 정한 AMBA AXI(Advanced eXtensible Interface)는 오늘날 SoC 블록 대부분이 쓰는 표준 인터페이스다. AXI는 핸드셰이크 통로 다섯 개를 묶은 것이다.
| 채널 | 방향 | 싣는 것 |
|---|---|---|
| AR (Read Address) | 마스터 → 슬레이브 | 읽을 주소, 버스트 길이, ID |
| R (Read Data) | 슬레이브 → 마스터 | 읽은 데이터 비트(beat)들, ID, 마지막 표시(RLAST) |
| AW (Write Address) | 마스터 → 슬레이브 | 쓸 주소, 버스트 길이, ID |
| W (Write Data) | 마스터 → 슬레이브 | 쓸 데이터와 바이트 마스크(WSTRB) |
| B (Write Response) | 슬레이브 → 마스터 | 쓰기 완료 응답(OK/오류) |
채널이 나뉘어 있으니 읽기와 쓰기가 서로를 기다리지 않고 동시에 흐른다. 여기에 세 가지 장치가 더해져 대역폭을 끌어올린다.
- 버스트(Burst): 주소 한 번으로 연속된 데이터 여러 비트(최대 256)를 받는다. 캐시 라인 64 B를 128비트 버스로 읽으면 주소 1번, 데이터 4비트다.
- 다중 미결 트랜잭션(Multiple outstanding transactions): 첫 요청의 데이터가 오기 전에 다음 주소를 계속 보낸다. DRAM처럼 지연이 긴 상대일수록 중요하다.
- 트랜잭션 ID: 같은 ID끼리는 응답이 요청 순서대로 와야 하지만, ID가 다르면 먼저 준비된 것부터 돌려줄 수 있다(순서 뒤바뀜, out-of-order).
아래 시뮬레이터에서 마스터 하나가 빠른 SRAM(지연 3클럭)과 느린 DRAM(지연 20클럭)에 요청을 섞어 보낸다. 미결 한도와 ID 방식을 바꿔 R 채널이 얼마나 쉬지 않고 일하는지 보자.
AXI 트랜잭션 타임라인
DRAM 요청 뒤에 SRAM 요청이 줄 서 있는 상황을 떠올려 보자.
답 보기
한도 1이면 요청 하나의 지연(DRAM 20클럭) 동안 R 채널이 놀아서 사용률이 20% 남짓에 그친다. 한도를 올리면 여러 요청의 지연이 겹쳐 숨겨지므로 사용률이 크게 오른다. 그러나 ID가 모두 같으면 SRAM 데이터가 3클럭 만에 준비되어도 앞선 DRAM 응답이 끝날 때까지 기다려야 한다(머리 막힘). ID를 대상별로 나누면 SRAM 응답이 DRAM 응답을 앞질러 오고, 평균 지연이 줄어든다. 일반적으로 필요한 미결 수는 지연 × 대역폭(리틀의 법칙)으로 정해진다. 지연 20클럭 동안 R 채널을 꽉 채우려면 비트 20개가 날아다니고 있어야 한다.
AXI는 점대점 프로토콜이지 네트워크 자체가 아니다. 저속 레지스터 접근에는 단순한 APB, 캐시 일관성이 필요한 CPU 클러스터 사이에는 스누프 채널을 더한 ACE나 패킷형 CHI가 쓰인다(6장). 칩 안의 NoC는 바깥쪽에서는 블록마다 AXI로 말을 받고, 안쪽에서는 이를 자기만의 패킷으로 바꿔 나른 뒤 반대편에서 다시 AXI로 풀어 준다. 이 변환기를 NIU(Network Interface Unit)라 한다.
한 길을 여럿이 쓸 때: 중재와 기아
가장 오래된 인터커넥트는 공유 버스(Shared bus)다. 모든 블록이 한 묶음의 선에 매달려 있고, 한 번에 한 마스터만 그 선을 쓴다. 누가 쓸지는 중재기(Arbiter)가 정한다. 가장 단순한 규칙은 고정 우선순위다. 언제나 순위가 높은 마스터가 이긴다. 회로는 간단하지만 위험하다. 아래에서 마스터 넷(CPU > GPU > DSP > 디스플레이 순)의 요청률을 올려 보자.
CPU와 GPU의 요청률 합을 1 가까이 올리면 고정 우선순위에서는 DSP와 디스플레이가 거의 버스를 얻지 못한다. 순위가 낮은 마스터가 무한정 기다리는 이 현상을 기아(Starvation)라 한다. 디스플레이가 굶으면 화면이 깨지고, 오디오 DSP가 굶으면 소리가 끊긴다. 라운드 로빈은 방금 허락받은 마스터를 맨 뒤로 보내 순서를 돌린다. 요청하는 마스터라면 누구든 최대 \(N-1\)번만 기다리면 차례가 오므로 기아가 없다. 실제 SoC는 여기에 가중치(대역폭 비율)와 마감 시간을 더한 중재를 쓰며, 이것이 뒤에서 볼 QoS의 출발점이다.
라운드 로빈은 모두에게 똑같이 나눠 주지만, 마스터마다 필요가 다르다. 디스플레이는 대역폭은 작아도 늦으면 안 되고, GPU는 대역폭은 크지만 조금 늦어도 괜찮다. 그래서 "공정"보다 "필요에 맞게"가 목표가 된다.
버스에서 크로스바, 링, 메시로
공유 버스는 블록이 몇 개일 때는 훌륭하지만, 블록이 늘어도 길은 하나라서 전체 대역폭이 늘지 않는다. 선이 길어지고 매달린 블록이 많아지면 정전 용량이 커져 클럭도 낮아진다. 해결책은 길을 여러 개 까는 것이다.
- 크로스바(Crossbar): 모든 입력과 모든 출력 사이에 스위치를 둔다. 서로 다른 목적지라면 동시에 N개가 오갈 수 있지만, 스위치 수가 \(N^2\)에 비례한다.
- 링(Ring): 노드를 원형으로 잇는다. 비용은 \(N\)에 비례하지만 평균 거리가 \(N/4\)로 늘고, 링을 반으로 자르는 단면의 대역폭이 고정되어 있다.
- 메시(Mesh): 바둑판처럼 이웃끼리 잇는다. 비용은 \(N\)에 비례하면서, 단면 대역폭이 \(\sqrt N\)에 비례해 늘어난다.
여기서 나온 단면 대역폭(Bisection bandwidth)은 네트워크를 노드 수가 같은 두 쪽으로 자를 때 잘리는 링크들의 대역폭 합(가장 작게 자르는 경우)이다. 무작위 트래픽이라면 패킷의 절반이 이 단면을 건너야 하므로, 단면이 전체 처리량의 상한이 된다. \(k\times k\) 메시(\(N=k^2\))는 단면에서 \(k\)개의 링크가 잘리므로 노드당 처리량이 \(\sim 4/k\)로 줄지만 총합은 \(\sqrt N\)에 비례해 늘어난다.
패킷, 플릿, 그리고 웜홀
NoC에서 요청 하나는 패킷(Packet)이 된다. 패킷은 다시 링크 폭만큼의 조각인 플릿(Flit, flow control unit)으로 나뉜다. 첫 플릿(머리, head)에는 목적지 주소가 들어 있고, 뒤따르는 몸통(body) 플릿들과 꼬리(tail) 플릿이 데이터를 싣는다. 64 B 캐시 라인을 128비트(16 B) 링크로 보내면 머리 1 + 데이터 4, 플릿 다섯 개다.
라우터가 패킷을 다음으로 넘기는 방식은 두 가지다. 저장 후 전달(Store-and-forward)은 패킷 전체를 받은 뒤에야 다음 라우터로 보낸다. 인터넷 라우터가 이렇게 한다. 웜홀(Wormhole)은 머리 플릿이 도착하자마자 길을 정해 바로 내보내고, 나머지 플릿이 지렁이처럼 그 뒤를 따른다. 버퍼는 플릿 몇 개분이면 충분하다. 홉 수와 패킷 길이를 바꿔 두 방식의 지연을 비교해 보자.
웜홀의 대가는 막힘이 길게 번진다는 것이다. 머리가 막히면 몸통 플릿들이 지나온 라우터 여러 개의 버퍼에 걸쳐 멈춰 서고, 그 링크들을 다른 패킷이 쓰지 못한다. 한 대기열의 맨 앞이 막혀 뒤에 선 패킷까지 못 가는 현상을 머리 막힘(Head-of-line blocking, HOL)이라 한다. 고전적인 예가 입력 대기열 스위치다. 입력마다 FIFO 하나만 두면, 맨 앞 패킷이 다른 입력과 같은 출력을 노려 지면 뒤의 패킷은 출력이 비어 있어도 못 나간다.
메시 NoC 시뮬레이터: 도로가 막히는 순간
이제 조각을 모두 조립하자. 아래는 라우터 16개(또는 36개)로 된 메시 NoC다. 라우터마다 입력 포트 다섯 개(동서남북 + 자기 블록)가 있고, 포트마다 플릿 몇 개분의 버퍼가 있다. 패킷은 웜홀 방식으로 흐르고, 출력 포트 하나를 두고 여러 입력이 다투면 라운드 로빈으로 중재한다. 각 라우터에 붙은 블록 색은 SB-1의 블록 종류다. 플릿 색은 패킷을 보낸 블록의 색이다.
먼저 주입률을 천천히 올려 보자. 처음에는 지연이 거의 변하지 않다가, 어느 순간 갑자기 치솟는다. 그 지점이 이 네트워크의 포화점(Saturation point)이다. "곡선 측정"을 누르면 지금 설정으로 부하를 바꿔 가며 지연-부하 곡선을 그린다.
메시 NoC 패킷 시뮬레이터
메모리 컨트롤러 노드 두 개로 들어가는 링크를 떠올려 보자. 노드 하나가 클럭당 받을 수 있는 플릿은 1개다.
답 보기
훨씬 낮다. 16개 노드가 내는 패킷의 절반이 MC 두 곳으로 몰리면, 주입률 \(r\)일 때 MC 하나에 클럭당 약 \(16 \times r \times 0.5 / 2 = 4r\)개의 플릿이 들어오려 한다. 이것이 1을 넘는 \(r \approx 0.25\)보다 훨씬 앞에서, MC로 들어가는 마지막 몇 링크가 먼저 꽉 차서 포화한다. 열지도에서 MC 주변만 빨갛게 달아오르는 것이 보인다. 그래서 실제 SoC는 메모리 컨트롤러를 여러 채널로 나눠 칩 여기저기에 흩고, 주소를 채널에 번갈아 섞어(인터리빙) 부하를 고르게 한다(7장).
몇 가지 실험을 더 해 보자. 버퍼 깊이를 1로 줄이면 포화점이 앞당겨진다. 웜홀 패킷이 여러 라우터에 걸쳐 멈춰 서서 링크를 오래 붙잡기 때문이다. 전치 트래픽에서는 XY 라우팅이 특정 열과 행에만 부하를 몰아 넣는데, 적응형 라우팅으로 바꾸면 덜 붐비는 길로 돌아가 포화점이 올라간다. 반대로 균일 트래픽에서는 XY가 이미 부하를 고르게 퍼뜨려 적응형의 이득이 작다. 라우팅 알고리즘에 정답이 없고 트래픽에 따라 다르다는 뜻이다.
교착: 모두가 서로를 기다릴 때
적응형 라우팅을 "서쪽 먼저"라는 이상한 이름으로 제한한 데는 이유가 있다. 웜홀 네트워크에서 패킷은 링크 하나를 붙잡은 채 다음 링크를 기다린다. 만약 네 패킷이 네모 모양으로 서로의 다음 링크를 붙잡고 있다면? 아무도 움직일 수 없다. 이것이 교착(Deadlock)이다. 사거리 네 방향에서 동시에 좌회전하려던 차들이 꽉 막힌 모습과 같다.
XY 라우팅이 교착을 피하는 이유는 간단하다. 패킷은 언제나 X 방향을 먼저 다 간 뒤 Y 방향으로 간다. 그러면 "Y에서 X로 꺾는" 회전이 하나도 일어나지 않는다. 고리를 만들려면 네 번의 같은 방향 회전이 필요한데, 그중 둘이 금지되었으니 고리가 생길 수 없다. 이것을 회전 모델(Turn model)이라 한다(Glass & Ni, 1992). 서쪽 먼저 라우팅은 여덟 가지 회전 가운데 "서쪽으로 꺾는" 두 회전만 금지해 교착을 막으면서도 나머지 방향에서는 길을 고를 자유를 남긴 것이다.
링크 하나에 독립된 버퍼(대기열) 여러 개를 두고 시간을 나눠 쓰게 하면, 물리적 링크는 하나지만 논리적으로는 길이 여러 개가 된다. 이것이 가상 채널(Virtual channel, VC)이다(Dally, 1992). VC는 두 가지 일을 한다. ① 막힌 패킷 옆으로 다른 패킷이 지나갈 수 있어 머리 막힘이 준다. ② 요청과 응답을 다른 VC에 태우면 "응답을 기다리느라 요청이 막히고, 요청이 막혀 응답을 못 보내는" 프로토콜 교착을 끊을 수 있다. 캐시 일관성 메시지(6장)처럼 요청 → 스누프 → 응답이 꼬리를 무는 트래픽에서는 VC 분리가 필수다.
QoS: 늦으면 안 되는 트래픽
디스플레이 엔진은 120 Hz 화면에 화면 한 장(예: 2,800×1,300 화소 × 4 B ≈ 14.6 MB)을 1/120초마다 읽어 패널로 보낸다. 약 1.75 GB/s, DRAM 대역폭의 몇 %에 불과하다. 하지만 이 데이터가 제때 오지 않으면 디스플레이 엔진의 작은 버퍼(FIFO)가 바닥나고, 패널에는 줄무늬나 깨진 화면이 나타난다. 이를 언더런(Underrun)이라 한다. 반면 GPU는 게임 중 수십 GB/s를 원하지만 프레임이 조금 늦게 끝나도 큰일은 아니다.
아래에서 GPU 부하를 올리면서 디스플레이 버퍼가 버티는지 보자. QoS(Quality of Service)를 켜면 디스플레이 트래픽을 별도의 높은 우선순위 가상 채널에 태우고, 버퍼가 비어 갈수록 우선순위를 더 올린다.
실제 NoC의 QoS는 여러 장치의 조합이다. 마스터마다 대역폭 조절기(Regulator)로 평균 대역폭 상한을 걸고, 패킷에 우선순위 필드를 달아 라우터 중재기가 참고하게 하며, 디스플레이·카메라처럼 실시간 블록은 버퍼 수위에 따라 "급함" 신호를 올려 메모리 컨트롤러가 그 요청을 먼저 처리하게 한다. 모뎀·오디오처럼 늦으면 통화가 끊기는 블록도 같은 대접을 받는다.
| 항목 | 대략값 | 비고 |
|---|---|---|
| 스마트폰 SoC NoC 클럭 | 약 0.8~2 GHz | 블록·영역마다 다른 클럭, 2023~2025년 |
| NoC 링크 폭 | 128~512 비트 | 메모리 쪽 간선은 더 넓다 |
| 링크 하나 대역폭 | 약 16~64 GB/s | 예: 256비트 × 1.5 GHz = 48 GB/s |
| 라우터 한 홉 지연 | 1~3 클럭 | 파이프라인 단계 수에 따라 |
| LPDDR5X 총 대역폭 (64비트) | 약 68 GB/s | 8.5 Gb/s/핀, 2024년 (7장) |
| 디스플레이 읽기 (120 Hz, 약 3.6 MP) | 약 1.7 GB/s | 층이 여럿이면 몇 배 |
| 서버 메시 NoC (예: Arm CMN-700) | 최대 약 12×12 메시 | 노드당 수십 GB/s, 2021년 공개 |
| 칩 내 데이터 이동 에너지 | 약 0.1 pJ/bit/mm | 칩 밖 DRAM은 수 pJ/bit (1장) |
핵심 정리
- valid와 ready가 같은 클럭 에지에서 함께 1일 때 데이터가 넘어간다. 송신자는 올린 valid를 전송 전에 내리면 안 되고, ready는 수신자가 백프레셔로 자유롭게 쓴다.
- AXI는 AR·R·AW·W·B 다섯 채널로 읽기·쓰기를 분리하고, 버스트·다중 미결 트랜잭션·ID로 긴 지연을 숨긴다. 필요한 미결 수 ≈ 지연 × 대역폭.
- 고정 우선순위 중재는 기아를 낳고, 라운드 로빈은 기아를 막는다. 실제 SoC는 필요에 맞춘 가중치·마감 기반 중재(QoS)를 쓴다.
- 버스·링은 싸지만 단면 대역폭이 고정, 크로스바는 빠르지만 N², 메시는 비용 N에 처리량 √N로 균형을 잡는다.
- 웜홀은 지연 Htr+L로 짧고 버퍼가 작지만 막힘이 번진다. 부하가 포화점에 다가가면 지연이 폭발한다. XY 같은 회전 제한 라우팅이나 가상 채널로 교착을 막는다.
확인 퀴즈
Q1. valid/ready 핸드셰이크에서 데이터가 넘어가는 조건은?
Q2. DRAM 왕복 지연이 80 ns이고 마스터가 64 B 요청으로 32 GB/s를 내고 싶다. 필요한 미결 트랜잭션 수는 대략?
Q3. 같은 AXI ID를 단 읽기 요청 두 개(앞은 DRAM, 뒤는 SRAM)에 대해 옳은 것은?
Q4. 노드 수 N이 커질 때 비용이 N²에 비례해 늘어나는 토폴로지는?
Q5. 5홉을 지나는 플릿 8개짜리 패킷, 라우터 지연 2클럭일 때 웜홀 지연은? (혼잡 없음)
Q6. 2차원 메시에서 XY 라우팅이 교착을 일으키지 않는 이유는?