gem5源码解读se.py以及simulate.py(一)

之前都是在从RISC-V的基础上进行改动,最近想自定制一个指令集,但是感觉对gem5的熟悉度不够,对它的运作方式理解也不是很到位,准备从se.py开始看看源码,观察一个顺序的单核RISC-V是怎么样进行模拟的,顺便记录一下。理解有误的地方欢迎大家指正。

一、se.py

前面部分的代码主要使用于将args中的参数进行处理,如:

Options.addCommonOptions(parser)
Options.addSEOptions(parser)
(CPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args)

Simulation.setCPUClass()会从默认的几个CPU模型中通过输入的参数进行选择,如果要使用到改变的模型可以像我这样:

if args.rand == 1:
    CPUClass = O3_riscv.O3_RISCV
    FutureClass = None
    test_mem_mode = CPUClass.memory_mode()
else:
    (CPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args)

当然前面要声明添加rand参数以及导入头文件,当输入了–rand = 1 后就会选择我在RISC-V的基础上配置的O3_RISCV。
之后一大堆的system.**的赋值,在官方教程里也有写,就是在构建整个系统,这里面包括了之前设置的CPUClass,以及设置cache的参数:

CacheConfig.config_cache(args, system)
root = Root(full_system = False, system = system)

Root()在src/sim/Root.py。这里会调用SimObject中初始化的方法来初始化一个Root,后续在simulate.py中使用到的Root就是当前创建的这个。Root.py中部分代码:

Root.py中部分代码:

class Root(SimObject):

    _the_instance = None
    def __new__(cls, **kwargs):
        if Root._the_instance:
            fatal("Attempt to allocate multiple instances of Root.")
            return None
        Root._the_instance = SimObject.__new__(cls)
        return Root._the_instance

重要的是后面这个

Simulation.run(args, root, system, FutureClass)

将所有设置好的参数放入Simulation.run()中。

二、Simulate.py()

主要就是后面的run()函数,run函数看起来挺多的,将其中主要的部分挑选出来,把这次都用不到的都删掉以及改了一下得到下面的几个部分:

def run(options, root, testsys, cpu_class):

    np = options.num_cpus

    if cpu_class:
        switch_cpus = [cpu_class(switched_out=True, cpu_id=(i))
                       for i in range(np)]

        for i in range(np):
            switch_cpus[i].system = testsys
            switch_cpus[i].workload = testsys.cpu[i].workload
            switch_cpus[i].clk_domain = testsys.cpu[i].clk_domain
            switch_cpus[i].progress_interval = \
                testsys.cpu[i].progress_interval
            switch_cpus[i].isa = testsys.cpu[i].isa
            # simulation period
        testsys.switch_cpus = switch_cpus
        switch_cpu_list = [(testsys.cpu[i], switch_cpus[i]) for i in range(np)]

这部分主要还是设置好switch_cpu_list。对于单核来说np=1,那么此时switch_cpu_list和testsys是一样的。

   if cpu_class:
        print("Switch at curTick count:%s" % str(10000))
        exit_event = m5.simulate(10000)
        print("Switched CPUS @ tick %s" % (m5.curTick()))

        m5.switchCpus(testsys, switch_cpu_list)

    exit_event = benchCheckpoints(options, m5.MaxTick, cptdir)             #do  #maxtick = m5.MaxTick

    print('Exiting @ tick %i because %s' %
          (m5.curTick(), exit_event.getCause()))

    if exit_event.getCode() != 0:
        print("Simulated exit code not 0! Exit code is", exit_event.getCode())

第三行中的m5.simulate位于src/python/m5/simulate.py

def simulate(*args, **kwargs):
    global need_startup

    if need_startup:
        root = objects.Root.getInstance()			#得到之前初始化的Root
        for obj in root.descendants(): obj.startup()

gem5是事件驱动的,所以我认为,这里的循环里的startup()就是驱动部件一次,结合起来就是依次驱动系统中的每个部件。
root.descendants():

    def descendants(self):
        yield self
        for (name, child) in sorted(self._children.items()):
            for obj in child.descendants():
                yield obj

yield用法参考:https://blog.csdn.net/mieleizhi0522/article/details/82142856。总之就是运行一次就选择进一步的孩子模块。
startup()位于src/cim/root.cc

Root::startup()
{
    timeSyncEnable(params().time_sync_enable);
}

其中timeSyncEnable():

Root::timeSyncEnable(bool en)
{
    if (en == _enabled)
        return;
    _enabled = en;
    if (_enabled) {
        // Get event going.
        Tick periods = ((curTick() + _periodTick - 1) / _periodTick);
        Tick nextPeriod = periods * _periodTick;
        schedule(&syncEvent, nextPeriod);
    } else {
        // Stop event.
        deschedule(&syncEvent);
    }
}
负责事件在线程调度的运行与停止
schedule(),deschedule()都在src/sim/eventq.hh中

再回到之前的simulate():

        need_startup = False

        # Python exit handlers happen in reverse order.
        # We want to dump stats last.
        atexit.register(stats.dump)		#生成json
        # register our C++ exit callback function with Python
        atexit.register(_m5.core.doExitCleanup)		#退出内核

atexit.register()用于注册函数在程序退出时以注册顺序逆序执行,参考https://blog.csdn.net/hwb18253164494/article/details/53453694

        # Reset to put the stats in a consistent state.
        stats.reset()	#重置stats文件

    if _drain_manager.isDrained():
        _drain_manager.resume()

在src/sim/drain.cc中有

_drain_manager = _m5.drain.DrainManager.instance()
bool isDrained() const { return _state == DrainState::Drained; }
总之就是重置所有的部件
    # We flush stdout and stderr before and after the simulation to ensure the
    # output arrive in order.
    sys.stdout.flush()		#sys.stdout.flush()的作用就是显示地让缓冲区的内容输出。
    sys.stderr.flush()
    sim_out = _m5.event.simulate(*args, **kwargs)			###还没找到,下次再找吧
    sys.stdout.flush()
    sys.stderr.flush()

    return sim_out

stdout与stderr的区别:https://blog.csdn.net/jasonchen_gbd/article/details/80795645

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值