简体   繁体   中英

PyBind11 with SystemC using CMake: ImportError

I'm working with a SystemC project into which I would like to add python bindings using PyBind11 and I've been following the docs on the site here to write the bindings.
I'm using CMake to build my project and the challenge I'm facing is that I need to link my C++ code to SystemC as well as PyBind11. This is how my CMakeLists currently looks:

project(simple_fifo_python)
cmake_minimum_required(VERSION 3.0)

find_package(pybind11 CONFIG REQUIRED)
message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}")

find_library(SC_LIB systemc $ENV{SYSTEMC_HOME}/lib-linux64 REQUIRED)

pybind11_add_module (simple_fifo_python simple_fifo.cpp)
target_compile_options(simple_fifo_python PUBLIC  -Wall)
target_include_directories(simple_fifo_python PUBLIC $ENV{SYSTEMC_HOME}/include)
target_link_libraries(simple_fifo_python PUBLIC ${SC_LIB})

My code which is being compiled using this looks like:

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <systemc.h>

class write_if : virtual public sc_interface
{
   public:
     virtual void write(char) = 0;
     virtual void reset() = 0;
};

class read_if : virtual public sc_interface
{
   public:
     virtual void read(char &) = 0;
     virtual int num_available() = 0;
};

class fifo : public sc_channel, public write_if, public read_if
{
   public:
     fifo(sc_module_name name) : sc_channel(name), num_elements(0), first(0) {}

     void write(char c) {
       if (num_elements == max)
         wait(read_event);

       data[(first + num_elements) % max] = c;
       ++ num_elements;
       write_event.notify();
     }

     void read(char &c){
       if (num_elements == 0)
         wait(write_event);

       c = data[first];
       -- num_elements;
       first = (first + 1) % max;
       read_event.notify();
     }

     void reset() { num_elements = first = 0; }

     int num_available() { return num_elements;}

   private:
     enum e { max = 10 };
     char data[max];
     int num_elements, first;
     sc_event write_event, read_event;
};

class producer : public sc_module
{
   public:
     sc_port<write_if> out;

     SC_HAS_PROCESS(producer);

     producer(sc_module_name name) : sc_module(name)
     {
       SC_THREAD(main);
     }

     void main()
     {
       const char *str =
         "This can really work with pybind11 yo peeepalsssssss!\n";

       while (*str)
         out->write(*str++);
     }
};

class consumer : public sc_module
{
   public:
     sc_port<read_if> in;

     SC_HAS_PROCESS(consumer);

     consumer(sc_module_name name) : sc_module(name)
     {
       SC_THREAD(main);
     }

     void main()
     {
       char c;
       cout << endl << endl;

       while (true) {
         in->read(c);
         cout << c << flush;

         if (in->num_available() == 1)
       cout << "<1>" << flush;
         if (in->num_available() == 9)
       cout << "<9>" << flush;
       }
     }
};

class top : public sc_module
{
   public:
     fifo *fifo_inst;
     producer *prod_inst;
     consumer *cons_inst;

     top(sc_module_name name) : sc_module(name)
     {
       fifo_inst = new fifo("Fifo1");

       prod_inst = new producer("Producer1");
       prod_inst->out(*fifo_inst);

       cons_inst = new consumer("Consumer1");
       cons_inst->in(*fifo_inst);
     }
};

int sc_main (int, char *[]) {
   top top1("Top1");
   sc_start();
   return 0;
}

namespace py = pybind11;
using namespace pybind11::literals;

PYBIND11_MODULE(test, m) 
{
    m.doc() = "pybind11 test plugin with sysc support"; // optional module docstring
    py::class_<fifo>(m, "fifo")
        .def(py::init<sc_module_name>())
        .def("write", &fifo::write)
        .def("read", &fifo::read)
        .def("reset", &fifo::reset)
        .def("num_available", &fifo::num_available)
        .def_property("num_elements", &fifo::reset, &fifo::num_available);
    py::class_<producer>(m, "producer")
        .def(py::init<sc_module_name>())
        .def("main", &producer::main);
    py::class_<consumer>(m, "consumer")
        .def(py::init<sc_module_name>())
        .def("main", &consumer::main);
    py::class_<top>(m, "top")
        .def(py::init<sc_module_name>());

    m.def("sc_main", [](std::vector<std::string> args)
    {
        std::vector<char *> cstrs;
        cstrs.reserve(args.size());
        for (auto &s : args) cstrs.push_back(const_cast<char *>(s.c_str()));
        return sc_main(cstrs.size(), cstrs.data());
    });
}

I'm compiling this by following the cmake_example repository and the pip command runs succesfully and generales the wheel which I can import. However, when I run python and try to import the module, it fails with the following error:

(py37) mayank@b101l:~/test/pybindmod/simple_fifo$ python
Python 3.7.1 (default, Oct 23 2018, 19:19:42) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import simple_fifo_python
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: /home/mayank/tools/lib-linux64/libsystemc-2.3.2.so: undefined symbol: sc_main
>>> 

This is my first project working with PyBind and CMakeLists with multiple dependencies so I can't figure out where this error is coming from. Any help would be greatly appreciated.

This is because SystemC shared library requires sc_main function.

You should try to link with the static library ( libsystemc-2.3.2.a ).

This could be due to C++ mangling the sc_main symbol which the systemc library requires and expects in C symbol format. Can you try adding a extern "C" before sc_main? Something like

extern "C" int sc_main (int, char *[]) {
   top top1("Top1");
   sc_start();
   return 0;
}

This or move the function sc_main to a separate *.c file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM