The System
Object
Congrats, now we can start writing real gem5 script stuff. Our first such line of code is
system = System()
Congrats, you just built a computer, go tell your mom (no really, you probably haven't called your mom in a bit, go do that now and come back in 5 minutes).
Why did I do that?
The system object is the most important part, you can find the source code detailing all the options for the system in $GEM_SRC/sim/system.py
. Think of this like your motherboard. On it, you will place your processor(s), your memory and any other system objects. You can then feed this system a workload (possibly multithreaded), and then observe the results through the statistics you collect, more about that later.
Adding Components
Now that we have a system, we can begin adding components. Consider first a clock domain:
system.clk_domain = SrcClockDomain()
system.clk_domain.clock = '1GHz'
system.clk_domain.voltage_domain = VoltageDomain()
Now that we have a reference clock for timing, we can enable timing mode for the memory system:
system.mem_mode = 'timing'
system.mem_ranges = [AddrRange('512MB')]
So now that we have a bit of memory, we can attach a processor:
system.cpu = RiscvTimingSimpleCPU()
The gem5 ecosystem revolves around the idea of SimObjects being simple m5 objects that we construct using python and C++ that can then be added to the system directly. They are also the reason I cannot provide a full listing of fields or methods for any classes in gem5, there are a lot of layers to this here program.
Connecting Memory
Now that we have all the components necessary to run a program, we can connect them all together. This is done through a system memory bus. A processor will also require an interrupt controller, which we will also attach here.
# System membus
system.membus = SystemXBar() # our crossbar
# Cache ports!
system.cpu.icache_port = system.membus.cpu_side_ports
system.cpu.dcache_port = system.membus.cpu_side_ports
# Setup an interrupt controller
system.cpu.createInterruptController()
system.system_port = system.membus.cpu_side_ports
Memory Control
Before we finally get to running a process with our fancy little processor, we need to add in some memory control to make sure that everything functions as expected.
# MEMORYYYYY
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
The Code so Far
#### IMPORTS ####
# import the m5 (gem5) library created when gem5 is built
import m5
import os
import sys
# import all of the SimObjects
from m5.objects import *
from m5.util import fatal
#### CONSTANTS ####
try:
os.environ['GEM_TESTS']
os.environ['GEM_PATH']
os.environ['GEM_CONFIGS']
except e:
fatal("This script requires the GEM_TESTS, GEM_PATH and GEM_CONFIGS environment variables")
# Add the common scripts to our path
# m5.util.addToPath(os.environ['GEM_CONFIGS'])
sys.path.append(os.environ['GEM_CONFIGS'])
# Creaing a simple system
system = System() # YAAAAAAAAAAAY
# We can now reference the system and change some parameters
system.clk_domain = SrcClockDomain()
system.clk_domain.clock = '1GHz'
system.clk_domain.voltage_domain = VoltageDomain()
# now we set the memory
system.mem_mode = 'timing'
system.mem_ranges = [AddrRange('512MB')]
# The type of CPU
system.cpu = RiscvTimingSimpleCPU()
# System membus
system.membus = SystemXBar()
# Cache ports!
system.cpu.icache_port = system.membus.cpu_side_ports
system.cpu.dcache_port = system.membus.cpu_side_ports
# Setup an interrupt controller
system.cpu.createInterruptController()
system.system_port = system.membus.cpu_side_ports
# NOTE: RISCV does not require the PIO setup they have in the tutorial
# MEMORYYYYY
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
This tutorial is heavily based on learning gem5 with some modifications for the University of Alberta CMPUT 429 course made by Ayrton Chilibeck in Summer 2024.