Gem5 学习 4 - 搭建基本的系统

下面按照gem5官网的学习手册记录一下学习过程,便于复习
注意
1、我参考的是老版本教程。因为我用的版本比较低,新版本在mem_ctrl那边的设置不太一样,但是他们的源代码都在./configs/learning_gem5/part1/simple.py,可以直接看
2、我修改了运行的测试程序。在上一篇博客里我写了一个和helloworld类似的测试程序,这里我改成了自己的。具体编译过程见gem5学习-用se模式测试自己的程序
3、我在configs下新建了文件夹。因此我的路径是./configs/sys_code/simple.py,这个在caches.py的代码里面需要根据自己的情况调整

学习gem5 Part 1(搭建基本系统)

源代码
官方教程在
http://learning.gem5.org/book/part1/simple_config.html
http://learning.gem5.org/book/part1/cache_config.html

simple.py

我们的配置脚本将为一个非常简单的系统建模。我们只有一个简单的CPU内核。该CPU内核将连接到系统范围的内存总线。而且,我们将有一个DDR3内存通道,该通道也已连接到内存总线。
下面是搭建系统的示意图
image

在configs文件夹下新建一个文件夹,放置测试的代码
比如我./configs/sys_code/simple.py
把下面的代码拷进去
注意,我把binary改成自己的了

# -*- coding: utf-8 -*-
""" This file creates a barebones system and executes 'hello', a simple Hello World application.
翻:这个文件创建基本的系统并运行helloworld

See Part 1, Chapter 2: Creating a simple configuration script in the
learning_gem5 book for more information about this script.
翻:看learning_gem5可以详细的学习这个脚本

IMPORTANT: If you modify this file, it's likely that the Learning gem5 book also needs to be updated. For now, email Jason <[email protected]>
翻:如果你修正了文件,说明学习手册也得改,通知下Jason
"""


from __future__ import print_function

# import the m5 (gem5) library created when gem5 is built
# 导入gem5库文件,注意这个脚本直接用python是跑不通的,我们得在编译后的gem5.opt下面跑,所以刚才的命令前面要加build/X86/gem5.opt来执行
import m5
# import all of the SimObjects
# 直接把m5里面的objects类别直接导入,这样就不用每次再写m5.object.simobjectxxx了
from m5.objects import *

# create the system we are going to simulate
# System()函数创建系统,system下面有时钟/cpu/内存/等子模块,也就是我们下面设置的clk_domain/cpu/mem等
system = System()

# Set the clock fequency of the system (and all of its children)
# 创建完系统,开始设置里面的参数
# 设置sys中的时钟参数,SrcClockDomain()函数设置时钟域,在时钟域上用.clock设置频率,.voltage_domain设置电压,这里使用了VoltageDomain()函数设置为默认电压域
system.clk_domain = SrcClockDomain()
system.clk_domain.clock = '1GHz'
system.clk_domain.voltage_domain = VoltageDomain()

# Set up the system
'''
You will almost always use timing mode for the memory simulation, except in special cases like fast-forwarding and restoring from a checkpoint.
翻:正常情况都用timing模式,支持'512MB'或'2ns'这种内存或时间单位
'''
# 开始模拟内存,sys中的mem_mode这里使用的是'timing'模式
# 内存的范围是512MB(基本的计算机)
system.mem_mode = 'timing'               # Use timing accesses
system.mem_ranges = [AddrRange('512MB')] # Create an address range

# Create a simple CPU
# 开始创建CPU,使用TimingSimpleCPU()函数,创建一个基于计时模式的CPU
'''
This CPU model executes each instruction in a single clock cycle to execute, except memory requests, which flow through the memory system.
翻:计时模式的CPU在单个时钟周期内执行每条指令,除了内存请求(通过内存系统)
'''
system.cpu = TimingSimpleCPU()

# Create a memory bus, a system crossbar, in this case
# 用SystemXBar()创建内存总线
system.membus = SystemXBar()

# Hook the CPU ports up to the membus
# 让CPU的缓存连接到内存总线上,因为系统很简单,所以没缓存,直接连到总线
system.cpu.icache_port = system.membus.slave
system.cpu.dcache_port = system.membus.slave

# create the interrupt controller for the CPU and connect to the membus
# 创建CPU中断控制,连接内存总线 createInterruptController()
system.cpu.createInterruptController()

# For x86 only, make sure the interrupts are connected to the memory
# Note: these are directly connected to the memory bus and are not cached
# 中断直连内存总线,无缓存;pio和中断端口连接内存是X86的要求,其他ISA(如ARM)不需要这三条
# 指令集架构(英语:Instruction Set Architecture,缩写为ISA)
# m5.defines.buildEnv['TARGET_ISA']检查当前ISA架构
if m5.defines.buildEnv['TARGET_ISA'] == "x86":
    system.cpu.interrupts[0].pio = system.membus.master
    system.cpu.interrupts[0].int_master = system.membus.slave
    system.cpu.interrupts[0].int_slave = system.membus.master

# Create a DDR3 memory controller and connect it to the membus
# 创建DDR3内存控制器直连内存总线,system.mem_ctrl
system.mem_ctrl = DDR3_1600_8x8()
system.mem_ctrl.range = system.mem_ranges[0]
system.mem_ctrl.port = system.membus.master

# Connect the system up to the membus
# 将系统端口system_port连到内存总线
system.system_port = system.membus.slave

## 开始运行程序 连接
# get ISA for the binary to run.
# m5.defines.buildEnv['TARGET_ISA']返回当前架构
# str.lower()将所有大写字母转换为小写
# 这是因为test_progs/hellp/bin文件夹里面文件名都是小写
isa = str(m5.defines.buildEnv['TARGET_ISA']).lower()

# Run 'hello' and use the compiled ISA to find the binary
# 这里不是gem5自带的hello程序,是我之前编译的可执行文件
#binary = 'tests/test-progs/hello/bin/' + isa + '/linux/hello'
binary = '/home/zwj/gem5-code/hello/hellozwj'

# -----------------------
# -----------------------
# 下面开始运行
# -----------------------
# -----------------------

# 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 = [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 = 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()))

以上就搭建好了一个简单的系统,让我们来测试一下
sudo build/X86/gem5.opt configs/sys_code/simple.py
结果

gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled May 18 2021 21:53:40
gem5 started May 26 2021 19:44:55
gem5 executing on ubuntu, pid 6773
command line: build/X86/gem5.opt configs/sys_code/simple.py

Global frequency set at 1000000000000 ticks per second
warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb: listening for remote gdb on port 7000
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
warn: readlink() called on '/proc/self/exe' may yield unexpected results in various settings.
      Returning '/home/zwj/gem5-code/hello/hellozwj'
info: Increasing stack size by one page.
warn: ignoring syscall access(...)
today, your lucky number is 7
Exiting @ tick 834564000 because exiting with last active thread context

我们看一下m5out文件夹
在这里插入图片描述
系统的架构和我们开头的图片是一致的

caches.py + two_level.py

说明
刚才simple.py是一个非常简单的系统
我们可以发现它只有cpu\clk\membus\memctrl\intertrupt
icache和dcache是直接连到了membus上面

现在我们要给系统加上缓存,cache.py是缓存函数,two_level.py是在simple.py上进一步修改的代码

gem5的缓存比较有意思

Classic caches and Ruby

gem5 currently has two completely distinct subsystems to model the on-chip caches in a system, the “Classic caches” and “Ruby”. The historical reason for this is that gem5 is a combination of m5 from Michigan and GEMS from Wisconsin. GEMS used Ruby as its cache model, whereas the classic caches came from the m5 codebase (hence “classic”). The difference between these two models is that Ruby is designed to model cache coherence in detail. Part of Ruby is SLICC, a language for defining cache coherence protocols. On the other hand, the classic caches implement a simplified and inflexible MOESI coherence protocol.

