Modifying the Processor
This is the easy part, we simply connect everything up all nice and proper.
Setup
Now we can simply import our other file and use our Cache objects in the new processor. Modifying our imports from the first tutorial, we get:
import m5
import os
import sys
# import all of the SimObjects
from m5.objects import *
from m5.util import fatal
from caches import *
Now we can use this to create some cache objects:
system.cpu = RiscvTimingSimpleCPU()
system.cpu.icache = L1ICache()
system.cpu.dcache = L1DCache()
system.l2cache = L2Cache()
Note that the L2 Cache is not necessarily a part of the CPU die, you can go down quite a rabbit hole as to the reason for this. Often in modern processors, the cache is within the core boundary. There are an increasing number of processors that place an additional cache outside of the chip boundary, as you can see in the image below.
You may want to consult some example specifications. On the AMD EPYC and Intel Saphire Rapids, L3 cache will be arranged similarly to the above diagram. Interestingly, Intel experimented with something resembling an L4 cache in their Skylake, Haswell and Broadwell architectures called eDRAM Based Cache intended to give their integrated GPU's a leg up for memory access. Intel has also filed a patent application for L4 cache with the US patent office.
Connecting to the CPU
For this step, we can leverage the helper functions we made in the cache class:
system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)
Next, we need to connect the L1 caches to the L2 cache. To do this, we can create an L2 crossbar between the L1 caches and the L2 cache.
system.l2bus = L2XBar()
system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)
Finally, we can connect the L2 cache to main memory using a system crossbar.
system.l2cache.connectCPUSideBus(system.l2bus)
system.membus = SystemXBar()
system.l2cache.connectMemSideBus(system.membus)
And now you have cache.
The Code
import m5
import os
import sys
# import all of the SimObjects
from m5.objects import *
from m5.util import fatal
from caches import *
#### 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'])
# Default to running 'hello', use the compiled ISA to find the binary
# grab the specific path to the binary
thispath = os.path.dirname(os.path.realpath(__file__))
default_binary = os.path.join(
os.environ['GEM_TESTS'],
"test-progs/hello/bin/riscv/linux/hello",
)
# 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()
# NOTE: add the caches
system.cpu.icache = L1ICache()
system.cpu.dcache = L1DCache()
system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)
# NOTE: we remove these
# # Cache ports!
# system.cpu.icache_port = system.membus.cpu_side_ports
# system.cpu.dcache_port = system.membus.cpu_side_ports
system.l2bus = L2XBar() # note that this is built in, but you
# can try to make your own too!
# Connect all of the caches to the l2
system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)
# Make the L2
system.l2cache = L2Cache()
system.l2cache.connectCPUSideBus(system.l2bus)
# The system bus to main memory
system.membus = SystemXBar()
# Connect the L2 to the system bus
system.l2cache.connectMemSideBus(system.membus)
# 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
# Set up our binary
binary = default_binary
# for gem5 V21 and beyond
system.workload = SEWorkload.init_compatible(binary)
process = Process()
process.cmd = [binary]
system.cpu.workload = process
system.cpu.createThreads()
# Instantiate the system with gem5
root = Root(full_system = False, system = system)
m5.instantiate()
print("Beginning simulation!")
exit_event = m5.simulate()
# And once simulation finishes, we can inspect the state of the system.
print('Exiting @ tick {} because {}'
.format(m5.curTick(), exit_event.getCause()))