gem5学习(18):ARM DVFS建模——ARM DVFS modelling

目录

一、Voltage Domains

1、灵活方法:使用命令行标志

2、不太灵活方法:创建 CpuCluster 的子类

二、Clock Domains

三、Adding Clock Domains to an existing simulation

四、The DVFS Handler

1、the number of Voltage Domains does not match the number of Clock Domains

2、no enabled DVFSHandler found


官网教程:gem5: ARM DVFS Support

(温馨提示:笔记置顶有一个gem5学习导览,可以按照自己需要查询对应的博客)

说明:

DVFS(Dynamic Voltage and Frequency Scaling)是一种动态调整处理器电压和频率的技术,旨在根据处理器负载和性能需求来优化功耗和性能之间的平衡。ARM架构支持DVFS,并提供了相应的建模方法。

DVFS建模涉及定义处理器在不同电压和频率下的功耗模型。这可以通过以下步骤完成:

  1. 定义不同电压和频率下的功耗模型:根据处理器的规格和特性,定义不同电压和频率组合下的功耗模型。这些模型通常是基于实际测量数据或根据处理器架构和性能模型进行建模。
  2. 考虑处理器负载和性能需求:DVFS建模还需要考虑处理器负载和性能需求之间的关系。不同的负载和性能需求会导致处理器在不同的电压和频率下工作,从而产生不同的功耗。
  3. 实现DVFS控制算法:DVFS建模还需要实现DVFS控制算法,该算法根据处理器负载和性能需求来选择合适的电压和频率组合。这个算法可以根据实际应用场景进行定制,以实现最佳的功耗和性能平衡。

通过进行ARM DVFS建模,可以更好地理解处理器在不同电压和频率下的功耗特性,并优化处理器的功耗和性能表现。这对于系统级设计、功耗分析和性能优化都非常重要。

与大多数现代CPU一样,ARM CPU支持DVFS。可以通过gem5来对其进行建模,并监控由此产生的功耗使用情况。DVFS建模通过使用Clocked Objects的两个组件来实现:电压域(Voltage Domains)和时钟域(Clock Domains)。本教程详细介绍了不同的组件,并展示了将它们添加到现有仿真中的不同方法。

一、Voltage Domains

电压域(Voltage Domains)规定了CPU可以使用的电压值。在gem5中运行Full System仿真时,如果没有指定电压域(VD),将使用默认值1.0V(伏特)。这是为了避免在用户不希望模拟电压时强制考虑电压。

可以使用单个值或值列表构建电压域,并通过voltage关键字传递给VoltageDomain构造函数。如果指定了单个值和多个频率,则该电压值将用于时钟域中的所有频率。如果指定了电压值列表,其条目数必须与相应的时钟域中的条目数相匹配,并且条目必须按降序排列。与真实硬件一样,电压域适用于整个处理器插槽。这意味着如果要为不同的处理器(例如big.LITTLE设置)使用不同的VD,需要确保big和LITTLE集群位于不同的插槽中(检查与集群相关联的socket_id值)。

有两种方法可以将VD添加到现有的CPU/仿真中,其中一种更灵活,另一种更直观。第一种方法是在提供的configs/example/arm/fs_bigLITTLE.py文件中添加命令行标志,而第二种方法是添加自定义类。

1、灵活方法:使用命令行标志

将电压域添加到仿真中的最灵活方式是使用命令行标志。要添加命令行标志,找到文件中的addOptions函数,并在其中添加标志,可选地附带一些帮助文本。

以下是支持单个和多个电压的示例:

def addOptions(parser):
    [...]
    parser.add_argument("--big-cpu-voltage", nargs="+", default="1.0V",
                        help="Big CPU voltage(s).")
    return parser

可以使用以下方式指定电压域的值:

--big-cpu-voltage V [V [V [...]]]

然后可以在 build 函数中使用 options.big_cpu_voltage 来访问它。nargs="+" 确保至少需要一个参数。下面是在 build 函数中使用的示例:

def build(options):
    [...]
    # big cluster
    if options.big_cpus > 0:
        system.bigCluster = big_model(system, options.big_cpus,
                                      options.big_cpu_clock,
                                      options.big_cpu_voltage)
    [...]

可以添加类似的标志和对构建函数的修改来支持指定 LITTLE CPU 的电压值。这种方法非常方便指定和修改电压值。这种方法唯一的缺点是,多个命令行参数(其中一些是列表形式)可能会使用于调用模拟器的命令显得混乱。

使用基于标志的方法可以解决在运行仿真时指定和修改电压值的问题。通过添加额外的命令行参数,例如 --little_cpu_voltage,可以轻松地指定 LITTLE CPU 的电压值。这样的修改使得在不修改代码的情况下,能够通过命令行直接指定所需的电压值。

然而,随着命令行参数的增加,特别是当参数形式为列表时,命令行可能会变得混乱。这可能会给用户带来一些不便,特别是当需要指定多个不同电压值的时候。