To choose which model to use, you should ask yourself what you are trying to model. If you are modeling changes to the cache coherence protocol or the coherence protocol could have a first-order impact on your results, use Ruby. Otherwise, if the coherence protocol isn’t important to you, use the classic caches.

gem5有ruby和classic caches两种模式,这两个模型之间的区别在于:

  • Ruby:用于对缓存一致性进行详细建模。Ruby的一部分是SLICC,这是一种用于定义缓存一致性协议的语言。
  • Classic caches:基于MOESI协议,设计了简化不灵活的缓存方案。

要修改缓存一致性方案的就用ruby,只是随便用用的classic caches

这里使用的是classic caches,它默认的basecaches方案在./src/mem/cache/BaseCache.py,可以自行修改编译

下面我们要在simple.py的基础上增加缓存部分,搭建如图所示的系统结构,该部分分为两个部分,caches.py是函数,two_level.py是系统搭建代码
在这里插入图片描述

caches.py 缓存的函数

# -*- coding: utf-8 -*-
""" Caches with options for a simple gem5 configuration script

This file contains L1 I/D and L2 caches to be used in the simple
gem5 configuration script. It uses the SimpleOpts wrapper to set up command
line options from each individual class.
文件包含L1I/D 和L2缓存 用在基本的gem5配置脚本中,它使用SimpleOpts包装器来设置
每个单独类别的命令行选项。
"""

import m5
from m5.objects import Cache

# 这里需要注意,我们是要从当前路径进入./configs/common文件夹,这里需要根据自己的情况修改
# 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
# 缓存的一些特定选项
# 有关所有选项,请参阅src/mem/cache/BaseCache.py

class L1Cache(Cache):
    """Simple L1 Cache with default values(制作L1cache,保存一些与默认设定不通的参数)"""

    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

# L1缓存连接membus和cpu的函数

    def connectBus(self, bus):
        """Connect this cache to a memory-side bus"""
        self.mem_side = bus.slave

    def connectCPU(self, cpu):
        """Connect this cache's port to a CPU-side port
           This must be defined in a subclass"""
        raise NotImplementedError

# ------------------
# 以上设置好了L1cache,下面设置子类
# ------------------

# L1的两个子类,L1DCache和L1ICache,需要分别定义连接cpu

