GEM5教程--gem5开始之旅(二)

三、在配置脚本中添加缓存

这一部分要模拟的系统的结构图如下:
系统结构图

1、缓存对象

Cache SimObjection 的声明源代码可以在src/mem/cache/Cache.py
Cache SimObject继承自下面显示的BaseCache对象:

from m5.params import *
from m5.proxy import *
from MemObject import MemObject
from Prefetcher import BasePrefetcher
from ReplacementPolicies import *
from Tags import *

class BaseCache(MemObject):
    type = 'BaseCache'
    abstract = True
    cxx_header = "mem/cache/base.hh"

    size = Param.MemorySize("Capacity")
    assoc = Param.Unsigned("Associativity")

    tag_latency = Param.Cycles("Tag lookup latency")
    data_latency = Param.Cycles("Data access latency")
    response_latency = Param.Cycles("Latency for the return path on a miss");

    warmup_percentage = Param.Percent(0,
        "Percentage of tags to be touched to warm up the cache")

    max_miss_count = Param.Counter(0,
        "Number of misses to handle before calling exit")

    mshrs = Param.Unsigned("Number of MSHRs (max outstanding requests)")
    demand_mshr_reserve = Param.Unsigned(1, "MSHRs reserved for demand access")
    tgts_per_mshr = Param.Unsigned("Max number of accesses per MSHR")
    write_buffers = Param.Unsigned(8, "Number of write buffers")

    is_read_only = Param.Bool(False, "Is this cache read only (e.g. inst)")

    prefetcher = Param.BasePrefetcher(NULL,"Prefetcher attached to cache")
    prefetch_on_access = Param.Bool(False,
         "Notify the hardware prefetcher on every access (not just misses)")

    tags = Param.BaseTags(BaseSetAssoc(), "Tag store")
    replacement_policy = Param.BaseReplacementPolicy(LRURP(),
        "Replacement policy")

    sequential_access = Param.Bool(False,
        "Whether to access tags and data sequentially")

    cpu_side = SlavePort("Upstream port closer to the CPU and/or device")
    mem_side = MasterPort("Downstream port closer to memory")

    addr_ranges = VectorParam.AddrRange([AllMemory],
         "Address range for the CPU-side port (to allow striping)")

    system = Param.System(Parent.any, "System we belong to")

# Enum for cache clusivity, currently mostly inclusive or mostly
# exclusive.
class Clusivity(Enum): vals = ['mostly_incl', 'mostly_excl']

class Cache(BaseCache):
    type = 'Cache'
    cxx_header = 'mem/cache/cache.hh'

    # Control whether this cache should be mostly inclusive or mostly
    # exclusive with respect to upstream caches. The behaviour on a
    # fill is determined accordingly. For a mostly inclusive cache,
    # blocks are allocated on all fill operations. Thus, L1 caches
    # should be set as mostly inclusive even if they have no upstream
    # caches. In the case of a mostly exclusive cache, fills are not
    # allocating unless they came directly from a non-caching source,
    # e.g. a table walker. Additionally, on a hit from an upstream
    # cache a line is dropped for a mostly exclusive cache.
    clusivity = Param.Clusivity('mostly_incl',
                                "Clusivity with upstream cache")

    # Determine if this cache sends out writebacks for clean lines, or
    # simply clean evicts. In cases where a downstream cache is mostly
    # exclusive with respect to this cache (acting as a victim cache),
    # the clean writebacks are essential for performance. In general
    # this should be set to True for anything but the last-level
    # cache.
    writeback_clean = Param.Bool(False, "Writeback clean lines")

(1)创建具有特定参数的缓存:我们首先要在simple.py的configs/tutorial目录中创建一个新文件caches.py。第一步是导入要在此文件中扩展的SimObject。

from m5.objects import Cache

(2)接下来,我们可以像对待任何其他Python类一样对待BaseCache对象并对其进行扩展。我们可以随意命名新的缓存。让我们从一级缓存开始。

class L1Cache(Cache):
    assoc = 2
    tag_latency = 2
    data_latency = 2
    response_latency = 2
    mshrs = 4
    tgts_per_mshr = 20

(3)设置BaseCache的一些没有默认值的参数。要查看所有可能的配置选项,并查找哪些是必需的,哪些是可选的,您必须查看SimObject的源代码。在本例中,我们使用的是BaseCache。
我们已经扩展了BaseCache并设置了BaseCache SimObject中没有默认值的大多数参数。接下来,让我们再看两个子类L1Cache,一个L1DCache和一个L1ICache

class L1ICache(L1Cache):
    size = '16kB'

class L1DCache(L1Cache):
    size = '64kB'

(4)用一些合理的参数创建一个二级缓存:

class L2Cache(Cache):
    size = '256kB'
    assoc = 8
    tag_latency = 20
    data_latency = 20
    response_latency = 20
    mshrs = 20
    tgts_per_mshr = 12

(5)对于一级缓存,我们添加两个函数: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.slave

(6)为指令和数据缓存定义单独的connectCPU函数,因为I-cache和D-cache端口有不同的名称。我们的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添加函数,分别连接到内存端CPU端总线

def connectCPUSideBus(self, bus):
    self.cpu_side = bus.master

def connectMemSideBus(self, bus):
    self.mem_side = bus.slave
2、向简单的配置文件增添缓存

(1)复制脚本一个新的名字

cp simple.py two_level.py

(2)将caches.py文件导入名称空间。 将以下内容添加到文件顶部(在m5.objects导入之后):

from caches import *

(3)创建CPU之后,创建L1缓存:

system.cpu.icache = L1ICache()
system.cpu.dcache = L1DCache()

(4)使用我们创建的帮助器功能将缓存连接到CPU端口

system.cpu.icache.connectCPU(system.cpu)
system.cpu.dcache.connectCPU(system.cpu)

(5)删除前面将高速缓存端口直接连接到内存总线:

-system.cpu.icache_port = system.membus.slave
-system.cpu.dcache_port = system.membus.slave

(6)无法将L1缓存直接连接到L2缓存,因为L2缓存只希望有一个端口与其连接。 因此,我们需要创建一个L2总线以将我们的L1缓存连接到L2缓存。 我们可以使用我们的辅助函数将L1缓存连接到L2总线:

system.l2bus = L2XBar()

system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)

(7)创建L2缓存并将其连接到L2总线和内存总线

system.l2cache = L2Cache()
system.l2cache.connectCPUSideBus(system.l2bus)

system.l2cache.connectMemSideBus(system.membus)
3、向脚本添加参数

(1)使用gem5进行实验时,您不想每次用不同的参数测试系统时都编辑配置脚本。 为了解决这个问题,您可以将命令行参数添加到gem5配置脚本中。 同样,由于配置脚本只是Python,因此您可以使用支持参数解析的Python库。 尽管optparse已被正式弃用,但gem5随附的许多配置脚本都使用了它,而不是py:mod:argparse,因为gem5的最低Python版本曾经是2.5。 现在,最低的Python版本是2.7,因此py:mod:argparse是编写不需要与当前gem5脚本进行交互的新脚本时的更好选择。 要开始使用optparse,您可以查阅在线Python文档。

要在二级缓存配置中添加选项,请在导入缓存后添加一些选项。

from optparse import OptionParser

parser = OptionParser()
parser.add_option('--l1i_size', help="L1 instruction cache size")
parser.add_option('--l1d_size', help="L1 data cache size")
parser.add_option('--l2_size', help="Unified L2 cache size")

(options, args) = parser.parse_args()

现在,您可以运行build/X86/gem5.opt configs/tutorial/two_level_opts.py --help,它将显示您刚刚添加的选项,运行结果如下:
运行结果

(2)接下来,我们需要将这些选项传递到我们在配置脚本中创建的缓存上。 为此,我们只需更改two_level.py即可将选项作为参数传递给缓存,作为其构造函数的参数,然后添加一个适当的构造函数。

system.cpu.icache = L1ICache(options)
system.cpu.dcache = L1DCache(options)
...
system.l2cache = L2Cache(options)

(3)caches.py中,我们需要向每个类添加构造函数(Python中的__init__函数)。 从基本L1缓存开始,我们将添加一个空的构造函数,因为我们没有适用于基本L1缓存的任何参数。 但是,在这种情况下,我们不能忘记调用super类的构造函数。 如果跳过对super类构造函数的调用,则在尝试实例化缓存对象时,gem5的SimObject属性查找功能将失败,结果将为“ RuntimeError:maximum recursion depth exceeded”。 因此,在L1Cache中,我们需要在静态类成员之后添加以下内容:

def __init__(self, options=None):
    super(L1Cache, self).__init__()
    pass

(4)接下来,在L1ICache中,我们需要使用我们创建的选项(l1i_size)来设置大小。 在下面的代码中,可以防止是否未将选项传递给L1ICache构造函数,以及是否在命令行上未指定任何选项。 在这种情况下,我们将使用已经为尺寸指定的默认值。

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

以及unified L2Cache

def __init__(self, options=None):
    super(L2Cache, self).__init__()
    if not options or not options.l2_size:
        return
    self.size = options.l2_size

(5)通过这些更改,您现在可以从命令行(如下所示)将缓存大小传递到脚本中。

