GEM5入门学习

1 GEM5简介

gem5是一个开源计算机架构模拟器,包括系统级架构以及处理器微架构。gem5的前身为密歇根大学的m5项目与威斯康星大学的GEMS项目。2011年m5与GEMS合并为gem5,目前被广泛用于学术界和工业界。通过谷歌学术可以看到,gem5目前被引用超过5000次,大量论文采用gem5作为研究工具。同时也被许多工业界公司使用,包括ARM、AMD、Google、Micron、Metempsy、HP、Samsung等。许多公司也积极为gem5添加了新功能或改进了现有功能。近年来,gem5社区仍在积极更新与开发,以支持未来15年的计算机架构研究。

2 编译

获取gem5源码:

git clone https://gem5.googlesource.com/public/gem5

编译:

scons build/X86/gem5.opt -j24

编译命令为 scons build//gem5.

其中CONFIG为要指定的处理器类型,可指定如下选项:

  • X86

  • ARM

  • RISCV

  • MIPS

  • POWER

  • SPARC

  • ALL 和 NULL

  • GCN3_X86 (GPU Model)

  • VEGA_X86 (GPU Model)

buildtype为编译二进制类型,可指定如下选项:

  • debug:不进行优化,并且生成调试符号(-g)

  • opt:使用编译优化,并且生成调试符号 (-O3 -g)

  • fast:使用所有的编译优化,且不生成调试符号(-O3)

构建完成之后的二进制文件在 /build/X86/gem5.opt

3 运行脚本

3.1 运行简单系统

GEM5 可以Python脚本来配置需要模拟的的系统。本节内容将构建如下的简单系统:仅包含CPU、bus和和mem ctrl。

GEM5入门学习_第1张图片

脚本如下:

# file: configs/tutorial/part1/simple.py

import m5
from m5.objects import *

# 创建系统,并进行初始化
# “system”对象是仿真系统中所有其他对象的父对象,其中包含了很多的功能信息,
# 如物理内存的范围、根时钟域、根电压域、full-system仿真下的kernel等
system = System()

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

# 设置内存仿真模式
system.mem_mode = 'timing'
system.mem_ranges = [AddrRange('512MB')]

# 创建CPU
system.cpu = X86TimingSimpleCPU()

# 创建系统总线
system.membus = SystemXBar()

# 不使用cache,直接将cpu的icache、dcache接口连接到membus上
#               +------------------+
#               |       cpu        |
#               +--+-----------+---+
#                  ^           ^
#  cpu.dcache_port |           | cpu.icache_port
#                  |           |
#                  V           V
#               +--+-----------+---+
#               |   membus         |
#               +------------------+
system.cpu.icache_port = system.membus.cpu_side_ports
system.cpu.dcache_port = system.membus.cpu_side_ports

# 创建一个IO Controller,并互联一些额外的线
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

# 创建内存控制器,memory controller
# 并连接memctrl到membus上
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

到此,系统已经构建完毕,接下来需要运行系统,在系统上运行一段简单的“Hello world”程序。

gem5支持两种运行模式,一种full system,一种syscall emulation:

  • Full System(FS Mode):仿真整个硬件系统并运行kernel。完整的Full System模式类似于运行虚拟机。

  • Syscall Emulation(SE Mode):并不仿真系统中的所有设备,而是侧重于仿真CPU和内存系统,因此构建系统时不需要创建所有的硬件设备,也只能模拟Linux的系统调用,也就是只模拟用户模式代码。

上面的Python文件已经创建了一个简单的系统,接下来需要继续在上面的Python文件中指定运行程序:

# 接着上面的python文件

# 指定需要运行的二进制程序,此处使用gem5中自带的"hello"程序
binary = 'tests/test-progs/hello/bin/x86/linux/hello'
system.workload = SEWorkload.init_compatible(binary)

# 创建process对象,并设置cpu的workload
process = Process()
process.cmd = [binary]
system.cpu.workload = process
system.cpu.createThreads()

# 最后,需要创建系统实例,并且运行仿真
root = Root(full_system = False, system = system)
m5.instantiate()

# 开始执行仿真
print("Beginning simulation!")
exit_event = m5.simulate()

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

