gem5是一个开源计算机架构模拟器,包括系统级架构以及处理器微架构。gem5的前身为密歇根大学的m5项目与威斯康星大学的GEMS项目。2011年m5与GEMS合并为gem5,目前被广泛用于学术界和工业界。通过谷歌学术可以看到,gem5目前被引用超过5000次,大量论文采用gem5作为研究工具。同时也被许多工业界公司使用,包括ARM、AMD、Google、Micron、Metempsy、HP、Samsung等。许多公司也积极为gem5添加了新功能或改进了现有功能。近年来,gem5社区仍在积极更新与开发,以支持未来15年的计算机架构研究。
获取gem5源码:
git clone https://gem5.googlesource.com/public/gem5
编译:
scons build/X86/gem5.opt -j24
编译命令为 scons build/
其中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
GEM5 可以Python脚本来配置需要模拟的的系统。本节内容将构建如下的简单系统:仅包含CPU、bus和和mem ctrl。
脚本如下:
# 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 |
上面章节中的例子模拟了一个不带cache的系统,此节的内容在上面Simple系统的基础之上添加L1Data Cache、L1Inst Cache以及Unified L2 Cache,如下图所示。
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
运行完成之后会将系统运行的输出文件存放在当前路径下的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:详细的运行统计数据,这个对我们进行性能分析很有帮助
GEM5中已经有了很多的已经构建好的系统脚本,可以直接使用,其存放位置就在 configs/ 目录下。
boot/:存放FS模式下的rcS启动脚本。
commom/:存放一些帮助脚本和函数脚本,如Caches.py。
dram/:存放测试DRAM的脚本
example/:存放一些例子脚本,如se.py、fs.py
learning_gem5/:存放learning gem5的例子
.....
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模拟器入门(四)