GEM5官方教程全流程: part 1

GEM5 官方教程:learning gem5

gem5有一个很重要甚至离谱的事情是,和计算机领域很多东西一样,它变化很快,所以时间很重要: 这是2023年11月的官方教程。我跑了一遍并记录下来。

在这里插入图片描述
一共有四个大类, A Getting Started; B Modifying/Extending; C Modeling Cache Coherence with Ruby; D gem5 101。这一篇先是A的部分。

A.1 编译 Building.

建议用docker。learning gem5 的页面里没有介绍,但是搜索builid gem5可以出来一个界面,介绍如何使用docker。用docker的话可以保证没有环境问题,一定能编译成功。在另一篇帖子里有细节,这里放两个核心代码:一个是运行docker,把host上的gem5放进docker里的gem5文件夹里;另一个是在docker的gem5路径下,开始编译,docker的环境都是配好的所以不用担心环境问题。

docker run -u $UID:$GID --volume <gem5 directory>:/gem5 --rm -it <image>
scons build/{ISA}/gem5.{variant} -j {cpus}

A.2 创建一个简单的配置脚本 Creating a simple configuration script

这里我们要跟着教程手写一些东西,从sketch开始使用GEM5。不想复制的话可以在gem5/configs/learning_gem5/part1/simple.py 找到。https://github.com/gem5/gem5/blob/stable/configs/learning_gem5/part1/simple.py 下载。

找一个路径新建一个python文件:例如我在gem5/configs/yztutorial/partA/,也可以随便选一个自己喜欢的地方,建一个空白的 simple.py
第一件事是导入 m5 库和我们编译的所有 SimObjects。

A2.1 import 库

import m5
from m5.objects import *

A2.2 配置system

接下来,我们将创建第一个 SimObject:我们要模拟的系统。该System对象将成为我们模拟系统中所有其他对象的父对象。该System对象包含许多功能(非时序级)信息,例如物理内存范围、根时钟域、根电压域、内核(在全系统仿真中)等。为了创建system SimObject,我们只需像普通的 python 类一样实例化它:

system = System()

但是现在我们没有clock或在freq的概念,所以我们要建立时钟域/clokc domain
现在我们已经有了要模拟的系统的参考,让我们在系统上设置时钟。我们首先必须创建一个时钟域。然后我们可以在该域上设置时钟频率。在 SimObject 上设置参数与在 python 中设置对象的成员完全相同,因此我们可以简单地将时钟设置为 1 GHz。最后,我们必须为该时钟域指定一个电压域。由于我们现在不关心系统功率,因此我们将仅使用电压域的默认选项。

system.clk_domain = SrcClockDomain()
system.clk_domain.clock = '1GHz'
system.clk_domain.voltage_domain = VoltageDomain()

一旦我们有了一个系统,我们就可以设置如何模拟内存。我们将使用时序模式进行内存模拟。除了快进和从检查点恢复等特殊情况外,您几乎总是使用计时模式进行内存模拟。我们还将设置一个大小为 512 MB 的单个内存范围,这是一个非常小的系统。请注意,在 python 配置脚本中,每当需要大小时,您都可以使用常见单位(例如’512MB’. 同样,对于时间,您可以使用时间单位(例如, ‘5ns’)。这些将自动分别转换为通用表示形式,其实是因为gem5里有代码做了字符串的匹配而已。。。

system.mem_mode = 'timing'
system.mem_ranges = [AddrRange('512MB')]

现在,我们可以创建一个 CPU。我们将从 gem5 中用于 X86 ISA 的最简单的基于时序的 CPU X86TimingSimpleCPU开始。此 CPU 模型在单个时钟周期内执行每条指令,但流经内存系统的内存请求除外。要创建 CPU,您只需实例化该对象即可:

system.cpu = X86TimingSimpleCPU()
#其他选项是 RiscvTimingSimpleCPU ArmTimingSimpleCPU,这里先用x86吧,教程是这么用的

接下来,我们将创建系统范围的内存总线:

system.membus = SystemXBar()

现在我们有了内存总线,让我们将 CPU 上的缓存端口连接到它。在这种情况下,由于我们要模拟的系统没有任何缓存,因此我们将 I-cache 和 D-cache 端口直接连接到 membus。在这个示例系统中,我们没有缓存。

system.cpu.icache_port = system.membus.cpu_side_ports
system.cpu.dcache_port = system.membus.cpu_side_ports
#其他可连的选项是,system.cpu.icache_port = system.l1_cache.cpu_side,这就是有l1缓存里。在memobejct会涉及更多,这里先照着教程用就行

接下来,我们需要连接一些其他端口以确保我们的系统正常运行。我们需要在CPU上创建一个I/O控制器并将其连接到内存总线。此外,我们需要将系统中的一个特殊端口连接到 membus。该端口是仅功能端口,允许系统读取和写入内存。

将 PIO 和中断端口连接到内存总线是 x86 特定的要求。其他 ISA(例如 ARM)不需要这 3 条额外的线。

system.cpu.createInterruptController()
system.cpu.interrupts[0].pio = system.membus.mem_side_ports
system.cpu.interrupts[0].int_requestor = system.membus.cpu_side_ports
system.cpu.interrupts[0].int_responder = system.membus.mem_side_ports
system.system_port = system.membus.cpu_side_ports

接下来,我们需要创建一个内存控制器并将其连接到 membus。对于该系统,我们将使用一个简单的 DDR3 控制器,它将负责我们系统的整个内存范围。

system.mem_ctrl = MemCtrl()
system.mem_ctrl.dram = DDR3_1600_8x8()
system.mem_ctrl.dram.range = system.mem_ranges[0]
system.mem_ctrl.port = system.membus.mem_side_ports

这里可以看出,主要干的就是定义system里的东西,定义完的system长这样:
在这里插入图片描述

A2.3 配置进程process

A2.2 配置里我们想仿真的硬件system,这里我们告诉gem5,我们想在这个仿真的system上做什么:比如,我们写好了一个模拟器,下面要写这个模拟器上运行的代码里。

首先,我们必须创建进程(另一个 SimObject)。然后我们将进程命令设置为我们要运行的命令。这是一个类似于 argv 的列表,可执行文件位于第一个位置,可执行文件的参数位于列表的其余位置。然后我们设置CPU使用该进程作为它的工作负载,最后在CPU中创建功能执行上下文。

binary = 'tests/test-progs/hello/bin/x86/linux/hello'
system.workload = SEWorkload.init_compatible(binary)
process = Process()
process.cmd = [binary]
system.cpu.workload = process
system.cpu.createThreads()

这里的binary,是gem5自带的文件,路径如下。
在这里插入图片描述

我们需要做的最后一件事是实例化系统并开始执行。首先,我们创建Root对象。然后我们实例化模拟。实例化过程会遍历我们在 python 中创建的所有 SimObject,并创建等效C++项。
请注意,您不必实例化 python 类,然后将参数显式指定为成员变量。您还可以将参数作为命名参数传递,如Root下面的对象。

root = Root(full_system = False, system = system)
m5.instantiate()

最后,我们可以开始实际模拟了!另一方面,gem5 现在使用 Python 3 风格的print函数,因此print不再是语句,必须作为函数调用。

print("Beginning simulation!")
exit_event = m5.simulate()

一旦模拟完成,我们就可以检查系统的状态。

print('Exiting @ tick {} because {}'
      .format(m5.curTick(), exit_event.getCause()))

A2.4 跑实验并获得结果

在gem5的文件路径下,运行刚刚写的pytho文件或用gem5自带的文件:

 ./build/X86/gem5.opt configs/learning_gem5/part1/simple.py 

在这里插入图片描述

A3 添加缓存

在A2的基础上,添加了缓存,加完长这样:
在这里插入图片描述

A3.1 经典缓存和 Ruby

gem5 目前有两个完全不同的子系统来模拟系统中的片上缓存:“经典缓存”和“Ruby”。其历史原因是gem5是来自密歇根州的m5和来自威斯康星州的GEMS的组合。GEMS 使用 Ruby 作为其缓存模型,而经典缓存来自 m5 代码库(因此是“经典”)。这两个模型之间的区别在于,Ruby 旨在详细模拟缓存一致性。SLICC 是 Ruby 的一部分,它是一种用于定义缓存一致性协议的语言。另一方面,经典缓存实现了简化且不灵活的 MOESI 一致性协议。

要选择使用哪种模型,您应该问自己要建模的内容。如果您正在对缓存一致性协议的更改进行建模,或者一致性协议可能会对您的结果产生一阶影响,请使用 Ruby。否则,如果一致性协议对您来说不重要,请使用经典缓存。

gem5 的长期目标是将这两个缓存模型统一为一个整体模型。

简单粗暴的说,真正用gem5(我看到的近乎所有文章)的时候应该用ruby,经典缓存只是教程用了而已。

这里就不手打全部代码里,用gem5/configs/learning_gem5/part1/的caches.py 和gem5/configs/learning_gem5/part1/的two_level.py即可。
在这里插入图片描述
先引入一个gem5定义好的类,cache:

from m5.objects import Cache

Cache自带里两个子对象是cpu_side 和mem_side会在之后用到。
在这里插入图片描述
然后教程继承并且扩展里这个cache,叫做L1Cache。

class L1Cache(Cache):
    assoc = 2
    tag_latency = 2
    data_latency = 2
    response_latency = 2
    mshrs = 4
    tgts_per_mshr = 20

类似的创建一个l2的cache

class L2Cache(Cache):
    size = '256kB'
    assoc = 8
    tag_latency = 20
    data_latency = 20
    response_latency = 20
    mshrs = 20
    tgts_per_mshr = 12

A3.2 将缓存添加到简单的配置文件中

在two_lelvel.py里,先是引用同文件目录下的caches.py到命名空间里。
from caches import *

然后创建刚刚在chaces.py定义的教程用的l1cache

system.cpu.icache = L1ICache()
system.cpu.dcache = L1DCache()

这个icache就是我们的 L1ICache()指令cache,我们刚刚写了一个连接函数现在用上了:

system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)

我们无法直接将 L1 缓存连接到 L2 缓存,因为 L2 缓存只需要一个端口与其连接。因此,我们需要创建一个 L2 总线来将 L1 缓存连接到 L2 缓存。然后,我们可以使用辅助函数将 L1 缓存连接到 L2 总线。

system.l2bus = L2XBar()

system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)
#接下来,我们可以创建 L2 缓存并将其连接到 L2 总线和内存总线。
system.l2cache = L2Cache()
system.l2cache.connectCPUSideBus(system.l2bus)
system.membus = SystemXBar()
system.l2cache.connectMemSideBus(system.membus)

A3.3 运行结果

 ./build/X86/gem5.opt configs/learning_gem5/part1/two_level.py 

在这里插入图片描述
教程说是 57467000 个时钟周期内,而我实际花了58756000 cycle。

A3.4 将参数添加到脚本中

这里以l1 size为例子,教程讲述里如何添加命令行参数配置,而不是每次改python文件。

A3.4.1 Python 的 argparse

由于版本迭代的问题,有的老代码会用到pyoptparse(考虑到gem5曾经支持python 2.5) ,但是现在gem5基本用argparse了。

import argparse

parser = argparse.ArgumentParser(description='A simple system with 2-level cache.')
parser.add_argument("binary", default="", nargs="?", type=str,
                  help="Path to the binary to execute.")
parser.add_argument("--l1i_size",
                  help=f"L1 instruction cache size. Default: 16kB.")
parser.add_argument("--l1d_size",
                  help="L1 data cache size. Default: Default: 64kB.")
parser.add_argument("--l2_size",
                  help="L2 cache size. Default: 256kB.")

options = parser.parse_args()

请注意,如果您想按照上面所示的方式传递二进制文件的路径并通过选项使用它,您应该将其指定为options.binary. 例如:

system.workload = SEWorkload.init_compatible(options.binary)

然后改L1 cache,改为读取option里的值作为l1 cache size:

def __init__(self, options=None):
   super(L1ICache, self).__init__(options)
   if not options or not options.l1i_size:
       return
   self.size = options.l1i_size

