﻿# CXL —— 让 PCIe 带上缓存一致性的新型总线

> PCIe 只能传数据，设备上的内存和主内存是两套独立的空间——互相看不到对方的缓存、没法直接 load/store，必须走显式 DMA。**CXL（Compute Express Link）** 解决了这个问题：它基于 PCIe 5.0/6.0 的物理层，但在协议层上加上了**缓存一致性**和**共享内存语义**，首次让"设备上的内存"和"主内存"在同一个缓存一致性域内统一访问。


> 相关：PCIe 细节见 [../io/pcie/pcie.md](/concepts/io/pcie/pcie.md)，CPU 总线全景见 [cpu-bus-architecture.md](/concepts/cpu/cpu-bus-architecture.md)，DMA 与零拷贝见 [../io/dma.md](/concepts/io/dma.md)，NUMA 拓扑见 [../numa/numa.md](/concepts/numa/numa.md)。

## 零、一句话结论

| 总线 | 物理层 | 带缓存一致性 | 设备内存能被 CPU 直接 load/store | 主内存能被设备缓存 |
|------|--------|------------|-------------------------------|-------------------|
| **PCIe** | PCIe PHY | **不带** | 只能 MMIO（写设备配置寄存器），不能直接读写设备上的 DRAM | 只能 DMA（显式拷贝） |
| **CXL** | PCIe PHY（同一条物理线） | **带** | 可以（CXL.mem 设备） | 可以（CXL.cache 设备） |

CXL 本质上就是**在 PCIe 物理层上跑加了缓存一致性的新协议**——CXL 设备和 PCIe 设备插在同一个物理槽上，但行为完全不同。

---

## 一、为什么有 PCIe 还不够

### 1.1 传统 PCIe 的两堵墙

```bash
场景 1: PCIe 内存扩展卡
  卡上有 512GB DDR5
  CPU 想用这块内存?
  → 只能通过 block 层 / NVMe 协议 做块读写
  → 无法直接 mov [CXL_mem_addr], rax
  → 延迟: 微秒级（走 NVMe 协议栈），不是纳秒级（走 IMC 时序）
场景 2: GPU/FPGA 加速器（PCIe）
  GPU 想读 CPU 主存里的某个数据结构?
  → 必须 CPU 先发起 DMA 把数据拷到 GPU 显存
  → 或者 GPU 发起 PCIe DMA 读主存: PCIe MemRd TLP → CPU 查 cache → flush
  → 延迟大、编程模型复杂（显式管理两个内存池）
```

**根本原因**：PCIe 协议不认识缓存——它不知道数据在不在某个 CPU 的 L1/L2/L3 里。所以设备读主存时，要么走显式 DMA（需要软件协调 flush），要么只能用 MMIO 读设备寄存器（不含缓存一致性机制）。

### 1.2 CXL 解决了什么

CXL 在 PCIe 链路层上加了三套子协议（io/cache/mem），让设备可以：

- 像另一个 NUMA 节点一样被 CPU 直接访存（`mov` 指令直接读设备内存）
- 像另一个核一样缓存主内存（设备有 cache、参与一致性协议）

```bash
PCIe:  CPU ←→ PCIe ←→ 设备   (非一致性 IO)
CXL:   CPU ←→ CXL  ←→ 设备   (缓存一致性 + 共享内存)
```

---

## 二、CXL 三大子协议

| 子协议 | 干什么 | 谁发起的 | 典型场景 |
|--------|--------|---------|---------|
| **CXL.io** | PCIe 兼容层——设备发现、枚举、配置空间、MMIO | 必须支持 | 任何 CXL 设备的必备基础 |
| **CXL.cache** | 设备可以缓存宿主机的内存（设备→主机方向的一致性） | 设备发起 | 加速器（FPGA/GPU/智能网卡）缓存主存的"热数据" |
| **CXL.mem** | 宿主机可以把设备上的内存当系统内存直接 load/store（主机→设备方向的一致性） | 主机发起 | CXL 内存扩展卡、持久内存 |

### 2.1 CXL.io：必经之路

```bash
CXL.io = 标准 PCIe 协议的兼容子集
作用是: 启动时枚举设备、读 BAR、分配 MMIO 空间、配置 MSI-X 中断
CXL.io 本身不带缓存一致性——就是标准的 PCIe TLP
```

任何 CXL 设备都必须实现 `CXL.io`，否则 BIOS/OS 根本发现不了它。

### 2.2 CXL.cache：设备缓存主存

设备（GPU/FPGA/智能网卡）声明"我想要一份主存数据的缓存副本"：

