之前都是在从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
总之就是运行一次就选择进一步的孩子模块。
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()用于注册函数在程序退出时以注册顺序逆序执行
# 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