到现在脚本已经完成了,接下来在终端上运行仿真脚本:

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

运行日志如下:

gem5 Simulator System.  https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 version 22.1.0.0
gem5 compiled Feb 27 2023 20:11:09
gem5 started Feb 28 2023 19:11:34
gem5 executing on ubuntu, pid 189423
command line: build/X86/gem5.opt configs/tutorial/part1/simple.py

Global frequency set at 1000000000000 ticks per second
build/X86/mem/dram_interface.cc:690: warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb: listening for remote gdb on port 7000
Beginning simulation!
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0.  Starting simulation...
Hello world!
Exiting @ tick 454646000 because exiting with last active thread context

此外,gem5可以模拟其他的CPU指令,并且创建CPU还可以指定其他的CPU类型如下:

ISAs

CPUType

Riscv

Arm

X86

Sparc

Power

Mips

AtomicSimpleCPU

O3CPU

TimingSimpleCPU

KvmCPU

minorCPU

3.2 在系统中添加Cache

上面章节中的例子模拟了一个不带cache的系统,此节的内容在上面Simple系统的基础之上添加L1Data Cache、L1Inst Cache以及Unified L2 Cache,如下图所示。

GEM5入门学习_第2张图片

GEM5下面可以模拟两种Cache类型“Classic caches”和“Ruby”。由于历史原因gem5是合并于m5项目和GEMS项目,继承自m5的cache称为classic cache,而GEMS项目使用“Ruby”的Cache模型。两个Cache模型的不同之处在于:

  • Ruby:Ruby被设计用来详细的对缓存一致性进行建模,其采用SLICC建模。SLICC是一种定义缓存一致性协议的专用语言。

  • Clasic:Calsic的高速缓存实现了一个简化的MOESI一致性协议。

为了构建Cache结构,需要在之前的脚本上进行修改,文件位置:configs/tutorial/part1/cache.py

# file: configs/tutorial/part1/cache.py

import m5
from m5.objects import *

# 首先创建cache的类来方便进行cache的设置
# Base L1Cache 继承于Cache
class L1Cache(Cache):
    assoc = 2
    tag_latency = 2
    data_latency = 2
    response_latency = 2
    mshrs = 2
    tgts_per_mshr = 20
    
    def connectCPU(self, cpu):
        # 将在子类中重载
        raise NotImplementedError
    def connectBus(self, bus):
        self.mem_side = bus.cpu_side_ports
    
class L1ICache(L1Cache):
    size = '16kB'
    def connectCPU(self, cpu):
        self.cpu_side = cpu.icache_port
    
class L1DCache(L1Cache):
    size = '64kB'
    def connectCPU(self, cpu):
        self.cpu_side = cpu.dcache_port
    
# Base L2Cache 继承于Cache
class L2Cache(Cache):
    size = '256kB'
    assoc = 8
    tag_latency = 20
    data_latency = 20
    response_latency = 20
    mshrs = 20
    tgts_per_mshr = 12    
    def connectCPUSideBus(self, bus):
        self.cpu_side = bus.mem_side_ports
    def connectMemSideBus(self, bus):
        self.mem_side = bus.cpu_side_ports

# 创建系统,并进行初始化
# “system”对象是仿真系统中所有其他对象的父对象,其中包含了很多的功能信息,
# 如物理内存的范围、根时钟域、根电压域、full-system仿真下的kernel等
system = System()

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

# 设置内存仿真模式
system.mem_mode = 'timing'
system.mem_ranges = [AddrRange('512MB')]

# 创建CPU
system.cpu = X86TimingSimpleCPU()

# 创建并连接CPU的icache和dcache
#                +----------------------+
#                |       cpu            |
#                +--+---------------+---+
#  cpu.dcache_port  ^               ^ cpu.icache_port
#                   |               |
# L1DCache.cpu_side V               V L1ICache.cpu_side
#             +-----+-----+   +-----+-----+
#             | L1DCache  |   | L1DCache  |
#             +-----------+   +-----------+
system.cpu.icache = L1ICache()
system.cpu.dcache = L1DCache()
system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)