2、不太灵活方法:创建 CpuCluster 的子类

较不灵活的指定电压域的方法是创建 CpuCluster 的子类。类似于现有的 BigCluster 和 LittleCluster 子类,这些子类将扩展 CpuCluster 类。在子类的构造函数中,除了指定 CPU 类型外,还定义了一个电压域值的列表,并将其作为关键字参数 cpu_voltage 传递给超类构造函数的调用。以下是一个示例,用于向 BigCluster 添加电压:

class VDBigCluster(devices.CpuCluster):
    def __init__(self, system, num_cpus, cpu_clock=None, cpu_voltage=None):
        # use the same CPU as the stock BigCluster
        abstract_cpu = ObjectList.cpu_list.get("O3_ARM_v7a_3")
        # voltage value(s)
        my_voltages = [ '1.0V', '0.75V', '0.51V']

        super(VDBigCluster, self).__init__(
            cpu_voltage=my_voltages,
            system=system,
            num_cpus=num_cpus,
            cpu_type=abstract_cpu,
            l1i_type=devices.L1I,
            l1d_type=devices.L1D,
            wcache_type=devices.WalkCache,
            l2_type=devices.L2
        )

这段代码定义了一个名为 VDBigCluster 的自定义类,继承自 devices.CpuCluster。该类的作用是创建一个具有可调节电压(DVFS)功能的 CPU 集群。

在 VDBigCluster 类的构造函数中,有以下关键操作:

  1. 初始化 abstract_cpu

    • 通过 ObjectList.cpu_list.get("O3_ARM_v7a_3") 获取一个名为 "O3_ARM_v7a_3" 的 CPU 对象。这个 CPU 对象代表了用于 BigCluster 的 CPU,可能是一个 ARM 架构的 O3 处理器。
  2. 定义电压值列表 my_voltages

    • 在这个示例中,my_voltages 定义了三个不同的电压值:'1.0V'、'0.75V' 和 '0.51V'。这些电压值将用于创建电压域。
  3. 调用父类的构造函数 super(VDBigCluster, self).__init__(...)

    • 这里使用了 super() 函数来调用父类 devices.CpuCluster 的构造函数。
    • 通过传递相应的参数,包括 cpu_voltage(电压值列表)、systemnum_cpus(CPU 数量)、cpu_type(CPU 类型)、l1i_type(L1I Cache 类型)、l1d_type(L1D Cache 类型)、wcache_type(WalkCache 类型)和 l2_type(L2 Cache 类型),创建了一个具有指定电压值的 CPU 集群。

通过调用这个类的构造函数,可以创建具有不同电压的 CPU 集群对象,用于后续的仿真和研究。

要将电压添加到 LittleCluster,可以定义一个类似的 VDLittleCluster 类。

在定义子类后,我们仍然需要在文件中的 cpu_types 字典中添加一个条目,指定一个字符串名称作为键,以及一对类作为值。例如:

cpu_types = {
    [...]
    "vd-timing" : (VDBigCluster, VDLittleCluster)
}

通过传递已定义的 VD 类型,可以使用具有电压域(VD)的 CPU。

--cpu-type vd-timing

由于修改电压值需要找到正确的子类并修改其代码,或添加更多的子类和 cpu_types 条目,所以这种方法比基于标志的方法要灵活性较差。

使用子类和 cpu_types 的方式确实不如基于标志的方式灵活。当需要修改电压值时,必须修改代码并重新构建仿真环境。这种方式需要更多的手动操作,并且不够直观,特别是在需要频繁更改电压值时。

相比之下,基于标志的方法更加灵活和易于使用。通过命令行标志,可以在不修改代码的情况下指定电压值,从而实现动态的电压调整。例如,在上述的示例中,使用 `--big_cpu_voltage` 标志可以直接指定所需的电压值,而不需要修改代码或创建额外的子类。

因此,对于需要频繁修改电压值或希望提供更大灵活性的情况,使用基于标志的方法是更好的选择。这种方法使得在运行仿真时能够直接指定电压值,而不需要手动修改代码或创建额外的子类。

二、Clock Domains

电压域(Voltage Domains)通常与时钟域(Clock Domains)一起使用。如前所述,如果没有指定自定义的电压值,则时钟域中的所有值都使用默认值 1.0V。

时钟域有三种类型(来自 src/sim/clock_domain.hh):

  1. ClockDomain:为一组位于同一时钟域下的时钟对象提供时钟。时钟域进一步分组为电压域。时钟域提供了一个具有“源”和“派生”时钟域的分层结构支持。

  2. SrcClockDomain:提供与可调节时钟源连接的时钟域的概念。它维护时钟周期,并提供设置/获取时钟的方法,以及由处理器管理的时钟域的配置参数。这些参数包括不同性能级别下的频率值、域 ID 和当前性能级别。需要注意的是,软件请求的性能级别对应于时钟域可以操作的频率操作点之一。

  3. DerivedClockDomain:提供了一个与父时钟域连接的时钟域的概念,父时钟域可以是 SrcClockDomain 或 DerivedClockDomain。它维护时钟分频器,并提供获取时钟的方法。

