Gem5 Version: v25.1.0.0

gem5 多核互联主要分成两条路径: 分别采用 ClassicRuby 两种 memory system

  1. Classic memory system:
    每个 core 的 cache hierarchy 最终通过一个或多个共享 crossbar/bus 接到 memory side。这里的互联核心对象通常是 SystemXBarL2XBar
  2. Ruby memory system:
    每个 core 并不是直接挂到共享 bus 上,而是先绑定到对应的 Ruby controllerRubySequencer,再由 networktopology 将这些 controller 连接起来。

连接关系如下:

  • Classic: CPU -> cache hierarchy -> XBar -> memory
  • Ruby: CPU -> RubySequencer/controller -> network/topology -> directory/memory controller

Classic memory system 的多核互联机制

Classic memory system 的 interconnect 主要由 BaseXBar 的具体实现承担。最常见的是:

  • SystemXBar: 系统级共享 interconnect,连接 caches、memory controllers、I/O。
  • L2XBar: 较靠近 core side 的局部 interconnect,通常用于把多个 L1 cache 汇聚到 shared L2,或者作为单核 private L2 前端的局部连接点

在 stdlib 的 cache hierarchy 里,多核通常不是直接“核与核相连”,而是各个 core 的 cache hierarchy 汇到共享 XBar

因此,多核之间的共享路径体现为“共享 interconnect + 共享下层 cache/memory”

结构一: private L1 + shared SystemXBar

最简单的 Classic 多核结构是每个 core 有 private L1IL1D,但它们的 mem_side 都接到同一个 SystemXBar

1
2
3
4
core0 -> L1I/L1D \
core1 -> L1I/L1D \
core2 -> L1I/L1D -> SystemXBar -> memory
... /

这张图对应的是最直接的 Classic shared interconnect 形式。所有 cores 的 private L1 后端都汇入同一个 SystemXBar,因此这个 SystemXBar 是主要 shared point,也是多核共享访问路径上的核心仲裁点

关键代码:
src/python/gem5/components/cachehierarchies/classic/private_l1_cache_hierarchy.py

关键逻辑:

  1. _get_default_membus() 创建默认的 SystemXBar(width=64)
  2. incorporate_cache() 中,按 board.get_processor().get_num_cores() 为每个 core 创建 private L1I/L1D
  3. 对每个 core:
    • cpu.connect_icache(...)
    • cpu.connect_dcache(...)
    • self.l1icaches[i].mem_side = self.membus.cpu_side_ports
    • self.l1dcaches[i].mem_side = self.membus.cpu_side_ports

结构二: private L1 + shared L2 + SystemXBar

另一种常见结构是所有 cores 的 private L1 先接到一个共享 L2XBar,再通过 shared L2Cache 接到 SystemXBar

1
2
3
4
core0 -> private L1 \
core1 -> private L1 \
core2 -> private L1 -> shared L2XBar -> shared L2 -> SystemXBar -> memory
... /

这张图里有两个关键 shared point: shared L2XBar 和 shared L2Cache

相比上一种结构,它把多个 cores 的流量先在更靠近 CPU side 的 shared L2 层级汇聚,再进入系统级 SystemXBar

关键代码:
src/python/gem5/components/cachehierarchies/classic/private_l1_shared_l2_cache_hierarchy.py

关键逻辑:

  1. 创建默认 SystemXBar
  2. 按 core 数量创建 private L1I/L1D
  3. 创建一个共享 L2XBar 和一个共享 L2Cache
  4. 每个 core 的 L1 cache 都接到 self.l2bus.cpu_side_ports
  5. self.l2bus.mem_side_ports = self.l2cache.cpu_side
  6. self.membus.cpu_side_ports = self.l2cache.mem_side

结构三: private L1 + private L2 + shared SystemXBar

还有一种结构是每个 core 都有自己的 L2XBar 和 private L2Cache,但各个 L2 的后端仍并到同一个 SystemXBar

1
2
3
4
5
6
7
8
9
10
11
core0 -> L1I/L1D -> L2XBar_0 -> private L2_0 --\
\
core1 -> L1I/L1D -> L2XBar_1 -> private L2_1 ----> +===================+
|| SystemXBar ||
core2 -> L1I/L1D -> L2XBar_2 -> private L2_2 ----> +===================+
/ |
/ v
/ Mem ctrl
/ |
/ v
/ memory

这个结构把每个 core 的上层 cache hierarchy 尽量本地化,shared point 被下推到系统级 SystemXBar。因此,core 之间不会共享 L2,但仍通过同一个 system interconnect 访问更远端的 memory system。

关键代码:
src/python/gem5/components/cachehierarchies/classic/private_l1_private_l2_cache_hierarchy.py

