24.22.2. gem5 Python C++ interaction
The interaction uses the Python C extension interface https://docs.python.org/2/extending/extending.html interface through the pybind11 helper library: https://github.com/pybind/pybind11
The C++ executable both:
-
starts running the Python executable
-
provides Python classes written in C++ for that Python code to use
An example of this can be found at:
then gem5 magic SimObject
class adds some crazy stuff on top of it further, is is a mess. In particular, it auto generates params/
headers. TODO: why is this mess needed at all? pybind11 seems to handle constructor arguments just fine:
Let’s study BadDevice
for example:
src/dev/BadDevice.py
defines devicename
:
class BadDevice(BasicPioDevice): type = 'BadDevice' cxx_header = "dev/baddev.hh" devicename = Param.String("Name of device to error on")
The object is created in Python for example from src/dev/alpha/Tsunami.py
as:
fb = BadDevice(pio_addr=0x801fc0003d0, devicename='FrameBuffer')
Since BadDevice
has no __init__
method, and neither BasicPioDevice
, it all just falls through until the SimObject.__init__
constructor.
This constructor will loop through the inheritance chain and give the Python parameters to the C++ BadDeviceParams class as follows.
The auto-generated build/ARM/params/BadDevice.hh
file defines BadDeviceParams in C++:
#ifndef __PARAMS__BadDevice__ #define __PARAMS__BadDevice__ class BadDevice; #include <cstddef> #include <string> #include "params/BasicPioDevice.hh" struct BadDeviceParams : public BasicPioDeviceParams { BadDevice * create(); std::string devicename; }; #endif // __PARAMS__BadDevice__
and ./python/_m5/param_BadDevice.cc
defines the param Python from C++ with pybind11:
namespace py = pybind11; static void module_init(py::module &m_internal) { py::module m = m_internal.def_submodule("param_BadDevice"); py::class_<BadDeviceParams, BasicPioDeviceParams, std::unique_ptr<BadDeviceParams, py::nodelete>>(m, "BadDeviceParams") .def(py::init<>()) .def("create", &BadDeviceParams::create) .def_readwrite("devicename", &BadDeviceParams::devicename) ; py::class_<BadDevice, BasicPioDevice, std::unique_ptr<BadDevice, py::nodelete>>(m, "BadDevice") ; } static EmbeddedPyBind embed_obj("BadDevice", module_init, "BasicPioDevice");
src/dev/baddev.hh
then uses the parameters on the constructor:
class BadDevice : public BasicPioDevice { private: std::string devname; public: typedef BadDeviceParams Params; protected: const Params * params() const { return dynamic_cast<const Params *>(_params); } public: /** * Constructor for the Baddev Class. * @param p object parameters * @param a base address of the write */ BadDevice(Params *p);
src/dev/baddev.cc
then uses the parameter:
BadDevice::BadDevice(Params *p) : BasicPioDevice(p, 0x10), devname(p->devicename) { }
It has been found that this usage of pybind11 across hundreds of SimObject
files accounted for 50% of the gem5 build time at one point: pybind11 accounts for 50% of gem5 build time.
To get a feeling of how SimObject
objects are run, see: gem5 event queue AtomicSimpleCPU syscall emulation freestanding example analysis.
Bibliography:
Tested on gem5 08c79a194d1a3430801c04f37d13216cc9ec1da3.