时钟域和电压域的结合提供了对处理器和系统中时钟和电压的灵活控制。时钟域用于定义时钟的层次结构,并提供与时钟源和时钟频率相关的配置参数。电压域用于定义不同组件的电压值,并允许根据需求调整电压。

三、Adding Clock Domains to an existing simulation

这个示例将使用与电压域示例相同的文件,即 configs/example/arm/fs_bigLITTLE.py 和 configs/example/arm/devices.py。

与电压域类似,时钟域可以是单个值或值的列表。如果给出了一个时钟速度列表,那么与给定电压域值列表的规则相同,即时钟域中的值数量必须与电压域中的值数量相匹配,并且时钟速度必须按降序给出。提供的文件支持将时钟指定为单个值(通过 --{big,little}-cpu-clock 标志),但不支持作为值列表指定。扩展/修改提供的标志的行为是添加对多值时钟域支持的最简单和最灵活的方式,但也可以通过添加子类来实现。

要向现有的 --{big,little}-cpu-clock 标志添加多值支持,请定位 configs/example/arm/fs_bigLITTLE.py 文件中的 addOptions 函数。在各种 parser.add_argument 调用中,找到添加 CPU 时钟标志的那些,并将 kwarg type=str 替换为 nargs="+":

def addOptions(parser):
    [...]
    parser.add_argument("--big-cpu-clock", nargs="+", default="2GHz",
                        help="Big CPU clock frequency.")
    parser.add_argument("--little-cpu-clock", nargs="+", default="1GHz",
                        help="Little CPU clock frequency.")
    [...]

在这里使用 system.numCpuClusters() 是因为时钟域适用于整个集群(cluster),即第一个集群的编号为0,第二个集群的编号为1,依此类推。

如果您不设置域 ID,在尝试运行支持 DVFS 的仿真时,会出现以下错误,因为一些内部检查会捕获默认的域 ID:

--{big,little}-cpu-clock GHz [MHz [MHz [...]]]

四、The DVFS Handler

1、the number of Voltage Domains does not match the number of Clock Domains

如果您指定了电压域(VDs)和时钟域(CDs),然后尝试运行仿真,它可能会运行,但可能会在输出中注意到以下警告:

warn: You have specified both Voltage Domains and Clock Domains, but the number of Voltage Domains does not match the number of Clock Domains.

这个警告表明您在配置中同时指定了电压域和时钟域,但电压域的数量与时钟域的数量不匹配。

要解决这个警告,需要确保电压域和时钟域的数量相匹配。可以通过添加或删除电压域或时钟域来调整它们的数量,以使它们一致。

例如,如果有两个电压域和三个时钟域,可以添加一个额外的电压域或删除一个时钟域,以使它们的数量匹配。

一旦调整了电压域和时钟域的数量,警告就会消失,并且可以继续运行仿真。确保在配置中正确设置电压域和时钟域的数量可以确保Gem5按预期工作,并获得准确的仿真结果。

2、no enabled DVFSHandler found

warn: Existing EnergyCtrl, but no enabled DVFSHandler found.

如果您已经添加了VDs(电压域)和CDs(时钟域),但系统无法与DVFSHandler(动态电压频率调节处理器)进行接口交互以调整数值,则最简单的方法是在configs/example/arm/fs_bigLITTLE.py文件中添加另一个命令行标志。

请按照VD和CD示例的方式,在文件中找到addOptions函数,并在其中追加以下代码:

def addOptions(parser):
    [...]
    parser.add_argument("--dvfs", action="store_true",
                        help="Enable the DVFS Handler.")
    return parser

然后,在 build 函数中追加以下代码:

def build(options):
    [...]
    if options.dvfs:
        system.dvfs_handler.domains = [system.bigCluster.clk_domain,
                                       system.littleCluster.clk_domain]
        system.dvfs_handler.enable = options.dvfs

    return root

有了这些代码,现在能够在调用仿真时使用 --dvfs 标志运行支持DVFS的仿真,并根据需要指定大核心和小核心集群的电压和频率操作点。

例如,可以使用以下命令行来运行DVFS仿真:

./build/ARM/gem5.opt --dvfs --big-voltage 1.2V --little-voltage 0.9V --big-cpu-clock 2GHz 1.5GHz --little-cpu-clock 1.2GHz 900MHz configs/example/arm/fs_bigLITTLE.py

在这个示例中,--dvfs 标志用于启用DVFS功能。--big-voltage 和 --little-voltage 用于分别指定大核心和小核心集群的电压值。--big-cpu-clock 和 --little-cpu-clock 用于分别指定大核心和小核心集群的频率值。

可以根据需要调整这些值,以满足实际需求。通过使用这些命令行选项,Gem5将会加载指定的DVFSHandler,并根据您提供的电压和频率操作点进行动态调整。

你可能感兴趣的:(gem5学习,学习)