目錄
一、Creating cache objects
1、Classic caches and Ruby
二、Cache
1、導(dǎo)入SimObject(s)
2、創(chuàng)建L1Cache
3、創(chuàng)建L1Cache子類
4、創(chuàng)建L2Cache
5、L1Cache添加連接函數(shù)
6、為L1ICache和L1DCache添加連接函數(shù)
7、為L2Cache添加內(nèi)存?zhèn)群虲PU側(cè)的連接函數(shù)
完整代碼
三、Adding caches to the simple config file
1、導(dǎo)入caches
2、創(chuàng)建L1Cache
3、連接緩存和CPU
4、創(chuàng)建L2XBar
完整代碼
四、Adding parameters to your script
測(cè)試
以上一教程的配置腳本作為起點(diǎn),本章將詳細(xì)介紹一個(gè)更復(fù)雜的配置。
將根據(jù)下圖所示添加一個(gè)緩存層次結(jié)構(gòu)到系統(tǒng)中。
此外,本教程還將介紹如何理解gem5的統(tǒng)計(jì)輸出,并向你的腳本中添加命令行參數(shù)。
一、Creating cache objects
因?yàn)槲覀冋诮R粋€(gè)單核心CPU系統(tǒng),,不關(guān)心建模緩存一致性。所以這個(gè)教程中,將使用經(jīng)典緩存(Classic caches)而不是Ruby入門章節(jié)(ruby-intro-chapter)。教程將擴(kuò)展Cache SimObject并對(duì)其進(jìn)行系統(tǒng)配置。首先,需要了解用于配置Cache對(duì)象的參數(shù)。
1、Classic caches and Ruby
gem5目前有兩個(gè)完全不同的子系統(tǒng)用于模擬系統(tǒng)中的片上緩存,即“經(jīng)典緩存”和“Ruby”。這是由于gem5是密歇根大學(xué)的m5和威斯康辛大學(xué)的GEMS的結(jié)合體。GEMS使用Ruby作為其緩存模型,而經(jīng)典緩存來自m5代碼庫(因此稱為“經(jīng)典”)。這兩種模型的區(qū)別在于Ruby被設(shè)計(jì)用于詳細(xì)模擬緩存一致性。Ruby的一部分是SLICC,一種用于定義緩存一致性協(xié)議的語言。而經(jīng)典緩存則實(shí)現(xiàn)了簡(jiǎn)化且不靈活的MOESI一致性協(xié)議。
在選擇使用哪個(gè)模型時(shí),應(yīng)考慮正在建模的內(nèi)容側(cè)重點(diǎn)。如果正在建模緩存一致性協(xié)議的更改,或者一致性協(xié)議對(duì)結(jié)果有重要影響,使用Ruby。如果一致性協(xié)議不重要,使用經(jīng)典緩存。
gem5的長期目標(biāo)是將這兩種緩存模型統(tǒng)一為一個(gè)綜合模型。
二、Cache
Cache SimObject的聲明可以在src/mem/cache/Cache.py中找到。這個(gè)Python文件定義了可以設(shè)置SimObject的參數(shù)。在內(nèi)部,當(dāng)實(shí)例化SimObject時(shí),這些參數(shù)會(huì)傳遞給該對(duì)象的C++實(shí)現(xiàn)。Cache SimObject繼承自下面顯示的BaseCache對(duì)象。
在BaseCache類中,有許多參數(shù)。例如,assoc是一個(gè)整數(shù)參數(shù)。一些參數(shù),比如write_buffers,在這種情況下有一個(gè)默認(rèn)值,為8。默認(rèn)參數(shù)是Param.*的第一個(gè)參數(shù),除非第一個(gè)參數(shù)是一個(gè)字符串。每個(gè)參數(shù)的字符串參數(shù)是該參數(shù)的描述(例如,tag_latency = Param.Cycles("Tag lookup latency")表示tag_latency控制著“此緩存的命中延遲”)。
許多這些參數(shù)沒有默認(rèn)值,所以在調(diào)用m5.instantiate()之前需要設(shè)置這些參數(shù)。
為了使用特定的參數(shù)創(chuàng)建緩存,首先要在與configs/tutorial/part1/simple.py相同目錄中創(chuàng)建一個(gè)名為caches.py的新文件。
1、導(dǎo)入SimObject(s)
第一步是導(dǎo)入將在此文件中擴(kuò)展的SimObject(s)。
from m5.objects import Cache
2、創(chuàng)建L1Cache
接下來,可以像處理其他Python類一樣處理BaseCache對(duì)象并進(jìn)行擴(kuò)展??梢愿鶕?jù)需要給新的緩存取任意名稱,創(chuàng)建一個(gè)L1緩存。
class L1Cache(Cache):
assoc = 2
tag_latency = 2
data_latency = 2
response_latency = 2
mshrs = 4
tgts_per_mshr = 20
在這里,擴(kuò)展了BaseCache并設(shè)置了大多數(shù)在BaseCache SimObject中沒有默認(rèn)值的參數(shù)。BaseCache的部分參數(shù)沒有默認(rèn)值,就需要設(shè)置。如果要查看所有可能的配置選項(xiàng),并找出哪些是必需的,哪些是可選的,必須查看SimObject的源代碼。
3、創(chuàng)建L1Cache子類
接下來,創(chuàng)建兩個(gè)L1Cache的子類,一個(gè)是L1DCache,一個(gè)是L1ICache。
class L1ICache(L1Cache):
size = '16kB'
class L1DCache(L1Cache):
size = '64kB'
4、創(chuàng)建L2Cache
創(chuàng)建一個(gè)帶有部分參數(shù)的L2緩存
class L2Cache(Cache):
size = '256kB'
assoc = 8
tag_latency = 20
data_latency = 20
response_latency = 20
mshrs = 20
tgts_per_mshr = 12
現(xiàn)在,已經(jīng)指定了BaseCache所需的所有必要參數(shù),只需要實(shí)例化子類并將緩存連接到互連網(wǎng)絡(luò)。但如果將大量對(duì)象連接到復(fù)雜的互連網(wǎng)絡(luò),可能會(huì)導(dǎo)致配置腳本文件的規(guī)模增大,同時(shí)腳本配置文件的可讀性降低。因此,首先在Cache的子類中添加一些輔助函數(shù)(上述的類都是python類)。
5、L1Cache添加連接函數(shù)
對(duì)于L1緩存,添加兩個(gè)函數(shù):connectCPU將CPU連接到緩存,connectBus將緩存連接到總線。我們需要將以下代碼添加到L1Cache類中。
def connectCPU(self, cpu):
# need to define this in a base class!
raise NotImplementedError
def connectBus(self, bus):
self.mem_side = bus.cpu_side_ports
6、為L1ICache和L1DCache添加連接函數(shù)
接下來,需要為指令緩存和數(shù)據(jù)緩存分別定義單獨(dú)的connectCPU函數(shù),因?yàn)镮-cache和D-cache端口有不同的名稱。此時(shí)的L1ICache和L1DCache類變成如下:
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
7、為L2Cache添加內(nèi)存?zhèn)群虲PU側(cè)的連接函數(shù)
分別為L2Cache添加連接到內(nèi)存?zhèn)群虲PU側(cè)總線的函數(shù)。
def connectCPUSideBus(self, bus):
self.cpu_side = bus.mem_side_ports
def connectMemSideBus(self, bus):
self.mem_side = bus.cpu_side_ports
完整代碼
?
import m5
from m5.objects import Cache
# Add the common scripts to our path
m5.util.addToPath("../../")
from common import SimpleOpts
# Some specific options for caches
# For all options see src/mem/cache/BaseCache.py
class L1Cache(Cache):
"""Simple L1 Cache with default values"""
assoc = 2
tag_latency = 2
data_latency = 2
response_latency = 2
mshrs = 4
tgts_per_mshr = 20
def __init__(self, options=None):
super(L1Cache, self).__init__()
pass
def connectBus(self, bus):
"""Connect this cache to a memory-side bus"""
self.mem_side = bus.cpu_side_ports
def connectCPU(self, cpu):
"""Connect this cache's port to a CPU-side port
This must be defined in a subclass"""
raise NotImplementedError
class L1ICache(L1Cache):
"""Simple L1 instruction cache with default values"""
# Set the default size
size = "16kB"
SimpleOpts.add_option(
"--l1i_size", help=f"L1 instruction cache size. Default: {size}"
)
def __init__(self, opts=None):
super(L1ICache, self).__init__(opts)
if not opts or not opts.l1i_size:
return
self.size = opts.l1i_size
def connectCPU(self, cpu):
"""Connect this cache's port to a CPU icache port"""
self.cpu_side = cpu.icache_port
class L1DCache(L1Cache):
"""Simple L1 data cache with default values"""
# Set the default size
size = "64kB"
SimpleOpts.add_option(
"--l1d_size", help=f"L1 data cache size. Default: {size}"
)
def __init__(self, opts=None):
super(L1DCache, self).__init__(opts)
if not opts or not opts.l1d_size:
return
self.size = opts.l1d_size
def connectCPU(self, cpu):
"""Connect this cache's port to a CPU dcache port"""
self.cpu_side = cpu.dcache_port
class L2Cache(Cache):
"""Simple L2 Cache with default values"""
# Default parameters
size = "256kB"
assoc = 8
tag_latency = 20
data_latency = 20
response_latency = 20
mshrs = 20
tgts_per_mshr = 12
SimpleOpts.add_option("--l2_size", help=f"L2 cache size. Default: {size}")
def __init__(self, opts=None):
super(L2Cache, self).__init__()
if not opts or not opts.l2_size:
return
self.size = opts.l2_size
def connectCPUSideBus(self, bus):
self.cpu_side = bus.mem_side_ports
def connectMemSideBus(self, bus):
self.mem_side = bus.cpu_side_ports
?
三、Adding caches to the simple config file
將上一教程的最終完整代碼文件復(fù)制在同目錄,并命名為two_level.py。
cp ./configs/tutorial/part1/simple.py ./configs/tutorial/part1/two_level.py
1、導(dǎo)入caches
首先,在文件的頂部(m5.objects導(dǎo)入之后)添加以下內(nèi)容,將caches.py文件中的名稱導(dǎo)入到命名空間中。
from caches import *
2、創(chuàng)建L1Cache
在創(chuàng)建CPU之后,創(chuàng)建L1緩存:
system.cpu.icache = L1ICache()
system.cpu.dcache = L1DCache()
3、連接緩存和CPU
使用上述創(chuàng)建的連接函數(shù)將緩存連接到CPU上:
system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)
刪除原文件中用于直接將緩存端口連接到內(nèi)存總線上的兩行。
system.cpu.icache_port = system.membus.cpu_side_ports
system.cpu.dcache_port = system.membus.cpu_side_ports
4、創(chuàng)建L2XBar
L2緩存只允許一個(gè)端口與其連接,所以不能直接將L1緩存連接到L2緩存。因此,需要?jiǎng)?chuàng)建一個(gè)L2總線,將L1緩存間接連接到L2緩存。然后,可以使用輔助函數(shù)將L1緩存連接到L2總線。
system.l2bus = L2XBar()
system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)
需要注意的是,在system.l2cache.connectMemSideBus之前,已經(jīng)定義了system.membus = SystemXBar(),因此可以將其傳遞給system.l2cache.connectMemSideBus。文件中的其他部分保持不變。
現(xiàn)在有了一個(gè)完整的配置,包含了一個(gè)兩級(jí)緩存層次結(jié)構(gòu)。如果運(yùn)行當(dāng)前文件,hello程序應(yīng)該在57467000個(gè)時(shí)鐘周期內(nèi)完成。完整的腳本保存在gem5源代碼的configs/learning_gem5/part1/two_level.py中。
完整代碼
# import the m5 (gem5) library created when gem5 is built
import m5
# import all of the SimObjects
from m5.objects import *
from gem5.runtime import get_runtime_isa
# Add the common scripts to our path
m5.util.addToPath("../../")
# import the caches which we made
from caches import *
# import the SimpleOpts module
from common import SimpleOpts
# 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(
thispath,
"../../../",
"tests/test-progs/hello/bin/x86/linux/hello",
)
# Binary to execute
SimpleOpts.add_option("binary", nargs="?", default=default_binary)
# Finalize the arguments and grab the args so we can pass it on to our objects
args = SimpleOpts.parse_args()
# create the system we are going to simulate
system = System()
# Set the clock frequency of the system (and all of its children)
system.clk_domain = SrcClockDomain()
system.clk_domain.clock = "1GHz"
system.clk_domain.voltage_domain = VoltageDomain()
# Set up the system
system.mem_mode = "timing" # Use timing accesses
system.mem_ranges = [AddrRange("512MB")] # Create an address range
# Create a simple CPU
system.cpu = X86TimingSimpleCPU()
# Create an L1 instruction and data cache
system.cpu.icache = L1ICache(args)
system.cpu.dcache = L1DCache(args)
# Connect the instruction and data caches to the CPU
system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)
# Create a memory bus, a coherent crossbar, in this case
system.l2bus = L2XBar()
# Hook the CPU ports up to the l2bus
system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)
# Create an L2 cache and connect it to the l2bus
system.l2cache = L2Cache(args)
system.l2cache.connectCPUSideBus(system.l2bus)
# Create a memory bus
system.membus = SystemXBar()
# Connect the L2 cache to the membus
system.l2cache.connectMemSideBus(system.membus)
# create the interrupt controller for the CPU
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
# Connect the system up to the membus
system.system_port = system.membus.cpu_side_ports
# Create a DDR3 memory controller
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
system.workload = SEWorkload.init_compatible(args.binary)
# Create a process for a simple "Hello World" application
process = Process()
# Set the command
# cmd is a list which begins with the executable (like argv)
process.cmd = [args.binary]
# Set the cpu to use the process as its workload and create thread contexts
system.cpu.workload = process
system.cpu.createThreads()
# set up the root SimObject and start the simulation
root = Root(full_system=False, system=system)
# instantiate all of the objects we've created above
m5.instantiate()
print("Beginning simulation!")
exit_event = m5.simulate()
print("Exiting @ tick %i because %s" % (m5.curTick(), exit_event.getCause()))
測(cè)試結(jié)果
四、Adding parameters to your script
在使用gem5進(jìn)行實(shí)驗(yàn)時(shí),為了避免每次想要使用不同的參數(shù)測(cè)試系統(tǒng)時(shí)都需要編輯配置腳本??梢栽趃em5配置腳本中添加命令行參數(shù)。
注意:由于配置腳本是Python代碼,可以使用支持參數(shù)解析的Python庫。盡管pyoptparse官方上已經(jīng)被棄用,但因?yàn)間em5的最低Python版本曾經(jīng)是2.5,gem5附帶的許多配置腳本仍然使用它,而不是新版的pyargparse。現(xiàn)在gem5的最低Python版本是3.6,因此在編寫不需要與當(dāng)前gem5腳本交互的新腳本時(shí),可以選擇Python的pyargparse。
-
Pyargparse是Python標(biāo)準(zhǔn)庫中的argparse模塊,而pyoptparse是gem5自己實(shí)現(xiàn)的一個(gè)選項(xiàng)解析庫。Pyargparse提供了更豐富的功能和更好的用戶體驗(yàn),并在Python社區(qū)中廣泛使用。
-
Pyargparse在Python 2.7版本之后成為標(biāo)準(zhǔn)庫的一部分,而pyoptparse是為了與較舊版本的Python兼容而創(chuàng)建的。由于gem5的最低Python版本現(xiàn)在是3.6,因此pyargparse是更好的選擇,特別是對(duì)于編寫新的腳本。
-
Pyargparse提供了更簡(jiǎn)潔、更直觀的API,并且支持更豐富的參數(shù)類型和選項(xiàng)配置。它具有更好的錯(cuò)誤處理和幫助信息生成。
為了向我們的兩級(jí)緩存配置添加選項(xiàng),在導(dǎo)入緩存之后,讓我們添加一些選項(xiàng)。
import argparse
parser = argparse.ArgumentParser(description='A simple system with 2-level cache.')
parser.add_argument("binary", default="", nargs="?", type=str,
help="Path to the binary to execute.")
parser.add_argument("--l1i_size",
help=f"L1 instruction cache size. Default: 16kB.")
parser.add_argument("--l1d_size",
help="L1 data cache size. Default: Default: 64kB.")
parser.add_argument("--l2_size",
help="L2 cache size. Default: 256kB.")
options = parser.parse_args()
如果想像上面所示的方式傳遞二進(jìn)制文件的路徑,并通過選項(xiàng)(options)使用它,可以將其指定為options.binary。
system.workload = SEWorkload.init_compatible(options.binary)
使用build/X86/gem5.debug configs/learning_gem5/part1/two_level.py --help可以顯示剛剛添加的選項(xiàng)。
接下來,需要將這些選項(xiàng)傳遞給配置腳本中創(chuàng)建的緩存。為了實(shí)現(xiàn)這一點(diǎn),我們將簡(jiǎn)單地修改two_level_opts.py
腳本,將選項(xiàng)作為參數(shù)傳遞給緩存的構(gòu)造函數(shù),并添加一個(gè)合適的構(gòu)造函數(shù)。
system.cpu.icache = L1ICache(options)
system.cpu.dcache = L1DCache(options)
...
system.l2cache = L2Cache(options)
在caches.py文件中,需要為每個(gè)類添加構(gòu)造函數(shù)(Python中的__init__函數(shù))。從基本L1緩存開始,因?yàn)闆]有任何適用于基本L1緩存的參數(shù),所以只需添加一個(gè)空的構(gòu)造函數(shù)。但在這種情況下,不能忘記調(diào)用父類的構(gòu)造函數(shù)。如果省略對(duì)父類構(gòu)造函數(shù)的調(diào)用,gem5的SimObject屬性查找函數(shù)將失敗,并且在嘗試實(shí)例化緩存對(duì)象時(shí)會(huì)出現(xiàn)"RuntimeError: maximum recursion depth exceeded"的錯(cuò)誤。因此,在L1Cache類中,需要在靜態(tài)類成員之后添加以下內(nèi)容。
def __init__(self, options=None):
super(L1Cache, self).__init__()
pass
接下來,在L1ICache中,需要使用創(chuàng)建的選項(xiàng)(l1i_size)來設(shè)置大小。在下面的代碼中,對(duì)于沒有將選項(xiàng)傳遞給L1ICache構(gòu)造函數(shù)和在命令行上沒有指定選項(xiàng)的情況,有相應(yīng)的保護(hù)代碼。在這些情況下,將使用我們指定的大小默認(rèn)值。
def __init__(self, options=None):
super(L1ICache, self).__init__(options)
if not options or not options.l1i_size:
return
self.size = options.l1i_size
可以在L1DCache使用相同的代碼:
def __init__(self, options=None):
super(L1DCache, self).__init__(options)
if not options or not options.l1d_size:
return
self.size = options.l1d_size
在L2Cache使用相同的代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-822805.html
def __init__(self, options=None):
super(L2Cache, self).__init__()
if not options or not options.l2_size:
return
self.size = options.l2_size
測(cè)試
build/X86/gem5.debug configs/learning_gem5/part1/two_level.py --l2_size='2MB' --l1d_size='128kB'
文章來源地址http://www.zghlxwxcb.cn/news/detail-822805.html
到了這里,關(guān)于gem5學(xué)習(xí)(11):將緩存添加到配置腳本中——Adding cache to the configuration script的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!