本质上,是python 运行的时候,对__init__(self, options=None)这些函数来说,他们用的值都来自于option内。 而option内的值,是在命令行输入的值存进option的。

A4 了解 gem5 统计数据和输出

除了模拟脚本打印出的任何信息之外,运行 gem5 后,还会在名为 的目录中生成三个文件m5out:

配置文件
包含为模拟创建的每个 SimObject 及其参数值的列表。
配置.json
与 config.ini 相同,但采用 json 格式。
统计数据.txt
为模拟注册的所有 gem5 统计数据的文本表示。在这里插入图片描述

A4.1 配置文件 config.ini

config.ini文件是一个很有价值的工具,可确保您正在模拟您认为正在模拟的内容。在 gem5 中,有许多可能的方法来设置默认值和覆盖默认值。config.ini始终检查配置文件中设置的值是否传播到实际的 SimObject 实例化是一种“最佳实践” 。

A4.2 统计数据.txt

gem5有一个灵活的统计生成系统。gem5 wiki 站点上详细介绍了 gem5 统计信息。SimObject 的每个实例都有其自己的统计数据。在模拟结束时,或者当发出特殊的统计转储命令时,所有 SimObjects 的统计数据的当前状态将转储到文件中。
看看就好,暂时无用。

A5 使用默认配置脚本

gem5 附带了许多配置脚本,可以让您非常快速地使用 gem5。然而,一个常见的陷阱是在没有完全理解所模拟的内容的情况下使用这些脚本。在使用 gem5 进行计算机体系结构研究时,充分理解您正在模拟的系统非常重要。本章将引导您了解默认配置脚本的一些重要选项和部分。

默认的config的目录结构

默认的配置里有很多脚本文件,其目录结构如下

configs/boot:
bbench-gb.rcS  bbench-ics.rcS  hack_back_ckpt.rcS  halt.sh

configs/common:
Benchmarks.py   Caches.py  cpu2000.py    FileSystemConfig.py  GPUTLBConfig.py   HMC.py       MemConfig.py   Options.py     Simulation.py
CacheConfig.py  cores      CpuConfig.py  FSConfig.py          GPUTLBOptions.py  __init__.py  ObjectList.py  SimpleOpts.py  SysPaths.py

configs/dist:
sw.py

configs/dram:
lat_mem_rd.py  low_power_sweep.py  sweep.py

configs/example:
apu_se.py  etrace_replay.py  garnet_synth_traffic.py  hmctest.py    hsaTopology.py  memtest.py  read_config.py  ruby_direct_test.py      ruby_mem_test.py     sc_main.py
arm        fs.py             hmc_hello.py             hmc_tgen.cfg  memcheck.py     noc_config  riscv           ruby_gpu_random_test.py  ruby_random_test.py  se.py

configs/learning_gem5:
part1  part2  part3  README

configs/network:
__init__.py  Network.py

configs/nvm:
sweep_hybrid.py  sweep.py

configs/ruby:
AMD_Base_Constructor.py  CHI.py        Garnet_standalone.py  __init__.py              MESI_Three_Level.py  MI_example.py      MOESI_CMP_directory.py  MOESI_hammer.py
CHI_config.py            CntrlBase.py  GPU_VIPER.py          MESI_Three_Level_HTM.py  MESI_Two_Level.py    MOESI_AMD_Base.py  MOESI_CMP_token.py      Ruby.py

configs/splash2:
cluster.py  run.py

configs/topologies:
BaseTopology.py  Cluster.py  CrossbarGarnet.py  Crossbar.py  CustomMesh.py  __init__.py  MeshDirCorners_XY.py  Mesh_westfirst.py  Mesh_XY.py  Pt2Pt.py

se.py 和fs.py

这里可以看出gem5的纠结与矛盾之处,2023年的gem5已经把se.py 和fs.py 放入deprecated弃用文件夹里,但是2023年11月的教程里里依旧提到se.py 和fs.py很重要。
跑两个命令行

#1
build/X86/gem5.opt configs/example/se.py --cmd=tests/test-progs/hello/bin/x86/linux/hello