```bash
主机 CPU                  CXL 设备（加速器）
  L1/L2/L3                  设备本地 cache
    |                        |
    | ← CXL.cache 协议 ←    | 设备请求: 我要缓存地址 0x12345000 的数据
    |                        |
    | → 响应:                |
    |   1. 数据从主存/CPU cache 取出
    |   2. 送回设备
    |   3. CPU 侧记录: "设备有这个地址的 S(Shared) 副本"
    |                        |
    如果 CPU 后来改了这行:
    → CXL.cache → Invalidate 设备端的副本
    （和两个核之间的 MESI 协议完全一样的逻辑）
```

> **关键**：CXL.cache 让设备参与 CPU 缓存一致性协议的 Snoop/Directory 机制——设备不再是"哑终端"，而是一个"有 cache 的 peer"。

### 2.3 CXL.mem：主机直接用设备内存

CXL 内存扩展卡（Type 3 设备）通过 CXL.mem 把自己板上的 DDR5 贡献给主机：

```bash
主机 CPU 侧                   CXL 设备侧
  mov rax, [cxl_addr]         CXL 内存控制器
  │                           │
  ├→ L1 miss → L2 miss        │
  │→ L3 miss                  │
  │→ IMC: 地址译码           │
  │  发现目标在 CXL 域 →      │
  │→ CXL Root Complex ───────→│ CXL Controller
  │                           │→ 查设备本地 DDR 控制器
  │                           │→ 读 DDR5 → 返回数据
  │← CXL 链路返回数据 ←───────│
  │→ L3/L2/L1 填充           │
  │→ rax 得到结果             │
```

**延迟量级**：CXL.mem 访问 ≈ CPU 本地 DDR + CXL 链路往返 + 设备端 DDR 延迟 ≈ 150-300ns（Gen5），约为本地 DDR 的 2-3 倍。OS 通常把它暴露为一个 NUMA 节点——`numactl --hardware` 可以看到。

---

## 三、CXL 三种设备类型

| 类型 | 支持子协议 | 是什么 | 例子 |
|------|-----------|--------|------|
| **Type 1** | CXL.io + CXL.cache | 纯加速器——设备有自己的 cache，缓存主存 | 智能网卡、FPGA 加速器 |
| **Type 2** | CXL.io + CXL.cache + CXL.mem | 加速器 + 自己有内存——双向一致性 | GPU、AI 加速器、NPU |
| **Type 3** | CXL.io + CXL.mem | 纯内存扩展——只贡献内存给主机 | CXL 内存扩展卡、持久内存 |

```plantuml
@startuml
skinparam shadowing false
skinparam rectangle {
  BackgroundColor<<host>> #E3F2FD
  BorderColor<<host>> #1976D2
  BackgroundColor<<cxl>> #C8E6C9
  BorderColor<<cxl>> #388E3C
}
rectangle "Host CPU\nCXL Root Complex\n(在 System Agent 内)" <<host>> as CPU
rectangle "CXL Switch\n(可选, CXL 2.0+)" <<cxl>> as SW
rectangle "Type 3\nCXL Memory\nExpander\n512GB DDR5" <<cxl>> as T3
rectangle "Type 2\nCXL GPU/AI\nAccelerator\n+ HBM" <<cxl>> as T2
rectangle "Type 1\nCXL NIC/SmartNIC\n带 cache" <<cxl>> as T1
CPU -down- SW
SW -down- T3
SW -down- T2
SW -down- T1
note right of CPU : OS 把 Type 3 内存\n当作普通 NUMA 节点\nnumactl 可见
note bottom of SW : CXL 2.0+ 支持 Switch\n允许多层拓扑
@enduml
```

---

## 四、CXL 版本演进

| 版本 | 年份 | 基于 PCIe | 关键新特性 |
|------|------|----------|-----------|
| **CXL 1.0/1.1** | 2019 | PCIe 5.0 | 基本协议定义，单设备无 Switch |
| **CXL 2.0** | 2020 | PCIe 5.0 | **CXL Switch** 支持（多设备共享一个根端口）、内存池化（MLD）、全局 Fabric 管理 |
| **CXL 3.0** | 2022 | PCIe 2×6.0 | **多级 Switch**、设备间 P2P 直访、全局 Fabric 附加内存、容量翻倍（双路 64GT/s）|
| **CXL 3.1** | 2023 | PCIe 6.0 | 安全性增强（TSP）、PBR（Port-Based Routing）扩展 |

> **当前落地状态（2025-2026）**：CXL 2.0 在 Sapphire Rapids/Emerald Rapids + Genoa 上已商用；Type 3 内存扩展卡（三星、SK hynix、Micron）已可用。CXL 3.0 设备仍在早期。

---

## 五、编程模型变化

### 5.1 传统 PCIe 模型 vs CXL 模型