build/X86/gem5.opt configs/tutorial/two_level_opts.py --l2_size='1MB' --l1d_size='128kB'

四、理解GEM5统计数据以及输出

在运行gem5之后,除了模拟脚本会输出的所有信息外,在名为m5out的目录中还生成了三个文件:
config.ini:包含为模拟创建的每个SimObject及其参数值的列表。
config.json:与config.ini相同,但格式为json。
stats.txt:为模拟注册的所有gem5统计信息的文本表示形式。

这些文件的创建位置可以通过以下方式控制:

--outdir=DIR, -d DIR

要创建的目录,其中包含gem5输出文件,包括config.ini,config.json,stats.txt以及其他文件。 如果该目录中的文件已经存在,则将其覆盖。

1、config.ini

该文件是模拟内容的确定版本。 此文件中显示了每个模拟的SimObject的所有参数,无论是在配置脚本中设置还是使用默认值。

该文件在gem5/m5out目录下可以查看

在这里,我们看到,在每个SimObject描述的开头都是它的名称,该名称是在方括号(例如[system.membus])包围的配置文件中创建的。

接下来,将显示SimObject的每个参数及其值,包括未在配置文件中明确设置的参数。 例如,配置文件将时钟域设置为1 GHz(在这种情况下为1000 ticks)。 但是,它没有设置高速缓存行大小(系统中为64)对象。

config.ini文件是用于确保您正在模拟自己想模拟的东西的有用工具。 gem5中有许多设置默认值和覆盖默认值的可能方法。 始终检查config.ini是一项明智的选择,这是对配置文件中设置的值是否传播到实际SimObject实例的健全性检查。