class L1ICache(L1Cache):
    """Simple L1 instruction cache with default values"""

    # Set the default size
    size = '16kB'

    SimpleOpts.add_option('--l1i_size',
                          help="L1 instruction cache size. Default: %s" % 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="L1 data cache size. Default: %s" % 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

# ------------------
# L1及L1D L1I制作完毕,下面开始L2
# ------------------

# 制作L2缓存,同样需要连接mem和cpu总线
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="L2 cache size. Default: %s" % 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.master

    def connectMemSideBus(self, bus):
        self.mem_side = bus.slave

设定好了caches函数之后,我们可以在主配置文件中进行调用

two_level.py

# -*- coding: utf-8 -*-
""" This file creates a single CPU and a two-level cache system.
This script takes a single parameter which specifies a binary to execute.
If none is provided it executes 'hello' by default (mostly used for testing)

See Part 1, Chapter 3: Adding cache to the configuration script in the
learning_gem5 book for more information about this script.
This file exports options for the L1 I/D and L2 cache sizes.

IMPORTANT: If you modify this file, it's likely that the Learning gem5 book
           also needs to be updated. For now, email Jason <[email protected]>
翻译:文件添加了L1 I/D L2
"""
from __future__ import print_function

# 先进行常规操作
# import the m5 (gem5) library created when gem5 is built
import m5
# import all of the SimObjects
from m5.objects import *

# Add the common scripts to our path
m5.util.addToPath('../../')

# import the caches which we made
# 导入刚才做的caches
from caches import *

# import the SimpleOpts module
from common import SimpleOpts

# 下面这堆没看懂在干嘛,不重要看system
# Set the usage message to display
SimpleOpts.set_usage("usage: %prog [options] <binary to execute>")

# Finalize the arguments and grab the opts so we can pass it on to our objects
(opts, args) = SimpleOpts.parse_args()

# get ISA for the default binary to run. This is mostly for simple testing
isa = str(m5.defines.buildEnv['TARGET_ISA']).lower()

# 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__))
#binary = os.path.join(thispath, '../../../',
#                      'tests/test-progs/hello/bin/', isa, 'linux/hello')
binary = 'tests/test-progs/hello/bin/' + isa + '/linux/hello'

# Check if there was a binary passed in via the command line and error if
# there are too many arguments
if len(args) == 1:
    binary = args[0]
elif len(args) > 1:
    SimpleOpts.print_help()
    m5.fatal("Expected a binary to execute as positional argument")
    
# ---------------
# 上面全是在设置调用文件的内容,下面开始搭建
# ---------------

# create the system we are going to simulate
# 和simple.py一样先创建系统、时钟clk、mem、cpu、membus,现在不一样的是加入了缓存
system = System()

# Set the clock fequency 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 = TimingSimpleCPU()

# Create an L1 instruction and data cache
#这里开始不一样,l1 i/d cache构建并连上cpu
system.cpu.icache = L1ICache(opts)
system.cpu.dcache = L1DCache(opts)

# 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
# 创建一个l2membus,L2XBar格式
system.l2bus = L2XBar()

# Hook the CPU ports up to the l2bus
# l1cache连上l2membus
system.cpu.icache.connectBus(system.l2bus)
system.cpu.dcache.connectBus(system.l2bus)

# Create an L2 cache and connect it to the l2bus
# 创建L2缓存,连接到l2membus
system.l2cache = L2Cache(opts)
system.l2cache.connectCPUSideBus(system.l2bus)

# Create a memory bus
# 创建总的membus
system.membus = SystemXBar()

# Connect the L2 cache to the membus
# l2缓存连接membus总线
system.l2cache.connectMemSideBus(system.membus)

# create the interrupt controller for the CPU
# CPU中断设置
system.cpu.createInterruptController()

# For x86 only, make sure the interrupts are connected to the memory
# Note: these are directly connected to the memory bus and are not cached
if m5.defines.buildEnv['TARGET_ISA'] == "x86":
    system.cpu.interrupts[0].pio = system.membus.master
    system.cpu.interrupts[0].int_master = system.membus.slave
    system.cpu.interrupts[0].int_slave = system.membus.master

# Connect the system up to the membus
system.system_port = system.membus.slave

# Create a DDR3 memory controller
# 设置mem ctrl
# system.mem_ctrl = MemCtrl()
system.mem_ctrl = DDR3_1600_8x8()
system.mem_ctrl.range = system.mem_ranges[0]
system.mem_ctrl.port = system.membus.master


# 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 = [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()))

以上搭建完系统,下面开始测试我们自己的构造
sudo build/X86/gem5.opt configs/sys_code/two_level.py
下面是测试结果

gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled May 18 2021 21:53:40
gem5 started May 26 2021 20:30:57
gem5 executing on ubuntu, pid 7081
command line: build/X86/gem5.opt configs/sys_code/two_level.py

Global frequency set at 1000000000000 ticks per second
warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb: listening for remote gdb on port 7000
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
Hello world!
Exiting @ tick 58513000 because exiting with last active thread context

在这里插入图片描述
和原图的架构完全一致
也可以把two_level.py里面的binary文件改掉
binary = 'tests/test-progs/hello/bin/' + isa + '/linux/hello'
把这一行改成自己二进制文件的绝对路径

猜你喜欢

转载自blog.csdn.net/Drinks_/article/details/117321024