关键逻辑:

  1. 创建默认 SystemXBar
  2. 按 core 数量创建 self.l2buses = [L2XBar() ...]
  3. 对每个 core:
    • 创建 private L2Cache
    • 再在其下创建 private L1IL1D
    • self.l2buses[i].mem_side_ports = l2_node.cache.cpu_side
    • self.membus.cpu_side_ports = l2_node.cache.mem_side

core 数限制

在 stdlib 里,core 通常来自 processor 对象:

  • src/python/gem5/components/processors/abstract_processor.py
  • src/python/gem5/components/processors/simple_processor.py

其中:

  • SimpleProcessor 会按照 num_cores 直接创建 SimpleCore 列表。
  • cache hierarchy 则按 board.get_processor().get_num_cores() 循环创建 cache 和连接。

4. Ruby memory system 的多核互联机制

Ruby 的设计思路与 Classic 不同。它并不是让 CPU 直接接共享 bus,而是引入一套更显式的 memory system 组件:

  • RubySequencer
  • protocol-specific controller
  • network
  • topology

Ruby 中,CPU 的 memory access 会先进对应的 RubySequencer/controller,之后消息通过 Ruby network 在多个 controller 之间传递。这里的“互联拓扑”主要由 network + topology 决定,而不是 SystemXBar

Ruby 的主要入口文件是: configs/ruby/Ruby.py

关键流程:

  1. create_system() 创建 RubySystem
  2. 通过 Network.create_network() 创建 network 对象
  3. 根据当前编译的 protocol 调用 configs/ruby/<protocol>.pycreate_system()
  4. 由 protocol 脚本返回:
    • cpu_sequencers
    • dir_cntrls
    • topology
  5. 再调用 topology.makeTopology(...) 将 controllers 具体连接成网络

Ruby 的 network 与 topology

network 选项定义在: configs/network/Network.py

默认值:

  • --topology=Crossbar
  • --network=simple

其中,

  • network:
    选择底层网络实现,如 simplegarnet
  • topology:
    决定 controllers 和 routers 如何连接,如 CrossbarPt2PtMesh_XY

protocol 将 cores 变成 network nodes

MESI_Two_Level 为例: configs/ruby/MESI_Two_Level.py

关键逻辑:

  1. 对每个 options.num_cpus:
    • 创建 L1I/L1D cache
    • 创建 MESI_Two_Level_L1Cache_Controller
    • 创建 RubySequencer
  2. 每个 controller 的 request/response MessageBuffer 都接到:
    • ruby_system.network.in_port
    • ruby_system.network.out_port
  3. 再创建 shared L2 controller、directory controller、DMA controller
  4. 最后把所有 controllers 组成 all_cntrls
  5. 调用 create_topology(all_cntrls, options)

Ruby 中:

  • CPU 先接 RubySequencer
  • RubySequencer 挂在 protocol-specific controller
  • controller 再通过 message buffers 连接到 Ruby network

MOESI_CMP_directory 的结构与此类似:
configs/ruby/MOESI_CMP_directory.py

CHI 的互联机制

CHI 是 Ruby 里更接近片上网络组织方式的一条路径,结构比 MESI_Two_Level 更明确地区分 node 类型:

  • RNF
  • HNF
  • SNF
  • RNI
  • MN
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CPU0 -> RNF0 --\
CPU1 -> RNF1 ---\
CPU2 -> RNF2 ----\
\
+----------- NoC fabric -----------+
DMA ----> RNI_DMA --/ \
I/O ----> RNI_IO --/ \
\
+--------+ +--------+ +--------+
| HNF0 | | HNF1 | | HNF2 |
| LLC | | LLC | | LLC |
+--------+ +--------+ +--------+
| | |
+------------+------------+
|
v
+----------------+
| SNF / MainMem |
+----------------+
|
v
memory

Additional CHI nodes:
MN: misc node, used for system-wide CHI functions
Boot/ROM memory: optional BootMem SNF nodes

这张图强调的是 CHI 的 node type 分层,而不是某一种固定物理 router 排布。RNF 代表 CPU side request node,HNF 代表 home node / LLC slice,SNF 代表 memory side node,RNI 代表 I/O 或 DMA 入口,整体通过一张 NoC fabric 互联。

关键代码: configs/ruby/CHI.py

关键逻辑:

  1. 先检查 num_dirsnum_l3caches 等约束
  2. options.num_cpus 生成 RNF
  3. 生成 HNFSNF、DMA RNI、I/O RNI
  4. 设置 downstream destination
  5. 根据 options.topology 决定用哪些 nodes 去创建 topology

Ruby 下的具体 topology

Crossbar

关键代码: configs/topologies/Crossbar.py

机制:

  1. 为每个 controller 建一个 router
  2. 再额外建一个 central xbar router
  3. 每个 controller 通过 ExtLink 接到自己的 router
  4. 每个 router 再通过 IntLink 双向连接到 central xbar
