24.22.4.1.1. AtomicSimpleCPU initial events
Let’s have a closer look at the initial magically scheduled events of the simulation.
Most events come from other events, but at least one initial event must be scheduled somehow from elsewhere to kick things off.
The initial tick event:
0: Event: AtomicSimpleCPU tick.wrapped_function_event: EventFunctionWrapped 39 scheduled @ 0
we’ll study by breaking at at the point that prints messages: b Trace::OstreamLogger::logMessage()
to see where events are being scheduled from:
Trace::OstreamLogger::logMessage() at trace.cc:149 0x5555593b3b1e void Trace::Logger::dprintf_flag<char const*, char const*, unsigned long>() at 0x55555949e603 void Trace::Logger::dprintf<char const*, char const*, unsigned long>() at 0x55555949de58 Event::trace() at eventq.cc:395 0x55555946d109 EventQueue::schedule() at eventq_impl.hh:65 0x555557195441 EventManager::schedule() at eventq.hh:746 0x555557194aa2 AtomicSimpleCPU::activateContext() at atomic.cc:239 0x555559075531 SimpleThread::activate() at simple_thread.cc:177 0x555559545a63 Process::initState() at process.cc:283 0x555559484011 ArmProcess64::initState() at process.cc:126 0x55555730827a ArmLinuxProcess64::initState() at process.cc:1,777 0x5555572d5e5e
The interesting call is at AtomicSimpleCPU::activateContext
:
schedule(tickEvent, clockEdge(Cycles(0)));
which calls EventManager::schedule
.
AtomicSimpleCPU
is an EventManager
because SimObject
inherits from it.
tickEvent
is an EventFunctionWrapper
which contains a std::function<void(void)> callback;
, and is initialized in the constructor as:
tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick", false, Event::CPU_Tick_Pri),
The call stack above ArmLinuxProcess64::initState
is pybind11 fuzziness, but if we grep a bit we find the Python call point:
src/python/m5/simulate.py
def instantiate(ckpt_dir=None): ... # Create the C++ sim objects and connect ports for obj in root.descendants(): obj.createCCObject() for obj in root.descendants(): obj.connectPorts() # Do a second pass to finish initializing the sim objects for obj in root.descendants(): obj.init() ... # Restore checkpoint (if any) if ckpt_dir: ... else: for obj in root.descendants(): obj.initState()
and this gets called from the toplevel Python scripts e.g. se.py configs/common/Simulation.py
does:
m5.instantiate(checkpoint_dir)
As we can see, initState
is just one stage of generic SimObject
initialization. root.descendants()
goes over the entire SimObject
tree calling initState()
.
Finally, we see that initState
is part of the SimObject
C++ API:
src/sim/sim_object.hh
class SimObject : public EventManager, public Serializable, public Drainable, public Stats::Group { ... /** * initState() is called on each SimObject when *not* restoring * from a checkpoint. This provides a hook for state * initializations that are only required for a "cold start". */ virtual void initState();
Finally, we see that initState
is exposed to the Python API at:
build/ARM/python/_m5/param_SimObject.cc
module_init(py::module &m_internal) { py::module m = m_internal.def_submodule("param_SimObject"); py::class_<SimObjectParams, std::unique_ptr<SimObjectParams, py::nodelete>>(m, "SimObjectParams") .def_readwrite("name", &SimObjectParams::name) .def_readwrite("eventq_index", &SimObjectParams::eventq_index) ; py::class_<SimObject, Drainable, Serializable, Stats::Group, std::unique_ptr<SimObject, py::nodelete>>(m, "SimObject") .def("init", &SimObject::init) .def("initState", &SimObject::initState) .def("memInvalidate", &SimObject::memInvalidate) .def("memWriteback", &SimObject::memWriteback) .def("regProbePoints", &SimObject::regProbePoints) .def("regProbeListeners", &SimObject::regProbeListeners) .def("startup", &SimObject::startup) .def("loadState", &SimObject::loadState, py::arg("cp")) .def("getPort", &SimObject::getPort, pybind11::return_value_policy::reference, py::arg("if_name"), py::arg("idx")) ; }
which is more magical than the other param classes since py::class_<SimObject
has non-trivial methods, those are auto-generated by the cxx_exports
code generation mechanism:
class SimObject(object): ... cxx_exports = [ PyBindMethod("init"), PyBindMethod("initState"), PyBindMethod("memInvalidate"), PyBindMethod("memWriteback"), PyBindMethod("regProbePoints"), PyBindMethod("regProbeListeners"), PyBindMethod("startup"), ]
And the second magically scheduled event is the exit event:
0: Event: Event_70: generic 70 scheduled @ 0 0: Event: Event_70: generic 70 rescheduled @ 18446744073709551615
which is scheduled with backtrace:
Trace::OstreamLogger::logMessage() at trace.cc:149 0x5555593b3b1e void Trace::Logger::dprintf_flag<char const*, char const*, unsigned long>() at 0x55555949e603 void Trace::Logger::dprintf<char const*, char const*, unsigned long>() at 0x55555949de58 Event::trace() at eventq.cc:395 0x55555946d109 EventQueue::schedule() at eventq_impl.hh:65 0x555557195441 BaseGlobalEvent::schedule() at global_event.cc:78 0x55555946d6f1 GlobalEvent::GlobalEvent() at 0x55555949d177 GlobalSimLoopExitEvent::GlobalSimLoopExitEvent() at sim_events.cc:61 0x555559474470 simulate() at simulate.cc:104 0x555559476d6f
which comes at object creation inside simulate()
through the GlobalEvent()
constructor:
simulate_limit_event = new GlobalSimLoopExitEvent(mainEventQueue[0]->getCurTick(), "simulate() limit reached", 0);
This event indicates that the simulation should finish by overriding bool isExitEvent()
which gets checked in the main simulation at EventQueue::serviceOne
:
if (event->isExitEvent()) { assert(!event->flags.isSet(Event::Managed) || !event->flags.isSet(Event::IsMainQueue)); // would be silly return event;
Tested in gem5 12c917de54145d2d50260035ba7fa614e25317a3.