在这里插入图片描述

#2
build/X86/gem5.opt configs/example/se.py --cmd=tests/test-progs/hello/bin/x86/linux/hello --cpu-type=TimingSimpleCPU --l1d_size=64kB --l1i_size=16kB

在这里插入图片描述

#3
build/X86/gem5.opt configs/example/se.py --cmd=tests/test-progs/hello/bin/x86/linux/hello --cpu-type=TimingSimpleCPU --l1d_size=64kB --l1i_size=16kB --caches

在这里插入图片描述

教程是想说#1 的结果是atomic的没有真正的timing数据,而#2没有真正启用caches,#3加了–caches才启用了caches 然后让程序变快了

A6 为 ARM 扩展 gem5

其实我不是很喜欢ARM的gem5因为感觉arm版GEM5有很多额外的工作,而且之前尝试的时候也碰了不少壁。
不过这次还是继续按着教程来吧。

先是启用docker,把自己的文件路径放进docker里

sudo docker run -u $UID:$GID --volume  /home/yz/myprojects/2024GEM5/parsec-tests/yzmodifiedgem5:/gem5  --rm -it gcr.io/gem5-test/ubuntu-22.04_all-dependencies:v22-1                                                                  

在docke里,cd 进gem5,开始scos编译:

scons build/ARM/gem5.opt -j 16

编译的还挺快,10分钟以内吧。

A6.1 ARM SE 模式

然后更改一下simple。py或者直接用simple-arm.py,路径在gem5/configs/learning_gem5/part1/
在这里插入图片描述

# 官网教程里的是:build/ARM/gem5.opt configs/tutorial/simple.py,别用因为文件路径不对。用下面的
build/ARM/gem5.opt configs/learning_gem5/part1/simple-arm.py       

在这里插入图片描述

A6.2 ARM FS 模式

先开启docker,然后进入文件夹,准备编译:
进入 gem5/util/term
在这里插入图片描述
命令行输入make编译文件,可以看到多了一个文件叫m5term。

make

在这里插入图片描述
教程说要下载image然后export IMG_ROOT=/absolute/path/to/fs_images/
但不如直接用绝对路径代替下面的IMG_ROOT/

  ./build/ARM/gem5.opt configs/example/arm/fs_bigLITTLE.py \
    --caches \
    --bootloader="$IMG_ROOT/binaries/<bootloader-name>" \
    --kernel="$IMG_ROOT/binaries/<kernel-name>" \
    --disk="$IMG_ROOT/disks/<disk-image-name>" \
    --bootscript=path/to/bootscript.rcS

其中kernel是vmlinux或者vmlinux64,bootloader随便选一个,32位或64位对应就行。bootscript在 gem5/util/dist/test/里,叫simple_bootscript.rcS。
在这里插入图片描述

 ./build/ARM/gem5.opt configs/example/arm/fs_bigLITTLE.py \
   --caches \
   --bootloader="/home/yz/myprojects/2024GEM5/parsec-tests/yzmodifiedgem5/yzdownload/binaries/boot.arm64" \
   --kernel="/home/yz/myprojects/2024GEM5/parsec-tests/yzmodifiedgem5/yzdownload/binaries/vmlinux.arm64" \
   --disk="/home/yz/myprojects/2024GEM5/parsec-tests/yzmodifiedgem5/yzdownload/disks/m5_exit.squashfs.arm64" \
   --bootscript=/home/yz/myprojects/2024GEM5/parsec-tests/yzmodifiedgem5/util/dist/test/simple_bootscript.rcS

这个窗口会卡住
在这里插入图片描述

这时开一个命令行窗口iu,

 ./util/term/m5term 3456

在这里插入图片描述
会有一些输出,要多等一会,跑的比较慢。多等一会以后, ./util/term/m5term 3456的窗口会更新一些输出。
最后结果如下图,不会自动结束,看上去跑出来的结果有点不对,但是我们教程只管能跑,不管跑出来的结果。
在这里插入图片描述

总结

至此,教程的A部分入门完成了,下一步是B部分修改和扩展,再下一步是C部分ruby缓存一致性。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值