```bash
传统 PCIe (GPU 加速器):
  // CPU 把数据从主存拷到 GPU 显存
  cudaMemcpy(d_gpu, h_cpu, size, cudaMemcpyHostToDevice);
  // GPU 计算
  kernel<<<...>>>(d_gpu);
  // CPU 把结果拷回来
  cudaMemcpy(h_cpu, d_gpu, size, cudaMemcpyDeviceToHost);
  → 两次拷贝，数据有三份：CPU 主存、GPU 显存、GPU 计算用的寄存器/共享内存
  → 显式管理，延迟大，编程复杂
CXL Type 2 (GPU 直接 load/store 主存):
  // GPU 上的 kernel 直接:
  int x = *shared_ptr;  // ← 走 CXL.cache 直接读 CPU 主存
  *shared_ptr = x + 1;  // ← 走 CXL.cache 写回 CPU 主存
  → 零拷贝！内存只存一份
  → 缓存一致性由硬件保证（不用软件 flush）
```

### 5.2 CXL 内存扩展卡的使用

```bash
# CXL Type 3 内存卡插入后，OS 看到新的 NUMA 节点
numactl --hardware
# available: 3 nodes (0-2)
# node 2 cpus: ... (无核——纯内存节点)
# node 2 size: 524288 MB  ← 512GB CXL 内存
# 把内存分配在 CXL 节点上
numactl --membind=2 ./my_app
# 只把冷数据放在 CXL 节点（热数据留在本地 DDR）
# 用 libnuma API: numa_alloc_onnode(size, 2)
```

---

## 六、UCIe 与统一内存架构

### 6.1 UCIe —— Chiplet 的时代标准

AMD（IFOP）、Intel（Foveros/EMIB）、NVIDIA（NVLink-C2C）各有自己的 Die-to-Die 互联方案，互不兼容。**UCIe（Universal Chiplet Interconnect Express）** 的目标是定义标准化接口，让不同厂商的 Chiplet 能拼装在一起。

```bash
现状（不互通）:
  AMD Chiplet ←IFOP→ AMD Chiplet
  Intel Tile ←EMIB/Foveros→ Intel Tile
  NVIDIA Chiplet ←NVLink-C2C→ NVIDIA Chiplet
UCIe 愿景（互通）:
  厂商 A Chiplet ←UCIe PHY→ 厂商 B Chiplet
```

| 封装方式 | 带宽密度 | 延迟 | 功耗 | 适用 |
|---------|---------|------|------|------|
| **Standard (有机基板)** | ~1.3 TB/s/mm | 2+ ns | 0.5 pJ/bit | 板级 Chiplet |
| **Advanced (硅桥/中介层)** | ~3+ TB/s/mm | <2 ns | <0.25 pJ/bit | 2.5D/3D 封装 |

> **UCIe 和 CXL 的关系**：CXL 是**系统级**（Socket 到插卡），UCIe 是**封装级**（Die 到 Die）。两种标准互补——CXL 管机箱内的设备互联，UCIe 管芯片封装内的 Die 互联。

### 6.2 统一内存架构大趋势

| 架构 | 实现 | 特点 |
|------|------|------|
| **Apple M 系列** | CPU+GPU+NPU 同片，共享 LPDDR 控制器 | 零拷贝、低功耗，不可扩展 |
| **AMD MI300A** | Zen 4 CCD + CDNA 3 GCD 共封装，统一 HBM | CPU 和 GPU 共用同一物理 HBM 池 |
| **NVIDIA Grace Hopper** | Grace CPU + Hopper GPU，NVLink-C2C 互联 | 750GB/s、缓存一致性、统一编程模型 |

> **趋势本质**：从"CPU 内存 + GPU 显存 + 之间搬运"走向"一个统一的、缓存一致的、共享的物理内存池"。CXL 是**系统级**实现这一目标，UCIe 是**封装级**实现。

---

## 七、总结

| 问题 | 答案 |
|------|------|
| CXL 和 PCIe 什么关系？ | CXL 用 PCIe 的物理层（同一条线、同一种插槽），但协议不同——加了三套缓存一致性子协议 |
| CXL.cache 和 CXL.mem 的区别？ | CXL.cache = 设备缓存主存（设备→主机方向）；CXL.mem = 主机直接 load/store 设备内存（主机→设备方向） |
| CXL 内存延迟如何？ | ~150-300ns（Gen5），是本地 DDR 的 2-3 倍，但比 NVMe 协议栈（微秒级）快数十倍 |
| UCIe 是什么？ | 封装级 Die-to-Die 互联标准，类比 CXL 是 Socket 级、UCIe 是封装级 |
| CXL 对程序员有什么改变？ | 不再需要显式 cudaMemcpy——设备上直接 load/store 主存，零拷贝访问 |

---

*关联：[cpu-bus-architecture.md](/concepts/cpu/cpu-bus-architecture.md)（CPU 总线全景）、[../io/pcie/pcie.md](/concepts/io/pcie/pcie.md)（PCIe 详解）、[../io/dma.md](/concepts/io/dma.md)（DMA 与零拷贝）、[../numa/numa.md](/concepts/numa/numa.md)（NUMA 感知编程）*

