Ch 8: CXL.mem — M2S·S2M·HDM Decoder
#한 줄 요약
“CXL.mem은 host CPU의 load·store instruction이 64 B cache line 단위로 변환되어 PCIe 링크 위를 흐르는 프로토콜입니다.” — M2S Req·RwD가 명령을, S2M NDR·DRS가 응답을 전달합니다. HDM Decoder가 system physical address → device physical address 매핑을 담당하고, 응답은 요청의 Tag로 짝지어집니다.
Ch 7에서 디바이스가 host memory를 cache하는 CXL.cache를 봤습니다. 이 장은 반대 방향 — host가 device memory를 load/store하는 CXL.mem입니다.
#CXL.mem의 매력 — Native load/store
CPU의 load instruction (mov rax, [0x12345000])이 device memory에 직접 도달합니다:
| 단계 | 처리 |
|---|---|
| 1 | CPU 명령 — mov rax, [VA] |
| 2 | MMU가 VA → PA 변환 |
| 3 | 메모리 컨트롤러가 DDR 또는 CXL Root Port로 분기 (HDM Decoder) |
| 4 | CXL Root Port → CXL link → CXL device |
| 5 | Device가 DRAM read (64 B cache line) |
| 6 | Device → host로 응답 |
| 7 | CPU가 데이터 받음, load 완료 |
드라이버 호출 없음. DMA setup 없음. NVMe SSD와는 완전히 다른 의미입니다.
이게 가능한 이유는 HDM Decoder가 해당 물리 주소 범위를 CXL 디바이스로 보내도록 설정되기 때문입니다.
#메시지 채널
CXL 3.1 기준 CXL.mem은 방향마다 세 채널, 모두 6개입니다(§3.3.2). 기본은 방향마다 두 채널이고, HDM-DB를 지원하는 디바이스는 BI* 두 채널(S2M BISnp·M2S BIRsp)을 더 씁니다.
| 방향 | 채널 | 메시지 | 의미 |
|---|---|---|---|
| Host → Device | M2S Req | MemRd·MemRdData·MemInv | host의 read·invalidate 요청 |
| Host → Device | M2S RwD | MemWr·MemWrPtl | host의 write 요청 + data |
| Device → Host | S2M NDR | Cmp·Cmp-S·Cmp-E·Cmp-M | no-data response (write 완료·invalidate 완료) |
| Device → Host | S2M DRS | MemData | data response (read 결과 64 B) |
| Device → Host | S2M BISnp | BISnp | Back-Invalidation Snoop (HDM-DB 영역) |
| Host → Device | M2S BIRsp | BIRsp | BISnp에 대한 host 응답 (HDM-DB 영역) |
기본은 M2S Req → S2M DRS (read) 또는 M2S RwD → S2M NDR (write). BISnp는 HDM-DB 영역에서 씁니다. HDM-DB는 Type 2와 Type 3 모두 쓸 수 있습니다(Ch 3).
#Read 트랜잭션 흐름
가장 단순한 load 동작:
| 단계 | 동작 |
|---|---|
| 1 | CPU 명령: mov rax, [0x12345000] |
| 2 | MMU가 VA → PA 변환 (예: 0x80000000) |
| 3 | HDM Decoder가 PA를 CXL device로 라우팅 |
| 4 | Host → Device: M2S Req MemRd, addr=0x80000000, tag=42 |
| 5 | Device가 DRAM read (64 B cache line) |
| 6 | Device → Host: S2M DRS MemData, tag=42, payload 64 B |
| 7 | CPU 데이터 수령, load 완료 |
Tag는 요청을 보낸 쪽(Master)이 트랜잭션 동안 잡아 둔 entry 번호(16 bit)입니다. 디바이스(Subordinate)는 응답에 이 값을 그대로 돌려주고, host는 그것으로 응답을 원래 요청에 연결합니다(CXL 3.1 §3.3). 한 채널 안에는 기본적으로 순서 규칙이 없어서(§3.3.2), 응답이 요청 순서대로 온다고 가정할 수 없습니다.
#Write 트랜잭션 흐름
Write는 RwD 채널로 명령과 데이터를 함께 보냅니다:
| 단계 | 동작 |
|---|---|
| 1 | CPU 명령: mov [0x80000000], rax |
| 2 | Host → Device: M2S RwD MemWr, addr=0x80000000, tag=43, 64 B payload |
| 3 | Device DRAM write |
| 4 | Device → Host: S2M NDR Cmp, tag=43 (write completion) |
Completion이 짧다는 점에 주의 — write data는 RwD에 실어 한 번에 보냄. host는 Cmp 응답만 기다리면 됩니다.
MemWrPtl (Partial Write)은 64 B 미만 쓰기에 사용. 64-bit Byte Enable을 함께 보내 어느 byte를 update할지 지정합니다.
#HDM Decoder — 주소 매핑의 핵심
CPU가 0x80000000에 load 했을 때, 그 주소가 어느 CXL 디바이스의 어느 DRAM에 해당하는지 결정하는 곳이 HDM Decoder입니다.
| 항목 | 의미 |
|---|---|
| Input | System Physical Address (SPA) |
| Output | Device Physical Address (DPA) + target device |
| Configurable | host CPU·CXL switch·CXL device 각 단계에 |
| Programming | Linux는 cxl create-region 시 자동 |
단일 디바이스 매핑:
| SPA Range | Device DPA |
|---|---|
| 0x0000_8000_0000 ~ 0x0000_FFFF_FFFF | Device A: 0x0 ~ 0x7FFF_FFFF (2 GB) |
2-way interleave (256 B 단위):
| 구간 | SPA | Device | DPA |
|---|---|---|---|
| 0 | 0x80000000 | A | 0x0 |
| 1 | 0x80000100 | B | 0x0 |
| 2 | 0x80000200 | A | 0x100 |
| 3 | 0x80000300 | B | 0x100 |
(interleave granularity = 256 B)
#Interleave Granularity
Linux 커널의 HDM decoder 코드는 granularity로 256 B부터 16 KB까지(2의 거듭제곱)를 받습니다.
| Granularity | 장점 | 단점 |
|---|---|---|
| 256 B | 부하가 여러 디바이스로 고르게 흩어짐 | 한 디바이스 안의 연속성이 짧음 |
| 4 KB | 한 디바이스 안에서 연속 영역 유지 | 접근이 몰리면 한 디바이스에 hot spot |
| 16 KB | sequential read·prefetch에 유리 | random에 약함 |
워크로드 access pattern에 따라 고릅니다. Sequential bulk read는 큰 granularity, random은 작은 granularity가 맞습니다.
Linux에서는 region을 만들 때 정하고, cxl list로 확인합니다.
$ cxl create-region -m -d decoder0.1 -w 2 -g 1024 mem0 mem1$ cxl list -R# region 출력에 "interleave_ways":2, "interleave_granularity":1024 가 보입니다#BISnp — HDM-DB의 Coherency Maintenance
HDM-DB 영역에서는 host가 디바이스 메모리의 line을 cache하고 있어도, 디바이스가 그 line을 무효화해야 할 때가 있습니다. Type 2라면 디바이스 자신이 쓰려 할 때, Type 3라면 다른 host나 peer가 접근할 때입니다. 이때 디바이스가 host에 보내는 것이
*BISnp (Back-Invalidation Snoop)*이고, host는 M2S BIRsp로 답합니다. 자세한 흐름은 Ch 3 메모리 일관성에서 다뤘습니다.
#Flit Packing과 Credit Flow Control
CXL.mem 메시지는 *flit (flow control unit)*에 packing되어 PCIe PHY로 전송됩니다.
| Flit 모드 | 크기 | 비고 |
|---|---|---|
| 68B flit | 68 B | link layer flit 528 bit(16 B slot 4개 + CRC 2 B) + Protocol ID 2 B. 1.1·2.0 |
| 256B flit | 256 B | 3.0에서 추가. 4.0의 128 GT/s도 사용 |
자세한 flit 구조는 Ch 9 Flit Format에서.
Credit-based flow control은 받는 쪽 버퍼가 넘치지 않게 하는 장치입니다. 받는 쪽이 받을 수 있는 만큼 credit을 주고, 보내는 쪽은 credit이 있을 때만 보냅니다. 받는 쪽이 처리하면 credit이 반환됩니다.
#Linux 측 — Region 생성과 사용
# Decoder 확인$ cxl list -DT
# Region 생성 (sysfs 또는 cxl-cli)$ cxl create-region -m -d decoder0.0 -t ram -s 128G \ -w 2 -g 256 mem0 mem1# -m: 뒤의 target 인자를 memdev 이름으로 해석# -t ram: 휘발성(ram) region# -w 2: 2-way interleave# -g 256: 256 B granularity (root decoder 설정과 맞아야 함)
# DAX 모드 또는 system RAM 모드 전환$ daxctl reconfigure-device dax0.0 -m system-ram
# NUMA 노드 확인$ numactl --hardware# node 2: CXL.mem region (별도 NUMA)자세한 Linux drivers/cxl/ 코드 분석은 Ch 11에서.
#자주 하는 실수
#“CXL.mem이 cache miss마다 CXL 트래픽 발생”
아닙니다. CPU의 L1·L2·L3가 CXL.mem 데이터를 캐시합니다. cache hit이면 CXL 트래픽 0. miss일 때만 CXL 트래픽. cache hit rate가 CXL.mem 워크로드의 핵심 metric.
#“Type 3 디바이스는 BISnp와 무관하다”
HDM-H Type 3면 그렇습니다. 하지만 Type 3도 direct P2P나 multi-host 일관성을 위해 HDM-DB를 쓸 수 있고, 그때는 BISnp 채널을 지원해야 합니다.
#“Interleave granularity는 작을수록 좋다”
워크로드 의존입니다. Sequential access는 큰 값이, random은 작은 값(최소 256 B)이 맞습니다.
#“CXL.mem load는 DDR load와 동일”
Latency가 다릅니다. 실제 CXL 디바이스 3종을 잰 연구(Sun et al., MICRO 2023)에서 load 지연이 원격 소켓 DDR5의 1.35배~약 3배로 디바이스마다 갈렸습니다. Linux에서 CXL 메모리는 별도 NUMA 노드로 보입니다(Ch 11).
#정리
- CXL.mem은 host load/store가 64 B cache line 단위 메시지로 변환되는 프로토콜입니다.
- M2S Req·RwD가 명령, S2M NDR·DRS가 응답. 응답은 Tag로 요청과 짝지음.
- HDM Decoder가 SPA → DPA + target device 매핑. interleave 설정 가능.
- Interleave granularity는 256 B~16 KB 중 선택. 워크로드 access pattern 의존.
- BISnp는 HDM-DB 영역에서 씁니다(Type 2·3). HDM-H Type 3는 쓰지 않습니다.
#다음 편
Ch 9: Flit Format — 68B vs 256B vs Latency-Optimized에서 CXL의 데이터 단위인 flit 구조가 세대별로 어떻게 진화했는지를 본격적으로 분해합니다.
#관련 항목
- Ch 3: 메모리 일관성 모델
- Ch 11: Linux drivers/cxl/ 분석
- HBM·GDDR 심화 Ch 10: CXL.mem 프로토콜 분해
- Embedded Performance Engineering Ch 54: CXL.mem 지연·대역폭 실측
#시리즈 자료 출처 안내
이 글은 CXL 3.1 spec, Linux drivers/cxl/ 소스, ndctl 문서, Sun et al. MICRO 2023를 근거로 합니다. 시리즈 전체의 자료 정책은 Ch 1에 있습니다.
CXL 4.0 Internals · 8 of 15
- 1 Ch 1: CXL의 자리와 진화 — 1.1에서 4.0까지
- 2 Ch 2: System Architecture — Type 1·2·3·MLD·MH-MLD
- 3 Ch 3: 메모리 일관성 모델 — HDM-DB·HDM-D·Bias·BISnp
- 4 Ch 4: Pooling·GFAM·Fabric — Multi-host 메모리 공유
- 5 Ch 5: CXL 4.0의 핵심 새 기능 — 128 GT/s·Bundled Port
- 6 Ch 6: CXL.io — PCIe와의 차이·DOE·DVSEC
- 7 Ch 7: CXL.cache — D2H·H2D 흐름과 coherency state
- 8 Ch 8: CXL.mem — M2S·S2M·HDM Decoder
- 9 Ch 9: Flit Format — 68B vs 256B vs Latency-Optimized
- 10 Ch 10: ARB/MUX — 세 프로토콜의 PHY 다중화
- 11 Ch 11: Linux drivers/cxl/ 분석 — Mainline kernel CXL 구현
- 12 Ch 12: QEMU CXL 에뮬레이션 — 노트북에서 CXL 개발
- 13 Ch 13: Switching·Fabric Manager — 2.0 pooling에서 3.x fabric까지
- 14 Ch 14: Security — IDE·SPDM·TSP·CXL TEE
- 15 Ch 15: RAS·Performance·Compliance — 운용·검증의 마지막 단계
관련 글
Ch 9: Flit Format — 68B vs 256B vs Latency-Optimized
68B·Standard 256B·Latency-Optimized 256B flit의 구조와 retry·credit 처리.
같은 시리즈에서 이어 읽기
Ch 10: ARB/MUX — 세 프로토콜의 PHY 다중화
같은 PHY에 CXL.io·CXL.cache·CXL.mem을 시분할로 흘리는 layer.
같은 시리즈에서 이어 읽기
CXL.mem 프로토콜 분해 — 왕복 횟수가 만드는 지연, 링크가 만드는 대역폭
Ch 9의 지연·대역폭 수치가 왜 그렇게 나오는지 — 왕복 횟수로 본 지연 예산, 링크에 묶인 대역폭, credit 고갈이 만드는 throughput 절벽, interleave granularity의 유불리.
공통 태그 기반 추천
이 글을 참조하는 글 (8)
- CXL.mem 프로토콜 분해 — 왕복 횟수가 만드는 지연, 링크가 만드는 대역폭 — HBM·GDDR 심화
- Ch 15: RAS·Performance·Compliance — 운용·검증의 마지막 단계 — CXL 4.0 Internals
- Ch 11: Linux drivers/cxl/ 분석 — Mainline kernel CXL 구현 — CXL 4.0 Internals
- Ch 9: Flit Format — 68B vs 256B vs Latency-Optimized — CXL 4.0 Internals
- Ch 7: CXL.cache — D2H·H2D 흐름과 coherency state — CXL 4.0 Internals
- Ch 3: 메모리 일관성 모델 — HDM-DB·HDM-D·Bias·BISnp — CXL 4.0 Internals
- Ch 2: System Architecture — Type 1·2·3·MLD·MH-MLD — CXL 4.0 Internals
- Ch 1: CXL의 자리와 진화 — 1.1에서 4.0까지 — CXL 4.0 Internals