3. CXL协议基础(1):CXL.io、CXL.cache、CXL.mem三大子协议简介

聊CXL协议,很多人一上来就被它的三层架构搞懵了。说实话,我第一次看spec的时候也在想:一个互联协议,干嘛要分三个子协议?搞这么复杂干嘛?

后来在实际项目中踩过坑才明白——这三个子协议,其实是分别解决三个完全不同的问题。有点像你盖房子,CXL.io是找平地基,CXL.cache是搭承重墙,CXL.mem才是铺水管电线。缺了哪个都不行。

3.1 为什么需要三个子协议?

我们先搞清楚背景。CXL的全称是Compute Express Link,它要干的事是让CPU和加速器、内存扩展设备之间能高效通信。但高效这个词,对不同场景含义完全不同。

  • 设备发现和初始化——比如你插上一块CXL内存扩展卡,系统得先认出它、分配资源。这个不需要多快,但必须可靠。
  • 缓存一致性——加速器要访问CPU的缓存数据,两边必须保持同步。这需要低延迟,而且协议得复杂一点。
  • 内存直接访问——把远端内存当成本地内存用,吞吐要高,延迟要低。

这三个目标,你用一套协议去cover,不是不行,但会非常臃肿。所以CXL干脆分了三个子协议,各管一摊。

子协议 核心作用 类似场景
CXL.io 设备发现、配置、初始化、I/O操作 PCIe的替代升级版
CXL.cache 访问并维护CPU侧缓存一致性 加速器和CPU共享缓存
CXL.mem 直接读写远端内存,支持内存池化 内存扩展、内存共享

你想想看,如果没有CXL.cache,加速器每次读数据都得走主存,延迟高得离谱。如果没有CXL.mem,你想把内存扩展到远端设备,系统根本不知道那块内存在哪。

3.2 CXL.io —— 基础通道

CXL.io说白了就是PCIe的戴了CXL帽子的版本。它基于PCIe 5.0/6.0的物理层和事务层,加了CXL特有的控制逻辑。

我个人习惯把CXL.io看作是整个CXL链路的总管。它负责:

  • 链路训练、初始化、热插拔
  • 设备枚举和地址空间分配
  • 中断、错误报告、虚通道管理
  • 非一致性I/O数据传输

嗯,这里要注意:CXL.io必须始终存在。即使你的设备只用了CXL.mem,它也得先通过CXL.io把基础通路搭好。我在项目中遇到过有人只配了CXL.mem的控制器,结果忘了开CXL.io的虚通道,设备根本没法被系统识别。折腾了两天才发现问题。

我的建议:做CXL设备开发,先把CXL.io的链路训练和配置空间调试通。这一步稳了,后面两个子协议才有机会跑起来。

3.3 CXL.cache —— 缓存一致性协议

这个就有点意思了。CXL.cache允许一个设备(比如AI加速器)直接访问并缓存CPU的cache line。关键是,它要保证一致性。

为什么会需要这个?举个例子:你有一个硬件加速器在做图像处理,它频繁读取CPU缓存里的数据。如果没有CXL.cache,加速器只能通过DMA把数据搬到自己本地内存,处理完再搬回去。一来一回,延迟翻倍。

CXL.cache让加速器可以直接发起cache请求,CPU的缓存控制器会协同处理。它定义了四种请求类型:

  • RdShared —— 读共享,别人也能读
  • RdUnique —— 读独占,我要改数据
  • WriteUnique —— 写一份新的,其他副本无效
  • WriteBack —— 把修改的数据写回

我曾经在调试一个FPGA加速器时,发现它的CXL.cache老是回写失败。查了一圈,发现是设备端的coherence engine没有正确处理snoop响应。说白了,就是CPU问它“你有没有改这块数据”,它回答错了。

避坑提醒:CXL.cache的实现远比看起来复杂。如果没有丰富的cache一致性设计经验,建议先用CXL.mem过渡,等技术成熟了再上CXL.cache。否则一个race condition就能让你调一个月。

3.4 CXL.mem —— 内存语义访问

CXL.mem是我最看重的子协议,也是CXL内存扩展技术的核心。它的目标很明确:让远端设备的内存,看起来就像本地DDR一样。

CXL.mem定义了两种访问模式:

  • MemRd —— 从远端内存读数据
  • MemWr —— 向远端内存写数据

就这么两样?对,但背后的事情可不少。CXL.mem需要解决延迟、带宽、一致性、原子操作等一堆问题。它不要求维护CPU的cache一致性,但需要保证访问顺序——也就是你写进去了,别人读到的必须是你写的内容。

我记得有个项目,客户想把CXL内存当作主存的冷数据分层。我建议他们先做延迟测试,因为CXL.mem的延迟虽然比本地内存低(相对网络),但毕竟走了物理链路和协议转换,一般会有100ns~300ns的额外延迟。这个数字做cache也许不行,做大容量内存池完全能接受。

对比项 本地内存 (DDR5) CXL.mem 扩展内存
延迟 ~80ns 200ns~500ns
带宽 单通道~40GB/s 单链路~64GB/s(PCIe 5.0 x16)
容量 受限DIMM槽位 可扩展到TB级
一致性 硬件保证 需配合CXL.cache

3.5 三个子协议怎么配合?

很多人以为这三个协议是独立的,选一个用就行。其实不是的。CXL.device必须实现CXL.io,然后根据应用场景选择是否实现CXL.cache和CXL.mem。

比如说:

  • 一个纯内存扩展设备:CXL.io + CXL.mem
  • 一个有自身cache的加速器:CXL.io + CXL.cache
  • 一个既要缓存一致性又要内存访问的加速器:CXL.io + CXL.cache + CXL.mem

在实际项目中,第三种情况最常见。三个子协议通过CXL链路层复用同一条物理通道,用传输层的虚通道隔离开。CXL.io走VC0,CXL.cache和CXL.mem走VC1。这样,设备发现和内存访问互不干扰。

最后,我放一张框架图,帮你把这三者的关系理清楚。

核心理解:CXL.io是基础,CXL.mem是内存扩展的入口,CXL.cache是性能加速器。根据你的硬件能力和应用需求,选择合适的子协议组合。
CXL协议架构框架 CPU Host CXL.io CXL.cache CXL.mem CXL Device 设备发现 / 配置 缓存一致性访问 内存直接读写 说明 CXL.io必须存在;CXL.cache和CXL.mem按需实现 三个子协议通过虚通道在同一条物理链路上复用

总结一句话:CXL.io是基础通道,CXL.mem是内存扩展的入口,CXL.cache是性能加速。理解好这三个子协议的分工,后面讲CXL内存扩展设备怎么设计、怎么调优,你就有了清晰的坐标系。