CXL的基本概念
CXL的基本概念
本文参考是 CXL 1.1 规范,先从CXL 1.1 规范中提取一些基本概念,帮助理解 CXL 的基本概念。新的规范提供了更多的复杂功能,直接看新规范会比较难理解。考虑将来补充更多内容。
CXL的三种设备类型
Type 1 CXL 设备
支持的协议:CXL.io、CXL.cache
设备和主机都可以访问主机内存,并且具备缓存一致性。
Type 2 CXL 设备
支持的协议:CXL.io、CXL.cache、CXL.mem
Type 2 设备是指挂载了内存(例如 DDR、高带宽内存 HBM 等)的加速器设备。 这类设备的计算操作直接作用于其本地挂载的内存,其性能优势主要来自加速器与本地内存之间的大带宽通道。 CXL 的关键目标是为 Host 提供一种高效方式,使其能够将操作数写入设备挂载内存,并从中读取计算结果,同时不会引入额外的软件和硬件开销。
具有系统一致性且映射到主机地址空间的设备挂载内存称为 Host-managed Device Memory(HDM)。
HDM 与传统的 I/O 或 PCIe 模型中的 私有设备内存(Private Device Memory,PDM) 有本质区别。 私有设备内存的一个典型的例子是带有 GDDR 的 GPGPU(通用图形处理器)。 此类设备通常将其挂载的内存视为私有,不允许主机直接访问,也不与系统其他部分保持一致性。 这类内存完全由设备硬件和驱动程序管理,主要作为中间数据缓冲区,用于处理大型数据集。
从高层次来看,HDM(主机管理的设备内存)有两种预期的操作模型,具体描述如下。
基于偏向的一致性模型(Bias Based Coherency Model)
基于偏向的一致性模型为设备连接的内存(device-attached memory)定义了两种偏向状态:主机偏向(Host Bias)和设备偏向(Device Bias)。
当设备连接的内存在主机偏向状态时,对于设备而言,该内存看起来就像普通的主机连接内存。 如果设备需要访问它,必须向主机发送请求,由主机负责解析所请求缓存行的一致性。
相反,当内存在设备偏向状态时,设备可以确定主机没有缓存该缓存行,因此设备可以直接访问该内存,无需向主机发送任何事务(如请求或探测等)。
需要注意的是,主机对设备连接内存始终保持一致的视图,无论当前偏向状态如何。在这两种模式下,设备连接内存始终保持一致性。
该模型的主要优势包括:
- 有助于维护映射到系统一致地址空间的设备连接内存的一致性。
- 允许设备以高带宽访问其本地连接内存,而不会产生显著的一致性开销(例如对主机的探测)。
- 使主机可以像访问主机本地内存一样,以一致、统一的方式访问设备连接内存。
为了维持偏向模式,Type 2 类型的 CXL 设备将执行以下操作:
- 实现偏向表(Bias Table),以页为粒度(例如每个 4KB 页一个比特)跟踪偏向状态,该表可以通过偏向缓存(Bias Cache)在设备中缓存。
- 构建对偏向转换的支持,使用转换代理(Transition Agent, TA)完成。这本质上类似一个用于“清理”页的 DMA 引擎,主要任务是刷新主机缓存中属于该页的缓存行。
- 支持主机对加速器本地内存进行基本的加载和存储操作。
主机偏向(Host Bias)
主机偏向模式通常对应于任务周期中的两个阶段:主机在提交任务时将操作数写入设备连接内存,或在任务完成后从该内存中读取结果。 在主机偏向模式下,一致性流程支持主机以高吞吐量访问设备连接内存(如下图中的蓝色箭头所示); 而设备访问设备连接内存则不够高效,因为必须通过主机进行访问(如下图中的绿色箭头所示)。
设备偏向(Device Bias)
设备偏向模式用于设备执行任务期间,即从任务提交到完成之间的阶段。在此模式下,设备需要以高带宽和低延迟访问其连接的内存。
在该模式中,设备可以无需依赖主机的一致性机制,直接访问设备连接内存(如下图中的红色箭头所示)。 主机仍然可以访问设备连接内存,但可能会被加速器强制放弃对内存行的所有权(如下图中的绿色箭头所示)。 这种机制使设备能够从设备连接内存中获得理想的延迟和带宽,而主机的访问性能则可能受到影响。
模式管理
有两种设想的偏向模式管理方案——软件辅助和硬件自治。CXL支持这两种模式。
尽管下面描述了这两种模式,但值得注意的是,严格来说,设备不需要实现任何偏向。 (符合标准的最基本的情况下,设备可以不实现任何偏向,默认使用主机偏向) 在这种情况下,所有设备连接的内存都会退化为主机偏向模式。 这意味着,所有对设备连接内存的访问都必须通过主机进行路由。 加速器可以自由选择软件辅助和硬件自治偏向管理方案的自定义组合。主机实现与上述选择无关。
Type 3 CXL 设备
支持的协议:CXL.io、CXL.mem
Type 3 CXL 设备不是一个主动的计算引擎,主要作为主机的内存扩展器。
由于这不是一个加速器,设备不会通过 CXL.cache 发起任何请求。 该设备主要通过 CXL.mem 操作,以响应从主机发送的请求。 CXL.io 链接用于设备发现、枚举、错误报告和管理。 CXL 架构独立于内存技术,并允许根据主机中实现的支持,提供多种内存组织方式。
CXL 的3种事务类型
CXL.io 事务
CXL.io 提供了一个非一致性的加载/存储接口,用于 I/O 设备。
上图显示了 CXL.io 事务层在 Flex Bus 分层结构中的位置。
CXL.cache 事务
CXL.cache 协议定义了设备与主机之间的交互方式,这些交互包括一系列请求,每个请求至少有一个关联的响应消息,有时还包含数据传输。 该接口由三个通道组成,每个方向都有:请求(Request)、响应(Response)和数据(Data)。 这些通道的命名基于它们的方向:D2H(设备到主机)和 H2D(主机到设备),以及它们所携带的事务类型:请求、响应和数据。
D2H 请求携带来自设备到主机的新请求。 这些请求通常针对内存。每个请求将接收到零个、一个或两个响应,最多会有一个 64 字节的缓存行数据。 该通道可能会因背压而暂时阻塞,但没有问题。
D2H 响应携带来自设备到主机的所有响应。 设备对嗅探请求的响应指示该缓存行在设备缓存中留下的状态,并可能表示数据正在返回给主机的提供数据缓冲区。 D2H 响应需要保证进展,否则可能发生死锁。它们可能会因链路层信用暂时阻塞,但不应需要其他事务完成才能释放信用。 D2H 数据携带来自设备到主机的所有数据和字节使能。 数据传输可以是隐式或显式的写回。 在所有情况下,都会传输一个完整的 64 字节缓存行数据。 D2H 数据传输必须确保进展,否则可能发生死锁。它们可能因链路层信用暂时阻塞,但不应需要其他事务完成才能释放信用。
H2D 请求携带从主机到设备的请求。 这些请求用于嗅探以保持一致性。可能会返回数据以响应嗅探。 请求携带数据缓冲区的位置,任何返回的数据应写入该位置。 H2D 请求可能会因设备资源不足而遭遇背压,但这些资源必须在没有需要 D2H 请求进展的情况下被释放。
H2D 响应携带排序消息并用于拉取写数据。 每个响应都携带来自原始设备请求的请求标识符,以指示响应应路由到哪里。对于写数据拉取响应,消息携带数据应写入的位置。 H2D 响应只能因链路层信用暂时被阻塞。 H2D 数据携带设备读取请求的数据。在所有情况下,将传输一个完整的 64 字节缓存行数据。H2D 数据传输只能因链路层信用暂时被阻塞。
CXL.cache 接口在设备和主机之间有三个主要的通道,每个方向都有:请求、响应和数据,如下图所示。 这些独立的通道允许不同类型的消息使用专用的线路,从而实现解耦并提高每条线路的有效吞吐量。
D2H 请求的类型
CXL.cache Read
设备向主机发起一个“我要读主机内存某个地址”的请求,主机会在维持缓存一致性的基础上,把该内存地址对应的数据(64 字节缓存行)返回给设备。
CXL.cache Read0
CXL.cache Read0 是一种“只请求不返回数据”的一致性事务,通常用于设备通知主机进行缓存控制等操作。
通常用于处理设备(Device)向主机(Host)写入数据的事务。它的工作流程包括以下几个关键步骤:
- 请求发送:设备首先通过 D2H 请求信道 向主机发送写请求,这需要一个有效的请求信用。
- GO-I 与 WritePull 消息:主机收到请求后,必须发送 GO-I 消息和 WritePull 消息。
- GO-I 消息标志着写操作从内存顺序和缓存一致性角度上完成,设备可以放弃对缓存行的所有权(如果该消息是 Evict 操作)。
- WritePull 消息触发设备发送数据消息到主机。
- 数据传输:设备会向主机发送数据,数据总量为 64 字节。
- 完成操作:设备在接收到 GO-I 消息并发送完所有数据消息后,认为写操作完成,可以释放该缓存行的条目。
CXL.cache Read0-Write
CXL.cache Read0-Write 是一种事务类型,结合了读取和写入操作。它要求主机首先发送一个合并的 GO-I 和 WritePull 消息。 WritePull 消息触发设备向主机发送数据,数据总量为 64 字节,可以设置不同的字节使能。
一旦设备收到 GO-I 消息并发送完所有数据消息,它会认为事务完成,并释放设备上的条目。主机在接收到所有数据并发送 GO-I 响应消息后,认为事务完成。 此操作通常用于一些特殊的内存访问场景,如 ItoMWr(从写)。
更多请求略
D2H 响应的类型
略
H2D 请求的类型
CXL.cache H2D 请求 包含多种 Snoop
CXL.cache Snoop 是一种主机到设备的请求,用于确保数据一致性。主机会发出一个 Snoop 请求,设备必须根据请求返回一个响应。如果该响应包含数据(即 RspFwd 格式),设备会通过 D2H 数据通道 返回 64 字节的数据。否则,主机收到所有响应后即可认为请求已完成。
简单来说,Snoop 用于主机检查设备缓存中的数据,并在需要时从设备获取数据。
SnpData
SnpData 是这是来自主机的嗅探请求,针对的是那些打算在请求者处缓存为共享(Shared)或独占(Exclusive)状态的缓存行(只有当所有设备都以 RspI 响应时,独占状态才可以在请求者处缓存)。 这种类型的嗅探通常由数据读取请求触发。接收到此嗅探的设备必须将所有缓存行作废或降级为共享状态。 如果设备持有脏数据,它必须将数据返回给主机。
SnpInv
SnpInv 是来自主机的嗅探(snoop)请求,目标是将缓存行的所有权和独占(Exclusive)状态授予请求者。此类嗅探请求通常由写入请求触发。 当设备接收到 SnpInv 请求时,必须执行以下操作:
- 作废所有相关的缓存行。
- 如果设备持有“脏”数据(即修改过的数据),它必须将这些数据返回给主机。
简而言之,SnpInv 用于确保请求者获得缓存行的独占权限,并且在此过程中保持数据的一致性。
更多snoop请求略
H2D 响应的类型
WritePull
这个响应告诉设备将写数据发送到主机,但不改变缓存行的状态。它用于 WrInv,即在写操作完成通知(GO-I)之前需要获取数据,因为 GO-I 是 I/O 完成的通知。
GO
GO(Global Observation)消息表示读请求和写请求是一致的,且一致性得到了保证。它指示该事务已被系统设备观察到,且 RspType 字段中编码的 MESI 状态表示与该事务关联的数据应在请求者缓存中处于什么状态。
如果主机将 Modified 状态返回给设备,那么设备需要负责处理脏数据,并且在未将数据写回主机之前不能丢弃该缓存行。
如果主机将 Invalid 或 Error 状态返回给设备,则设备只能使用该数据最多一次,并且不能缓存该数据。对读取和可缓存写请求(例如 RdOwn 或 ItoMWr)的错误响应通常是由中止条件引起的,因此修改后的数据可以安全地在设备中丢弃。
更多响应略
CXL.mem 事务
CXL内存协议称为CXL.mem,它是CPU和内存之间的事务接口。 在跨芯片通信时,它使用计算扩展链接(CXL)的物理层和链路层。 该协议可用于多种不同的内存连接选项,包括内存控制器位于主机CPU时,内存控制器位于加速器设备内时,或内存控制器被移至内存缓冲芯片时。 它适用于不同类型的内存(易失性、持久性等)和配置(平面、分层等)。
CPU中的一致性引擎通过CXL.mem请求和响应与内存(Mem)进行接口。 在此配置中,CPU一致性引擎被视为CXL.mem主设备(Master),而内存设备被视为CXL.mem从设备(Subordinate)。 CXL.mem主设备是负责发起CXL.mem请求(读取、写入等)的代理,而CXL.mem从设备是负责响应CXL.mem请求(数据、完成等)的代理。
当从设备是加速器时,CXL.mem协议假定存在一个设备一致性引擎(DCOH)。 该代理被假定负责实现一致性相关功能,如基于CXL.mem命令的设备缓存嗅探和元数据字段更新。 对具有元数据的内存的支持是可选的,但这需要提前与主机进行协商。 协商机制不在本规范的范围内。 如果设备附加内存不支持元数据,则DCOH仍然需要使用主机提供的元数据更新来解释命令。 如果设备附加内存支持元数据,则主机可以使用它来实现针对CPU插槽的粗粒度嗅探过滤器。
从主设备到从设备的CXL.mem事务称为“M2S”,从从设备到主设备的事务称为“S2M”。
在M2S事务中,有两个消息类别:
- 无数据的请求,通用地称为请求(Req)
- 带数据的请求(RwD)
类似地,在S2M事务中,有两个消息类别:
- 无数据的响应,通用地称为无数据响应(NDR)
- 带数据的响应,通用地称为数据响应(DRS)
在 CXL.mem 协议中,嗅探(snoop) 是一种用于保持缓存一致性的机制。当主设备(Master,通常是 CPU)向从设备(Subordinate,通常是加速器或内存扩展设备)发出访问请求时,如果该内存区域可能被设备缓存持有,则需要发起嗅探请求来保证一致性。
什么时候需要嗅探?
CXL.mem 请求中是否需要执行嗅探,取决于 SnpType 字段 是否包含有效的嗅探命令。常见需要嗅探的情况如下:
⸻
✅ 以下操作可能触发嗅探:
- 带有有效 SnpType 的读取操作:
- 如果主设备发起一个 Read 请求,且 SnpType 表示需要嗅探,例如:
- SnpData(请求设备降级缓存行)
- SnpInv(请求设备无效化缓存行)
- 则设备必须检查其缓存并作出响应(如返回数据或将缓存行降级/无效化)。
- 带有有效 SnpType 的元数据请求(Meta Data Update)或 Invalidate 请求:
- 虽然这些请求本身不读写数据,但 SnpType 可能仍要求嗅探设备端的缓存状态。
- 部分写请求(如写共享行) 也可能包含嗅探要求,确保写入操作之前缓存状态一致。
⸻
❌ 以下操作通常不需要嗅探:
- SnpType 字段为空或无效(Invalid)
- 表示主设备不要求一致性维护,设备无需执行嗅探。
- Meta0 只读请求(MetaField & MetaValue 被忽略)且 Meta0-State 不是 I 状态:
- 只需内部状态更新,不涉及缓存一致性,通常也不会嗅探。
- 请求操作访问的是非缓存区域(如 MMIO 映射或不可缓存的内存)
⸻