# 创建并连接L2bus
#                +-----------+   +-----------+
#                | L1DCache  |   | L1ICache  |
#                +-----+-----+   +-----+-----+
#    L1DCache.mem_side ^               ^ L1ICache.mem_side
#                      |               |
# l2bus.cpu_side_ports V               V l2bus.cpu_side_ports
#                 +----+---------------+-----+
#                 |     L2 Bus               |
#                 +--------------------------+
system.l2bus = L2XBar()
system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)

# 创建并连接L2Cache
#  +--------------------+
#  |     L2Bus          |
#  +---------+----------+
#            ^ L2Bus.mem_side_ports
#            |
#            V L2Cache.cpu_side
#  +---------+----------+
#  |     L2Cache        |
#  +---------+----------+
#            ^ L2Cache.mem_side
#            |
#            V MemBus.cpu_side_ports
#  +---------+----------+
#  |     MemBus        |
#  +--------------------+
system.l2cache = L2Cache()
system.l2cache.connectCPUSideBus(system.l2bus)
system.membus = SystemXBar()
system.l2cache.connectMemSideBus(system.membus)

# 创建一个IO Controller,并互联一些额外的线
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

# 创建内存控制器,memory controller
# 并连接memctrl到membus上
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

# 指定需要运行的二进制程序,此处使用gem5中自带的"hello"程序
binary = 'tests/test-progs/hello/bin/x86/linux/hello'
system.workload = SEWorkload.init_compatible(binary)

# 创建process对象,并设置cpu的workload
process = Process()
process.cmd = [binary]
system.cpu.workload = process
system.cpu.createThreads()

# 最后,需要创建系统实例,并且运行仿真
root = Root(full_system = False, system = system)
m5.instantiate()

# 开始执行仿真
print("Beginning simulation!")
exit_event = m5.simulate()

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

通过如下命令运行:

build/X86/gem5.opt configs/tutorial/part1/cache.py

运行日志如下:

gem5 Simulator System.  https://www.gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 version 22.1.0.0
gem5 compiled Feb 27 2023 20:11:09
gem5 started Feb 28 2023 19:12:49
gem5 executing on ubuntu, pid 189659
command line: build/X86/gem5.opt configs/tutorial/part1/cache.py

Global frequency set at 1000000000000 ticks per second
build/X86/mem/dram_interface.cc:690: warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb: listening for remote gdb on port 7000
Beginning simulation!
build/X86/sim/simulate.cc:192: info: Entering event queue @ 0.  Starting simulation...
Hello world!
Exiting @ tick 56435000 because exiting with last active thread context

3.3 输出文件

运行完成之后会将系统运行的输出文件存放在当前路径下的m5out下:

[qihangkong@ubuntu gem5]$ ls m5out/
config.dot  config.dot.pdf  config.dot.svg  config.ini config.json stats.txt

总共会输出三类文件:

  • config.dot, config.dot.pdf, config.dot.svg:系统结构图

  • config.ini, config.json:详细的系统配置文件,将会显示所有系统中配置的模块

  • stats.txt:详细的运行统计数据,这个对我们进行性能分析很有帮助

4 使用默认的配置脚本

4.1 现有的脚本

GEM5中已经有了很多的已经构建好的系统脚本,可以直接使用,其存放位置就在 configs/ 目录下。

  • boot/:存放FS模式下的rcS启动脚本。

  • commom/:存放一些帮助脚本和函数脚本,如Caches.py。

  • dram/:存放测试DRAM的脚本

  • example/:存放一些例子脚本,如se.py、fs.py

  • learning_gem5/:存放learning gem5的例子

  • .....

4.2 使用se.py和fs.py

gem5提供了两个全面的脚本,se.py和fs.py用来模拟SE模式和FS模式。

如可以直接使用:

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

或者可以指定CPU类型和L1cache的大小:

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

可以直接查看帮助文档:

build/X86/gem5.opt configs/example/se.py --help

参考资料:

  • gem5 官网

  • Learning GEM5

  • [知乎] gem5模拟器入门(一)

  • [知乎] gem5模拟器入门(二)

  • [知乎] gem5模拟器入门(三)

  • [知乎] gem5模拟器入门(四)

你可能感兴趣的:(GEM5,linux)