2、stats.txt (官方文档说明

gem5具有灵活的统计信息生成系统。 如上网站上详细介绍了gem5统计信息。 SimObject的每个实例都有自己的统计信息。 在模拟结束时,或发出特殊的统计信息转储命令时,所有SimObjects的统计信息的当前状态都转储到文件中。

首先,统计文件包含有关执行的常规统计信息:

sim_seconds:模拟的秒数
sim_ticks:模拟的滴答声的数量
final_tick:从模拟开始的滴答声的数量(从检查点恢复并且永不重置)sim_freq:模拟的滴答声的频率 
host_inst_rate:模拟器指令速率(inst / s)
host_op_rate:模拟器op(包括micro ops)速率(op / s)host_tick_rate:模拟器滴答速率(tick / s)
host_mem_usage:使用的主机内存字节数
host_seconds:主机上的实时时间
sim_insts:模拟的指令数
sim_ops:模拟的操作数(包括微操作)

五、采用默认的配置脚本

这一部分,我们将探索使用gem5随附的默认配置脚本。 gem5附带了许多配置脚本,可非常快速地使用gem5。 但是,常见的陷阱是使用这些脚本而没有完全了解要模拟的内容。 在使用gem5进行计算机体系结构研究时,充分了解要模拟的系统非常重要。 这一部分将完成一些重要的选项以及默认配置脚本的各个部分。

在前面的部分中,都是从头开始创建自己的配置脚本的。 这非常强大,因为它允许您指定每个系统参数。 但是,某些系统的设置非常复杂(例如,全系统ARM或x86计算机)。 幸运的是,gem5开发人员提供了许多脚本来引导构建系统的过程。

1、目录结构浏览

所有gem5的配置文件能够被发现在 configs/.

(1)boot/:

这些是在 full-system 模式下使用的rcS文件。 这些文件在Linux启动后由模拟器加载,并由Shell执行。 在全系统模式下运行时,其中大多数用于控制基准。 其中一些是实用程序功能,例如hack_back_ckpt.rcS。 这些文件将在 full-system simulation .一章中更深入地介绍。

(2)common/:

该目录包含许多用于创建模拟系统的帮助程序脚本和函数。例如,Caches.py与前面各章中创建的caches.py和caches_opts.py文件相似。
Options.py包含可以在命令行上设置的各种选项。像CPU的数量,系统时钟等等。这是查看是否要更改的选项是否已经包含命令行参数的好地方。
CacheConfig.py包含用于设置经典内存系统的缓存参数的选项和功能。
MemConfig.py提供了一些用于设置内存系统的帮助程序功能。
FSConfig.py包含必要的功能,可以为许多不同种类的系统设置全系统仿真。全系统仿真将在本章中进一步讨论。
Simulation.py包含许多帮助程序功能,用于设置和运行gem5。该文件中包含的许多代码都用于管理保存和还原检查点。下面的examples /中的示例配置文件使用该文件中的功能来执行gem5仿真。该文件非常复杂,但是在模拟的运行方式上也具有很大的灵活性。

(3)dram/: 包含用于测试DRAM的脚本。

(4)example/:

该目录包含一些示例gem5配置脚本,可以直接使用它们来运行gem5。 具体来说,se.pyfs.py非常有用。 有关这些文件的更多信息,请参见下一部分。 此目录中还有一些其他实用程序配置脚本。

(5)ruby/:此目录包含Ruby及其随附的缓存一致性协议的配置脚本。 更多细节可以在Ruby一章中找到。

(6)splash2/:目录包含用于运行splash2基准套件的脚本,其中包含一些用于配置模拟系统的选项。

(7)topologies/:该目录包含创建Ruby缓存层次结构时可以使用的拓扑的实现。 更多细节可以在Ruby一章中找到。

2、使用 se.py 和 fs.py

这一部分,将讨论在命令行上传递给se.py和fs.py的一些常用选项。 有关如何运行 full-system 模拟的更多详细信息,请参见“full-system模拟”部分。 在这里,将讨论两个文件共有的选项。

这一部分讨论的大多数选项都可以在Options.py中找到,并已在函数addCommonOptions中注册。 本节不详细介绍所有选项。 要查看所有选项,请使用--help运行配置脚本,或阅读脚本的源代码。

(1)首先,我们简单地运行不带任何参数的hello world程序:

build/X86/gem5.opt configs/example/se.py --cmd=tests/test-progs/hello/bin/x86/linux/hello

运行结果就不在这展示了

但是,这根本不是一个非常有趣的模拟! 默认情况下,gem5使用原子CPU并使用原子内存访问,因此没有实际的时序数据报告! 要确认这一点,您可以查看m5out / config.ini。 CPU显示在第46行:

[system.cpu]
type=AtomicSimpleCPU
children=apic_clk_domain dtb interrupts isa itb tracer workload
branchPred=Null
checker=Null
clk_domain=system.cpu_clk_domain
cpu_id=0
do_checkpoint_insts=true
do_quiesce=true
do_statistics_insts=true

(2)要在计时模式下实际运行gem5,请指定CPU类型。 在此期间,我们还可以指定L1缓存的大小。

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

现在,让我们检查config.ini文件,并确保将这些选项正确传播到最终系统。 如果在m5out / config.ini中搜索“cache”,则会发现未创建任何缓存! 即使我们指定了缓存的大小,也没有指定系统应使用缓存,因此未创建缓存。 正确的命令行应为:

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 --caches
3、一些常见的选项se.py和fs.py

运行时将打印所有可能的选项:

build/X86/gem5.opt configs/example/se.py --help

(1)–cpu-type=CPU_TYPE:要运行的cpu类型。 这是始终设置的重要参数。 默认值为atomic,它不执行时序模拟。

(2)–sys-clock=SYS_CLOCK:以系统速度运行的块的顶级时钟。

(3)–cpu-clock=CPU_CLOCK:以CPU速度运行的块的时钟。 这与上面的系统时钟是分开的。

(4)–mem-type=MEM_TYPE:要使用的内存类型。 选项包括不同的DDR内存和ruby内存控制器。

(5)–caches:使用经典缓存执行模拟。

(6)–l2cache:如果使用经典缓存,请使用L2缓存执行仿真。

(7)–ruby:使用Ruby代替传统的缓存作为缓存系统模拟。

(8)-m TICKS, --abs-max-tick=TICKS:运行到指定的绝对模拟滴答声,包括来自已恢复检查点的滴答声。 如果您只想模拟一定数量的模拟时间,这将很有用。

(9)-I MAXINSTS, --maxinsts=MAXINSTS:要模拟的指令总数(默认值:永远运行)。 如果要在执行一定数量的指令后停止仿真,此功能很有用。

(10)-c CMD, --cmd=CMD:在系统调用仿真模式下运行的二进制文件。

(11)-o OPTIONS, --options=OPTIONS:传递给二进制文件的选项在整个字符串周围使用“”。 当您运行带有选项的命令时,这很有用。 您可以通过此变量传递参数和选项(例如–whatever)。

(12)–output=OUTPUT:将标准输出重定向到文件。 如果您想将模拟应用程序的输出重定向到文件而不是打印到屏幕,这将很有用。 注意:要重定向gem5输出,您必须在配置脚本之前传递参数。

(13)–errout=ERROUT:将stderr重定向到文件。 与上面类似。

GEM5系列教程索引

GEM5教程–GEM5开始之旅(一)

GEM5教程–GEM5开始之旅(二)

GEM5教程–修改和拓展gem5(一)

GEM5教程–修改和拓展gem5(二)

GEM5教程–修改和拓展gem5(三)

GEM5教程–修改和拓展gem5(四)

GEM5教程-互联网络

GEM5教程-Garnet

  • 11
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值