1
2
3
4
5
6
CPU0 -> RubySequencer0 -> L1 controller0 -> router0 --\
CPU1 -> RubySequencer1 -> L1 controller1 -> router1 ---\
CPU2 -> RubySequencer2 -> L1 controller2 -> router2 ----> [ central xbar router ]
L2 controller0 -----------------------------> router3 ---/
Dir controller0 ----------------------------> router4 --/
DMA / I/O controller -----------------------> router5 -/

这里的 shared point 是中心 xbar router。与 Classic 的 shared SystemXBar 类似,它也是集中式结构,但运行语义是 Ruby network 上的 message transfer,而不是普通 port 直接连线。

Pt2Pt

关键代码: configs/topologies/Pt2Pt.py

机制:

  1. 每个 controller 一个 router
  2. 每个 router 与其它所有 routers 全互连
  3. 因此 internal link 数量是 O(N^2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CPU0 -> seq0 -> controller0 -> router0
CPU1 -> seq1 -> controller1 -> router1
CPU2 -> seq2 -> controller2 -> router2
Dir0 -----------------------> router3
L2_0 -----------------------> router4

router0 <-> router1
router0 <-> router2
router0 <-> router3
router0 <-> router4
router1 <-> router2
router1 <-> router3
router1 <-> router4
router2 <-> router3
router2 <-> router4
router3 <-> router4

Pt2Pt 没有单一中心节点,所有 routers 彼此直接互联。它的优点是路径直观,缺点是 router 和 link 数量增长很快,因此在 node 数量上去后会明显变重。

这种 topology 在 node 数量增大时会迅速膨胀,因此虽然没有源码层面的硬上限,但实际可扩展性受限。

Mesh_XY

关键代码: configs/topologies/Mesh_XY.py

机制:

  1. num_routers = options.num_cpus
  2. num_rows = options.mesh_rows
  3. num_rows x num_columns 构建二维 mesh
  4. controller 通过 ExtLink 均匀分布到 routers
  5. router 之间建立 East/West/North/South IntLink
  6. link weight 用于实现 XY routing
1
2
3
4
5
R(0,0) ---- R(0,1) ---- R(0,2)
| | |
R(1,0) ---- R(1,1) ---- R(1,2)
| | |
R(2,0) ---- R(2,1) ---- R(2,2)

在这个 topology 中,shared point 不再是单个中心节点,而是整个 mesh fabric。controller 通过 ExtLink 分布到不同 routers 上,消息按照 XY routing 穿过 mesh 到达目标 controller。

Mesh_XY 是 Ruby 中更接近典型 NoC 的多核互联形式。

Ruby 的 core 数约束

实际约束主要来自 protocol 和 topology

包括:

  • Mesh_XY:
    要求 mesh_rows > 0,且 num_columns * num_rows == num_routers
  • MESI_Three_Level / MESI_Three_Level_HTM:
    要求 options.num_cpus % options.num_clusters == 0
  • MOESI_AMD_Base:
    要求 options.num_cpus 为偶数
  • CHI:
    要求 num_dirs >= 1num_l3caches >= 1

Ruby 术语

  • Ruby memory system:
    gem5 中基于 message passing 的 memory system。CPU 不直接连共享 bus,而是通过 RubySequencer 和 protocol-specific controller 接入 network
  • RubySequencer:
    CPU side 的 Ruby 入口,负责把 CPU 的 memory access 转成 Ruby protocol 能处理的请求。
  • controller:
    Ruby protocol 中的控制节点,例如 L1 controllerL2 controllerdirectory controllerDMA controller。这些 controller 是 topology 连接的基本对象。
  • network:
    Ruby 底层网络实现,例如 simplegarnet。它定义链路、router、buffer 等基础通信机制。
  • topology:
    controller 和 router 的连接方式,例如 CrossbarPt2PtMesh_XY。它决定 message 在网络中如何穿行。
  • MessageBuffer:
    Ruby controller 与 network 之间传递 request/response/unblock 等消息的缓冲与端口对象。

CHI 相关术语

  • CHI:
    Ruby 中一种更偏向 NoC/SoC 组织方式的 protocol 路径,节点类型划分更明确。
  • RNF:
    Request Node Functional。CPU side request node,通常承载 core 相关的请求入口。
  • HNF:
    Home Node Functional。通常对应 home node 与 LLC slice,是请求路由和 home 语义的核心节点。
  • SNF:
    Subordinate Node Functional。通常对应 main memory side 或其它下游 memory node。
  • RNI:
    Request Node I/O。I/O 或 DMA 一类 non-CPU requester 的入口节点。
  • MN:
    Misc Node。承载某些系统级 CHI 功能的附加节点。
  • NoC fabric:
    不是某个单独类名,而是对整张 on-chip interconnect 网络的概括性称呼