1 Introdução ao GEM5
gem5 é um simulador de arquitetura de computador de código aberto, incluindo arquitetura em nível de sistema e microarquitetura de processador. O predecessor do gem5 é o projeto m5 da Universidade de Michigan e o projeto GEMS da Universidade de Wisconsin. Em 2011, o m5 se fundiu com o GEMS para formar o gem5, que agora é amplamente utilizado na academia e na indústria. De acordo com o Google Scholar, o gem5 foi citado mais de 5.000 vezes, e um grande número de artigos usa o gem5 como ferramenta de pesquisa. Também é usado por muitas empresas industriais, incluindo ARM, AMD, Google, Micron, Metempsy, HP, Samsung, etc. Muitas empresas também adicionaram ativamente novos recursos ou melhoraram os existentes ao gem5. Nos últimos anos, a comunidade gem5 ainda está atualizando e desenvolvendo ativamente para apoiar a pesquisa de arquitetura de computadores pelos próximos 15 anos.
2 compilar
Obtenha o código-fonte do gem5:
git clone https://gem5.googlesource.com/public/gem5
Compilar:
scons build/X86/gem5.opt -j24
O comando de compilação é scons build/<CONFIG>/gem5.<buildtype>
Dentre eles, CONFIG é o tipo de processador a ser especificado, podendo ser especificadas as seguintes opções:
X86
BRAÇO
RISCV
MIPS
PODER
SPARC
ALL e NULL
GCN3_X86 (Modelo de GPU)
VEGA_X86 (Modelo de GPU)
buildtype é o tipo binário compilado e as seguintes opções podem ser especificadas:
debug: não otimiza e gera símbolos de depuração (-g)
opt: usa otimização de compilação e gera símbolos de depuração (-O3 -g)
rápido: Use todas as otimizações de compilação sem gerar símbolos de depuração (-O3)
O arquivo binário após a compilação é /build/X86/gem5.opt
3 Execute o script
3.1 Executando um sistema simples
GEM5 pode usar scripts Python para configurar o sistema a ser simulado. Esta seção construirá um sistema simples da seguinte forma: apenas CPU, barramento e controle de memória estão incluídos.
O roteiro é o seguinte:
# 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
Neste ponto, o sistema foi construído e a próxima etapa é executar o sistema e executar um programa simples "Hello world" no sistema.
gem5 suporta dois modos de operação, um sistema completo e uma emulação syscall:
Sistema Completo (Modo FS): Simule todo o sistema de hardware e execute o kernel. O modo Full System completo é semelhante à execução de uma máquina virtual.
Emulação Syscall (Modo SE): Não emula todos os dispositivos do sistema, mas foca em emular a CPU e a memória do sistema, portanto não é necessário criar todos os dispositivos de hardware na hora de montar o sistema, podendo apenas emular Linux chamadas do sistema, ou seja, apenas simular o código do modo de usuário.
O arquivo Python acima criou um sistema simples e, em seguida, você precisa continuar a especificar o programa em execução no arquivo Python acima:
# 接着上面的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()))
Agora que o script está completo, execute o script de simulação no terminal:
build/X86/gem5.opt configs/tutorial/part1/simple.py
O log de execução é o seguinte:
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
Além disso, gem5 pode simular outras instruções de CPU, e criar uma CPU também pode especificar outros tipos de CPU da seguinte forma:
ISAs |
Tipo de CPU |
Risco Braço X86 Sparc Poder Mips |
AtomicSimpleCPU O3CPU TimingSimpleCPU KvmCPU menorCPU |
3.2 Adicionar Cache ao sistema
O exemplo no capítulo acima simula um sistema sem cache. O conteúdo desta seção adiciona L1Data Cache, L1Inst Cache e Unified L2 Cache com base no sistema Simples acima, conforme mostrado na figura abaixo.
O GEM5 pode simular dois tipos de cache "Caches clássicos" e "Ruby". Por razões históricas, o gem5 é mesclado com o projeto m5 e o projeto GEMS.O cache herdado do m5 é chamado de cache clássico, e o projeto GEMS usa o modelo de cache "Ruby". As diferenças entre os dois modelos de Cache são:
Ruby: Ruby é projetado para modelar a coerência do cache em detalhes, usando a modelagem SLICC. SLICC é uma linguagem de propósito especial para definir protocolos de coerência de cache.
Clasic: o cache do Calsic implementa um protocolo de coerência MOESI simplificado.
Para construir a estrutura do Cache, você precisa modificar o script anterior, local do arquivo: 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()))
Execute com o seguinte comando:
build/X86/gem5.opt configs/tutorial/part1/cache.py
O log de execução é o seguinte:
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 Arquivos de saída
Após a conclusão da operação, o arquivo de saída da operação do sistema será armazenado em m5out no caminho atual:
[qihangkong@ubuntu gem5]$ ls m5out/
config.dot config.dot.pdf config.dot.svg config.ini config.json stats.txt
Um total de três tipos de arquivos são produzidos:
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