├── MANIFEST.in ├── pysysc ├── __init__.py ├── sysc.py ├── structural.py └── sccppyy.py ├── .project ├── .pydevproject ├── NOTES.md ├── .gitignore ├── PyScModule.h ├── setup.py ├── README.md ├── tests └── test_pysysc.py ├── PyScModule.cpp ├── CONTRIBUTING.md └── LICENSE /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include projectname *.py 2 | include README.md 3 | include LICENSE 4 | include requirements.txt 5 | -------------------------------------------------------------------------------- /pysysc/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | from .sccppyy import * -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | PySysC 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /pysysc/sysc.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | ''' 8 | Created on 30.08.2021 9 | 10 | @author: eyck 11 | ''' 12 | from cppyy import gbl as cpp 13 | 14 | class ScModule(cpp.scc.PyScModule): 15 | ''' 16 | classdocs 17 | ''' 18 | 19 | def __init__(self, name): 20 | super().__init__(self, name) 21 | 22 | def __getattr__(self, attr): 23 | if self.instance is None: 24 | raise AttributeError 25 | return getattr(self.instance, attr) 26 | 27 | def ScMethod(self, func, sensitivity=[], initialize=False): 28 | pass 29 | 30 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Default 8 | 9 | 10 | 11 | 12 | python interpreter 13 | 14 | 15 | 16 | 17 | 3.6 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | /${PROJECT_DIR_NAME} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # PySysC notes 2 | 3 | ## How to install pyenv 4 | 5 | ``` 6 | apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl 7 | 8 | git clone https://github.com/pyenv/pyenv.git ~/.pyenv 9 | echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc 10 | echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc 11 | echo 'eval "$(pyenv init -)"' >> ~/.bashrc 12 | 13 | git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv 14 | echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc 15 | 16 | git clone https://github.com/pyenv/pyenv-virtualenvwrapper.git $(pyenv root)/plugins/pyenv-virtualenvwrapper 17 | ``` 18 | 19 | ## How to generate an AST dump 20 | 21 | ``` 22 | clang++-6.0 -Xclang -ast-dump -fsyntax-only components.h -I/home/eyck/.conan/data/SystemC/2.3.3/minres/stable/package/639c4a2f24965e2a3603237a94eab6929f9d3993/include/ -std=c++11 -fno-color-diagnostics 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Python 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | -------------------------------------------------------------------------------- /PyScModule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef COMPONENTS_PYSCMODULE_H_ 8 | #define COMPONENTS_PYSCMODULE_H_ 9 | #include 10 | #include 11 | #include 12 | #define PY_SSIZE_T_CLEAN 13 | #include 14 | 15 | 16 | typedef struct _object PyObject; 17 | 18 | namespace scc { 19 | 20 | class PyScModule: public sc_core::sc_module { 21 | public: 22 | PyScModule(PyObject* self, const sc_core::sc_module_name& nm); 23 | virtual ~PyScModule(); 24 | 25 | void ScThread(std::string fname); 26 | void ScWait(); 27 | void ScWait( const sc_core::sc_event& e ); 28 | void ScWait( const sc_core::sc_event_or_list& el ); 29 | void ScWait( const sc_core::sc_event_and_list& el ); 30 | void ScWait( const sc_core::sc_time& t ); 31 | void ScWait( double v, sc_core::sc_time_unit tu ); 32 | void ScWait( const sc_core::sc_time& t, const sc_core::sc_event& e ); 33 | void ScWait( double v, sc_core::sc_time_unit tu, const sc_core::sc_event& e ); 34 | void ScWait( const sc_core::sc_time& t, const sc_core::sc_event_or_list& el ); 35 | void ScWait( double v, sc_core::sc_time_unit tu, const sc_core::sc_event_or_list& el ); 36 | void ScWait( const sc_core::sc_time& t, const sc_core::sc_event_and_list& el ); 37 | void ScWait( double v, sc_core::sc_time_unit tu, const sc_core::sc_event_and_list& el ); 38 | 39 | protected: 40 | void before_end_of_elaboration() override; 41 | void end_of_elaboration() override; 42 | void start_of_simulation() override; 43 | void end_of_simulation() override; 44 | private: 45 | void invoke_callback(std::string const&); 46 | PyObject* self{nullptr}; 47 | PyGILState_STATE gstate; 48 | }; 49 | 50 | } 51 | #endif /* COMPONENTS_PYSCMODULE_H_ */ 52 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | from setuptools import setup, Extension 8 | import os 9 | 10 | def find(name, path): 11 | for root, dirs, files in os.walk(path): 12 | if name in files: 13 | return os.path.join(root, name) 14 | 15 | def readme(): 16 | with open('README.md') as f: 17 | return f.read() 18 | 19 | sysc_home = os.environ['SYSTEMC_HOME'] 20 | if os.environ.get('STDCXX') is not None: 21 | sysc_cxxstd = os.environ['STDCXX'] 22 | else: 23 | sysc_cxxstd = '11' 24 | sysc_lib_dir = os.path.dirname(find('libsystemc.so', sysc_home)) 25 | 26 | pysyscsc = Extension('pysyscsc', 27 | define_macros = [('MAJOR_VERSION', '1'), ('MINOR_VERSION', '0')], 28 | include_dirs = [sysc_home+'/include'], 29 | extra_compile_args = ['-std=c++%s'%sysc_cxxstd], 30 | extra_link_args = ['-Wl,-rpath,%s'%sysc_lib_dir], 31 | libraries = ['systemc'], 32 | library_dirs = [sysc_lib_dir], 33 | sources = ['PyScModule.cpp'], 34 | depends = ['PyScModule.h']) 35 | 36 | 37 | setup(name='PySysC', 38 | version='0.2', 39 | description='Python SystemC binding', 40 | long_description=readme(), 41 | ext_modules = [pysyscsc], 42 | classifiers=[ 43 | 'Development Status :: 3 - Alpha', 44 | 'License :: OSI Approved :: Apache Software License', 45 | 'Programming Language :: Python :: 3.6', 46 | 'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)' 47 | ], 48 | keywords='SystemC simulation', 49 | url='https://github.com/accellera-official/PySysC', 50 | author='MINRES Technologies GmbH', 51 | author_email='info@minres.com', 52 | license='Apache-2.0', 53 | packages=['pysysc'], 54 | package_data={ 55 | 'pysyscsc': ['PyScModule.h'], 56 | }, 57 | #data_files=[('include',['PyScModule.h'])], 58 | headers=['PyScModule.h'], 59 | include_package_data=True, 60 | install_requires=[ 61 | 'cppyy', 62 | 'conan<2.0' 63 | ], 64 | test_suite='nose.collector', 65 | tests_require=['nose'], 66 | ) 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PySysC 2 | 3 | A Python package to make SystemC usable from Python. It supports composition of a SystemC/TLM model as well as running the simulation. As of version 0.2 it also allows to run Python functions as part of an SC_THREAD. 4 | 5 | ## How to setup the environment 6 | 7 | The installation for PySysC is as follows (using bash shell). The process has 8 | been tested under CentOS7 and Ubuntu 20.04. Make sure a newer version of gcc 9 | is in your path (tested with gcc-6.3.0). Also the SystemC installation has to 10 | be reference with the environment variable SYSTEMC_HOME. 11 | 12 | If you get an error complaining about 13 | missing Python.h, you need to install Python development headers. See the 14 | articel under https://blog.ducthinh.net/gcc-no-such-file-python-h. 15 | 16 | ``` 17 | # create virtual environment 18 | python3 -m venv pysysc-env 19 | # and avtivate it 20 | . pysysc-env/bin/activate 21 | # update pip to mekae sure we have the newest version 22 | python3 -m pip install --upgrade pip 23 | # install wheel package 24 | python3 -m pip install wheel 25 | # install cppyy, C++ std version needs to match the version used when building the SystemC library 26 | STDCXX=11 python3 -m pip install cppyy 27 | # clone of PySysC 28 | git clone https://git.minres.com/SystemC/PySysC.git 29 | # install PySysC, for development PySysC use 'python3 -m pip install -e` 30 | SYSTEMC_HOME= python3 -m pip install PySysC 31 | ``` 32 | 33 | ## Running the example 34 | 35 | To run the example you need to clone and build the PySysC-SC repo. It contains the code and libraries being used in the example. This project uses [Conan.io](https://conan.io/) as package manager so it should be installed (see down below). 36 | To deactivate conan and use a SystemC installation just comment out the line `setup_conan()` in CMakeLists.txt and set the environment variable SYSTEMC_HOME. 37 | 38 | ### Run the router_eample.py 39 | 40 | ``` 41 | # get the PySysC-SC repo 42 | git clone --recursive https://git.minres.com/SystemC/PySysC-SC.git 43 | # build the project libraries as shared libs 44 | cd PySysC-SC 45 | mkdir -p build/Debug 46 | cd build/Debug 47 | cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON ../.. 48 | make -j components 49 | cd ../.. 50 | # now we are ready to run the example 51 | python3 router_eample.py 52 | 53 | ``` 54 | 55 | ### Installing conan separately 56 | 57 | To install conan being used during cmake and build the example project without 58 | PySysC you need to execute the following steps: 59 | 60 | ``` 61 | # install conan into our virtual environment pysysc-env 62 | python3 -m pip install conan 63 | # create the default profile 64 | conan profile new default --detect 65 | # add the repo for SystemC packages used in the project 66 | conan remote add minres https://api.bintray.com/conan/minres/conan-repo 67 | ``` 68 | 69 | ## TODO 70 | 71 | * pythonize `sc_module` with iteration protocol (`__next__` and `StopIteration` exception) 72 | * add support for SC_METHOD 73 | -------------------------------------------------------------------------------- /tests/test_pysysc.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | import unittest 8 | import json 9 | import cppyy 10 | import os.path 11 | import pysysc as scpy 12 | from cppyy import gbl as cpp 13 | 14 | 15 | class Test(unittest.TestCase): 16 | 17 | not_initialized=True 18 | 19 | def setUp(self): 20 | if Test.not_initialized: 21 | proj_home='../../PySysC-SC' 22 | conan_path=os.path.join(proj_home, 'conanfile.txt') 23 | conan_res = scpy.read_config_from_conan(conan_path) 24 | scpy.load_systemc() 25 | scpy.add_include_path(os.path.join(proj_home, 'sc-components/incl')) 26 | scpy.add_library('scc.h', os.path.join(proj_home, 'build/Debug/lib/libscc.so')) 27 | scpy.add_include_path(os.path.join(proj_home, 'components')) 28 | scpy.add_library('components.h', os.path.join(proj_home, 'build/Debug/lib/libcomponents.so')) 29 | 30 | 31 | ############################################################################### 32 | # instantiate 33 | ############################################################################### 34 | Test.clkgen = cpp.ClkGen(cpp.sc_core.sc_module_name("clk_gen")) 35 | Test.rstgen = cpp.ResetGen(cpp.sc_core.sc_module_name("rst_gen")) 36 | Test.initiator = cpp.Initiator(cpp.sc_core.sc_module_name("initiator")) 37 | Test.memories = [cpp.Memory(cpp.sc_core.sc_module_name(name)) for name in ["mem0", "mem1", "mem2", "mem3"]] 38 | Test.router = cpp.Router[4](cpp.sc_core.sc_module_name("router")) 39 | ############################################################################### 40 | # signals 41 | ############################################################################### 42 | Test.sig_clk = cpp.sc_core.sc_signal[cpp.sc_core.sc_time]("clk") 43 | Test.sig_rst = cpp.sc_core.sc_signal[cpp.sc_dt.sc_logic]("rst") 44 | ############################################################################### 45 | # connect it 46 | ############################################################################### 47 | Test.clkgen.clk_o(Test.sig_clk) 48 | Test.rstgen.reset_o(Test.sig_rst) 49 | Test.initiator.socket.bind(Test.router.target_socket) 50 | Test.initiator.clk_i(Test.sig_clk) 51 | Test.initiator.reset_i(Test.sig_rst) 52 | Test.router.clk_i(Test.sig_clk) 53 | Test.router.reset_i(Test.sig_rst) 54 | for idx,m in enumerate(Test.memories): 55 | Test.router.initiator_socket.at(idx).bind(m.socket) 56 | m.clk_i(Test.sig_clk) 57 | m.reset_i(Test.sig_rst) 58 | 59 | Test.not_initialized=False 60 | 61 | def tearDown(self): 62 | #cpp.sc_core.sc_stop() 63 | pass 64 | 65 | 66 | def testSCTimeRepr(self): 67 | cur_time=cpp.sc_core.sc_time_stamp() 68 | cur_time_str=cur_time.to_string() 69 | self.assertEqual(cur_time_str, '0 s') 70 | 71 | def testMembers(self): 72 | members = scpy.get_members(Test.initiator) 73 | print("members") 74 | methods = scpy.get_methods(Test.initiator) 75 | print("methods") 76 | ports = scpy.get_ports(Test.initiator) 77 | print("ports") 78 | intors = scpy.get_inititator_sockets(Test.initiator) 79 | print("intors") 80 | tgts = scpy.get_target_sockets(Test.initiator) 81 | print("tgts") 82 | childs = scpy.get_submodules(Test.initiator) 83 | print("childs") 84 | self.assertTrue(len(intors)==1, "Wrong numbers of initiator sockets") 85 | 86 | def testConnection(self): 87 | intors = scpy.get_inititator_sockets(Test.initiator) 88 | self.assertFalse(isinstance(intors[0][0], cpp.sc_core.sc_object), " intor[0] connects to sc_object", ) 89 | 90 | # def testSim(self): 91 | # cpp.sc_core.sc_in_action=True 92 | # cpp.sc_core.sc_start() 93 | 94 | if __name__ == "__main__": 95 | #import sys;sys.argv = ['', 'Test.testName'] 96 | unittest.main() -------------------------------------------------------------------------------- /PyScModule.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #define SC_INCLUDE_DYNAMIC_PROCESSES 8 | #include "PyScModule.h" 9 | #define PY_SSIZE_T_CLEAN 10 | #include 11 | 12 | class TPyScriptThreadLocker { 13 | PyGILState_STATE m_state; 14 | public: 15 | TPyScriptThreadLocker(): m_state(PyGILState_Ensure()) {} 16 | ~TPyScriptThreadLocker() { PyGILState_Release(m_state); } 17 | }; 18 | 19 | scc::PyScModule::PyScModule(PyObject* self, const sc_core::sc_module_name& nm) 20 | : sc_core::sc_module(nm) 21 | , self(self) 22 | { 23 | if (! PyEval_ThreadsInitialized()) 24 | PyEval_InitThreads(); 25 | Py_INCREF(self); 26 | } 27 | 28 | scc::PyScModule::~PyScModule() { 29 | Py_DECREF(self); 30 | } 31 | 32 | void scc::PyScModule::before_end_of_elaboration(){ 33 | invoke_callback("BeforeEndOfElaboration"); 34 | } 35 | 36 | void scc::PyScModule::end_of_elaboration(){ 37 | invoke_callback("EndOfElaboration"); 38 | } 39 | 40 | void scc::PyScModule::start_of_simulation(){ 41 | invoke_callback("StartOfSimulation"); 42 | } 43 | 44 | void scc::PyScModule::end_of_simulation(){ 45 | invoke_callback("EndOfSimulation"); 46 | } 47 | 48 | void scc::PyScModule::ScThread(std::string fname) { 49 | sc_core::sc_spawn_options opts; 50 | auto run_handle = sc_core::sc_spawn([this, fname](){ 51 | invoke_callback(fname); 52 | }, nullptr, &opts); 53 | this->sensitive << run_handle; 54 | this->sensitive_pos << run_handle; 55 | this->sensitive_neg << run_handle; 56 | } 57 | void scc::PyScModule::invoke_callback(std::string const& callback_name) { 58 | if(PyObject_HasAttrString(self, callback_name.c_str())){ 59 | // acquiring the GIL 60 | gstate = PyGILState_Ensure(); 61 | auto* func = PyObject_GetAttrString(self, callback_name.c_str()); 62 | PyObject_CallFunctionObjArgs(func, nullptr); 63 | // Release the thread. No Python API allowed beyond this point. 64 | PyGILState_Release(gstate); 65 | } 66 | } 67 | 68 | void scc::PyScModule::ScWait() { 69 | PyGILState_Release(gstate); 70 | sc_core::sc_module::wait(); 71 | gstate = PyGILState_Ensure(); 72 | } 73 | 74 | void scc::PyScModule::ScWait(const sc_core::sc_event& e){ 75 | PyGILState_Release(gstate); 76 | sc_core::sc_module::wait(e); 77 | gstate = PyGILState_Ensure(); 78 | } 79 | 80 | void scc::PyScModule::ScWait(const sc_core::sc_event_or_list &el) { 81 | PyGILState_Release(gstate); 82 | sc_core::sc_module::wait(el); 83 | gstate = PyGILState_Ensure(); 84 | } 85 | 86 | void scc::PyScModule::ScWait(const sc_core::sc_event_and_list &el) { 87 | PyGILState_Release(gstate); 88 | sc_core::sc_module::wait(el); 89 | gstate = PyGILState_Ensure(); 90 | } 91 | 92 | void scc::PyScModule::ScWait(const sc_core::sc_time &t) { 93 | PyGILState_Release(gstate); 94 | sc_core::sc_module::wait(t); 95 | gstate = PyGILState_Ensure(); 96 | } 97 | 98 | void scc::PyScModule::ScWait(double v, sc_core::sc_time_unit tu) { 99 | PyGILState_Release(gstate); 100 | sc_core::sc_module::wait(v, tu); 101 | gstate = PyGILState_Ensure(); 102 | } 103 | 104 | void scc::PyScModule::ScWait(const sc_core::sc_time &t, const sc_core::sc_event &e) { 105 | PyGILState_Release(gstate); 106 | sc_core::sc_module::wait(t, e); 107 | gstate = PyGILState_Ensure(); 108 | } 109 | 110 | void scc::PyScModule::ScWait(double v, sc_core::sc_time_unit tu, const sc_core::sc_event &e) { 111 | PyGILState_Release(gstate); 112 | sc_core::sc_module::wait(v, tu, e); 113 | gstate = PyGILState_Ensure(); 114 | } 115 | 116 | void scc::PyScModule::ScWait(const sc_core::sc_time &t, const sc_core::sc_event_or_list &el) { 117 | PyGILState_Release(gstate); 118 | sc_core::sc_module::wait(t, el); 119 | gstate = PyGILState_Ensure(); 120 | } 121 | 122 | void scc::PyScModule::ScWait(double v, sc_core::sc_time_unit tu, const sc_core::sc_event_or_list &el) { 123 | PyGILState_Release(gstate); 124 | sc_core::sc_module::wait(v, tu, el); 125 | gstate = PyGILState_Ensure(); 126 | } 127 | 128 | void scc::PyScModule::ScWait(const sc_core::sc_time &t, const sc_core::sc_event_and_list &el) { 129 | PyGILState_Release(gstate); 130 | sc_core::sc_module::wait(t, el); 131 | gstate = PyGILState_Ensure(); 132 | } 133 | 134 | void scc::PyScModule::ScWait(double v, sc_core::sc_time_unit tu, const sc_core::sc_event_and_list &el) { 135 | PyGILState_Release(gstate); 136 | sc_core::sc_module::wait(v, tu, el); 137 | gstate = PyGILState_Ensure(); 138 | } 139 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | How to Contribute 2 | ================= 3 | 4 | This repository is owned by the [Accellera Systems Initiative][1] and 5 | is maintained by the [SystemC Language Working Group][2] (LWG) 6 | according to the [Accellera Policies and Procedures][3]. 7 | 8 | **Contributions to this reference implementation can only be 9 | accepted from Accellera members.** 10 | 11 | ### Join the Accellera SystemC Language Working Group 12 | 13 | If you would like to contribute to the development of SystemC and/or this 14 | reference implementation, have your company, organization, or university 15 | join Accellera and its working groups. 16 | Find out more information at http://www.accellera.org/about/join. 17 | If your company, organization or university is already an Accellera member, 18 | you can request to [join the SystemC Language Working Group here][4]. 19 | 20 | ### Join the SystemC community 21 | 22 | If you are not an Accellera member, please join the **[SystemC community 23 | forum][5]** to provide feedback, report bugs and join the general 24 | discussion around the evolution of SystemC. 25 | 26 | [1]: https://www.accellera.org 27 | [2]: https://accellera.org/activities/working-groups/systemc-language 28 | [3]: https://accellera.org/about/policies-and-procedures 29 | [4]: https://workspace.accellera.org/apps/org/workgroup/lwg/ 30 | [5]: https://forums.accellera.org/forum/9-systemc/ 31 | 32 | --------------------------------------------------------------------- 33 | Issue reporting 34 | --------------------------------------------------------------------- 35 | 36 | You can post the bugs and suggestions of general interest to the 37 | [SystemC Community Forum][5]. When reporting bugs, please specify 38 | the following information (if applicable): 39 | 40 | 1. SystemC version 41 | 2. platform, compiler, flags 42 | 3. description of the problem 43 | 4. steps to reproduce the problem 44 | 5. compile/runtime warnings and errors 45 | 6. code sample, not more than 100 lines to demonstrate the problem 46 | 47 | > **Note** 48 | > All bugs will only be tested against the latest publicly available 49 | > version of the product. 50 | 51 | > **Note** 52 | > All C++ compilers that SystemC supports have bugs of different 53 | > degree of severity. We cannot fix those bugs. 54 | > Please report them to the compiler vendor. 55 | 56 | Accellera WG members have access to the WG-internal issue tracking 57 | at GitHub, as described [here][6]. 58 | 59 | [6]: docs/DEVELOPMENT.md#issue-tracking 60 | 61 | --------------------------------------------------------------------- 62 | Patch submission 63 | --------------------------------------------------------------------- 64 | 65 | The following **sign-off procedure** is established to ensure that 66 | patches submitted for inclusion into this Accellera reference 67 | implementation are properly licensed under the 68 | [Apache License Version 2.0](LICENSE). 69 | 70 | The sign-off is a simple line at the end of the explanation for the 71 | patch (or commit message), which certifies that you wrote it yourself 72 | or otherwise have the right to pass it on as an open-source patch: 73 | 74 | ### Accellera Developer's Certificate of Origin 75 | 76 | By making a signed-off contribution to this Accellera project, 77 | I certify that: 78 | 79 | 1. The contribution was created in whole or in part by me and I have 80 | the right to submit it under the Apache License Version 2.0 81 | (see LICENSE). 82 | 83 | 2. The contribution was provided directly to me by some other person 84 | who certified (1) above, and I am forwarding it without 85 | modification. 86 | 87 | 3. I understand and agree that this Accellera project and the 88 | contribution are public and that a record of the contribution 89 | (including all personal information I submit with it, including 90 | my sign-off) is maintained indefinitely and may be redistributed 91 | in accordance with this project or the Apache License Version 2.0. 92 | 93 | If you can certify the above *Accellera Developer's Certificate of Origin*, 94 | please use `git commit --signoff` to add a line of the form: 95 | ``` 96 | Signed-off-by: Ima Contributor 97 | ``` 98 | using your real name (no pseudonyms or anonymous contributions). 99 | 100 | > **Note** 101 | > For Accellera members, contributions are already bound by the 102 | > [Accellera policies and procedures][3] and the sign-off is optional, 103 | > but recommended. For **non-Accellera** members, the sign-off is 104 | > **mandatory** for consideration by the Accellera WGs. 105 | 106 | When submitting a pull-request against the public repository, the 107 | contribution may be considered by the Accellera WGs for inclusion. 108 | An Accellera member may submit it to the private repository with their 109 | own `Signed-off-by` line appended. It stays under the sole governance 110 | of the corresponding WGs to decide whether the proposal will be included 111 | in the reference implementation (or future Accellera standards). 112 | 113 | --------------------------------------------------------------------- 114 | Repository organization 115 | --------------------------------------------------------------------- 116 | 117 | The central source code repository of the Accellera SystemC implementation 118 | is hosted in two repositories at [GitHub](http://github.com). The main 119 | repositories are **private** to the [`OSCI-WG` GitHub organization][7] and 120 | can be found at: 121 | 122 | * https://github.com/OSCI-WG/systemc (core SystemC library) 123 | * https://github.com/OSCI-WG/systemc-regressions (regression test suite) 124 | 125 | A read-only, **public** copy of these repositories can be found at 126 | 127 | * https://github.com/accellera-official/systemc (core SystemC library) 128 | * https://github.com/accellera-official/systemc-regressions (regression test suite) 129 | 130 | To obtain access to the repositories and the GitHub organization in general, 131 | LWG members can contact the LWG chairs at 132 | including their GitHub account name. 133 | 134 | [7]: https://github.com/osci-wg "Accellera WG GitHub organization" 135 | -------------------------------------------------------------------------------- /pysysc/structural.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | from cppyy import gbl as cpp 8 | from builtins import getattr 9 | import re 10 | from enum import Enum 11 | import logging 12 | 13 | class Mode(Enum): 14 | SIM = 1 15 | BUILD = 2 16 | 17 | mode=Mode.SIM 18 | 19 | module_list = list() 20 | connection_list = list() 21 | 22 | def dump_structure(): 23 | mports=dict() 24 | 25 | def add_port(p, io): 26 | mod = p.get_parent_object() 27 | if mod not in mports: 28 | mports[mod]=dict() 29 | mports[mod]['in']=[] 30 | mports[mod]['out']=[] 31 | if not p in mports[mod][io]: 32 | mports[mod][io].append(p) 33 | 34 | for c in connection_list: 35 | add_port(c.source, 'out') 36 | for t in c.targets: 37 | add_port(t, 'in') 38 | 39 | with open("structure.dot", "w") as f: 40 | f.write("""digraph structs { 41 | rankdir=LR 42 | node [shape=record];\n""") 43 | for m in mports.keys(): 44 | #struct3 [shape=record,label="hello\nworld |{ b |{c| d|e}| f}| g | h"]; 45 | in_names=['<%s> %s'%(p.basename(), p.basename()) for p in mports[m]['in']] 46 | out_names=['<%s> %s'%(p.basename(), p.basename()) for p in mports[m]['out']] 47 | if len(in_names) == 0: 48 | f.write(' %s [shape=record,label="{%s|{%s}}"];\n' % ( 49 | m.name(), m.basename(), '|'.join(out_names))) 50 | elif len(out_names) == 0: 51 | f.write(' %s [shape=record,label="{{%s}|%s}"];\n' % ( 52 | m.name(), '|'.join(in_names), m.basename())) 53 | else: 54 | f.write(' %s [shape=record,label="{{%s}|%s|{%s}}"];\n' % ( 55 | m.name(), '|'.join(in_names), m.basename(), '|'.join(out_names))) 56 | for c in connection_list: 57 | attr = 'dir=both arrowhead=box arrowtail=obox' 58 | if isinstance(c, Signal): 59 | attr = 'dir=none' 60 | src = '%s:%s' % (c.source.get_parent_object().name(), c.source.basename()) 61 | for t in c.targets: 62 | tgt = '%s:%s' % (t.get_parent_object().name(), t.basename()) 63 | f.write(" %s -> %s [%s];\n" % (src, tgt, attr)) 64 | f.write("}\n") 65 | 66 | class Simulation(object): 67 | 68 | @staticmethod 69 | def run(duration = None): 70 | if duration: 71 | if isinstance(duration, str): 72 | f = cpp.sc_core.sc_time.from_string(duration) 73 | elif isinstance(duration, float): 74 | f=cpp.sc_core.sc_time.from_seconds(duration) 75 | elif isinstance(duration, cpp.sc_core.sc_time): 76 | f=duration 77 | else: 78 | raise ValueError(f'Illegal value for duration: {duration}.') 79 | logging.debug(f"Starting simulation for {f.to_string()}") 80 | cpp.sc_core.sc_start(f, cpp.sc_core.SC_RUN_TO_TIME) 81 | else: 82 | logging.debug("Starting simulation") 83 | cpp.sc_core.sc_start() 84 | if not cpp.sc_core.sc_end_of_simulation_invoked(): 85 | cpp.sc_core.sc_stop() 86 | 87 | 88 | def __init__(self): 89 | pass 90 | 91 | class Module(object): 92 | ''' 93 | classdocs 94 | ''' 95 | def __init__(self, clazz): 96 | self.cppclazz=clazz 97 | self.instance=None 98 | module_list.append(self) 99 | 100 | def __getattr__(self, attr): 101 | if self.instance is None: 102 | raise AttributeError 103 | return getattr(self.instance, attr) 104 | 105 | def create(self, name, *args): 106 | sc_name = cpp.sc_core.sc_module_name(str(name)) 107 | if args: 108 | self.instance = self.cppclazz(sc_name, *args) 109 | else: 110 | self.instance = self.cppclazz(sc_name) 111 | return self 112 | 113 | class Connection(object): 114 | ''' 115 | classdocs 116 | ''' 117 | def __init__(self): 118 | self.source=None 119 | self.targets=[] 120 | connection_list.append(self) 121 | 122 | def src(self, module_port): 123 | self.source=module_port 124 | return self 125 | 126 | def sink(self, module_port): 127 | self.targets.append(module_port) 128 | #TODO: add try block and print types of both ports in case of a missmatch 129 | self.source.bind(module_port) 130 | return self 131 | 132 | def cross(self, module_port_in, module_port_out): 133 | self.targets.append(module_port_in) 134 | self.source.bind(module_port_in) 135 | return Connection().src(module_port_out) 136 | 137 | class Signal(Connection): 138 | ''' 139 | classdocs 140 | ''' 141 | 142 | _sc_inout_re = re.compile(r'^sc_core::sc_(?:_in)?out<(.*)>$') 143 | _sc_in_re = re.compile(r'^sc_core::sc_in<(.*)>$') 144 | _sc_port_re = re.compile(r'^sc_core::sc_port<[^<]*<(.*)> ?>$') 145 | 146 | def __init__(self, name=None): 147 | Connection.__init__(self) 148 | self.name=name 149 | self.signal=None 150 | self.data_type=None 151 | 152 | def create_signal(self, module_port): 153 | if self.signal is None: 154 | port_class_name = type(module_port).__cpp_name__ 155 | match = self._sc_inout_re.match(port_class_name) 156 | if match: 157 | self.data_type = match.group(1) 158 | else: 159 | match = self._sc_in_re.match(port_class_name) 160 | if match: 161 | self.data_type = match.group(1) 162 | else: 163 | match = self._sc_port_re.match(port_class_name) 164 | if match: 165 | self.data_type = match.group(1) 166 | if self.data_type is None: 167 | raise AttributeError 168 | py_dt_name = self.data_type.replace("::", ".").replace("<", "[").replace(">", "]") 169 | self.signal = eval("cpp.sc_core.sc_signal[cpp.%s](self.name)" % py_dt_name) 170 | 171 | def src(self, module_port): 172 | self.source=module_port 173 | self.create_signal(module_port) 174 | module_port.bind(self.signal) 175 | return self 176 | 177 | def sink(self, module_port): 178 | self.targets.append(module_port) 179 | self.create_signal(module_port) 180 | module_port.bind(self.signal) 181 | return self 182 | 183 | def cross(self, module_port_in, module_port_out): 184 | self.targets.append(module_port_in) 185 | self.source.bind(module_port_in) 186 | return Signal().src(module_port_out) 187 | 188 | class SignalVector(Connection): 189 | ''' 190 | classdocs 191 | ''' 192 | 193 | _sc_inout_re = re.compile(r'^sc_core::sc_vector ?>$') 194 | _sc_in_re = re.compile(r'^sc_core::sc_vector ?>$') 195 | _sc_port_re = re.compile(r'^sc_core::sc_vector ?> ?>$') 196 | 197 | def __init__(self, name=None): 198 | Connection.__init__(self) 199 | self.name=name 200 | self.signal=None 201 | self.data_type=None 202 | 203 | def create_signal(self, module_port): 204 | if self.signal is None: 205 | port_class_name = type(module_port).__cpp_name__ 206 | match = self._sc_inout_re.match(port_class_name) 207 | if match: 208 | self.data_type = match.group(1) 209 | else: 210 | match = self._sc_in_re.match(port_class_name) 211 | if match: 212 | self.data_type = match.group(1) 213 | else: 214 | match = self._sc_port_re.match(port_class_name) 215 | if match: 216 | self.data_type = match.group(1) 217 | if self.data_type is None: 218 | raise AttributeError 219 | py_dt_name = self.data_type.replace("::", ".").replace("<", "[").replace(">", "]") 220 | self.signal = eval("cpp.sc_core.sc_signal[cpp.%s](self.name)" % py_dt_name) 221 | 222 | def src(self, module_port): 223 | self.source=module_port 224 | self.create_signal(module_port) 225 | module_port.bind(self.signal) 226 | return self 227 | 228 | def sink(self, module_port): 229 | self.targets.append(module_port) 230 | self.create_signal(module_port) 231 | module_port.bind(self.signal) 232 | return self 233 | 234 | class Clock(Connection): 235 | ''' 236 | classdocs 237 | ''' 238 | _sc_time_re = re.compile(r'^sc_core::sc_in') 239 | 240 | def __init__( 241 | self, 242 | name=None, 243 | period=1.0, 244 | time_unit='SC_NS', 245 | duty_cycle=0.5, 246 | start_time=0, 247 | posedge_first=True): 248 | Connection.__init__(self) 249 | self.name=name 250 | self.clk_tgl = cpp.sc_core.sc_clock( 251 | 'sc_clock_'+self.name, 252 | cpp.sc_core.sc_time(period, eval(f"cpp.sc_core.{time_unit}")), 253 | duty_cycle, 254 | cpp.sc_core.sc_time(start_time, eval(f"cpp.sc_core.{time_unit}")), 255 | posedge_first) 256 | self.clk_time = None 257 | 258 | def sink(self, module_port): 259 | self.targets.append(module_port) 260 | port_class_name = type(module_port).__cpp_name__ 261 | match = self._sc_time_re.match(port_class_name) 262 | if match: 263 | if self.clk_time is None: 264 | self.clk_time = cpp.sc_core.sc_signal[cpp.sc_core.sc_time](self.name) 265 | self.clk_time.write(self.clk_tgl.period()) 266 | module_port.bind(self.clk_time) 267 | else: 268 | module_port.bind(self.clk_tgl) 269 | 270 | return self 271 | 272 | class Event(object): 273 | ''' 274 | classdocs 275 | ''' 276 | 277 | def __init__(self, name='PySysCEvent'): 278 | self.name=name 279 | self.evt = cpp.sc_core.sc_event('sc_event_'+self.name) 280 | 281 | def notify(self, period=0.0, time_unit='SC_NS'): 282 | self.evt.notify(cpp.sc_core.sc_time(period, eval(f"cpp.sc_core.{time_unit}"))) 283 | 284 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /pysysc/sccppyy.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2019 -2021 MINRES Technolgies GmbH 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | import cppyy 8 | import os.path 9 | import site 10 | from sysconfig import get_paths 11 | import sys 12 | import re 13 | import logging 14 | from contextlib import (redirect_stdout, redirect_stderr) 15 | import io 16 | 17 | lang_symbols = { 18 | 3: '199711L', 19 | 11:'201103L', 20 | 14:'201402L', 21 | 17:'201703L'} 22 | sysIncludeDirs = set() 23 | 24 | includeDirs = set() 25 | 26 | interactive = False 27 | 28 | def find_file(name, paths): 29 | for path in paths: 30 | for root, dirs, files in os.walk(path): 31 | if name in files: 32 | return os.path.join(root, name) 33 | 34 | def read_config_from_conan(build_dir, build_type='Release'): 35 | data={} 36 | with io.open(os.path.join(build_dir, 'conanbuildinfo.txt'), encoding='utf-8') as conan_file: 37 | sl = conan_file.readlines() 38 | key='' 39 | for item in sl: 40 | stripped_item = item.rstrip() 41 | match = re.search(r'\[(\S+)\]', stripped_item) 42 | if match: 43 | key=match.group(1) 44 | data[key]=[] 45 | elif len(stripped_item): 46 | data[key].append(stripped_item) 47 | # set include pathes and load libraries 48 | for p in data['includedirs']: 49 | add_sys_include_path(p) 50 | for l in data['libdirs']: 51 | if os.path.exists(l+'/'+'libsystemc.so'): 52 | cppyy.load_library(l+'/'+'libsystemc.so') 53 | for b in data['builddirs']: 54 | if '/systemc/' in b: 55 | os.environ['SYSTEMC_HOME'] =b 56 | elif '/systemc-cci/' in b: 57 | os.environ['CCI_HOME'] = b 58 | elif '/systemc-scv/' in b: 59 | os.environ['SCV_HOME'] = b 60 | 61 | systemc_loaded=False 62 | cci_loaded=False 63 | 64 | def load_systemc(cxxstd=11): 65 | if 'SYSTEMC_HOME' in os.environ: 66 | add_sys_include_path(os.path.join(os.environ['SYSTEMC_HOME'], 'include')) 67 | systemc_loaded = False 68 | for l in ['lib', 'lib64', 'lib-linux', 'lib-linux64']: 69 | for f in ['libsystemc.so']: 70 | full_file=os.path.join(os.environ['SYSTEMC_HOME'], l, f) 71 | if os.path.isfile(full_file): 72 | cppyy.load_library(full_file) 73 | cppyy.cppdef(""" 74 | #define SC_CPLUSPLUS %s 75 | #include "systemc" 76 | #include "tlm" 77 | namespace sc_core { extern void pln(); } 78 | """ % lang_symbols[cxxstd]) 79 | systemc_loaded=True 80 | _load_systemc_cci() 81 | break 82 | if systemc_loaded: break; 83 | if not interactive: cppyy.gbl.sc_core.pln() 84 | cppyy.gbl.sc_core.sc_in_action=True 85 | _load_pythonization_lib() 86 | return True 87 | return False 88 | 89 | def _load_systemc_scv(): 90 | if 'SCV_HOME' in os.environ: 91 | add_sys_include_path(os.path.join(os.environ['SCV_HOME'], 'include')) 92 | for l in ['lib', 'lib64', 'lib-linux', 'lib-linux64']: 93 | for f in ['libscv.so']: 94 | full_file = os.path.join(os.environ['SCV_HOME'], l, f) 95 | if os.path.isfile(full_file): 96 | cppyy.load_library(full_file) 97 | cppyy.include("cci_configuration") 98 | cci_loaded=True 99 | return True 100 | return False 101 | 102 | def _load_systemc_cci(): 103 | for home_dir in ['CCI_HOME', 'SYSTEMC_HOME']: 104 | if home_dir in os.environ: 105 | if os.path.exists(os.path.join(os.environ[home_dir], 'include', 'cci_configuration')): 106 | add_sys_include_path(os.path.join(os.environ[home_dir], 'include')) 107 | for l in ['lib', 'lib64', 'lib-linux', 'lib-linux64']: 108 | for f in ['libcciapi.so']: 109 | full_file = os.path.join(os.environ[home_dir], l, f) 110 | if os.path.isfile(full_file): 111 | cppyy.load_library(full_file) 112 | cppyy.include("cci_configuration") 113 | cci_loaded=True 114 | return True 115 | return False 116 | 117 | def _load_pythonization_lib(debug = False): 118 | plat_info = get_paths() 119 | # check for standard search path 120 | for key in plat_info: 121 | plat_dir =plat_info[key] 122 | if os.path.isdir(plat_dir): 123 | if debug: logging.debug("Checking for pythonization lib in platform dir %s"%plat_dir) 124 | for file in os.listdir(plat_dir): 125 | if re.match(r'pysyscsc.*\.so', file): 126 | cppyy.load_library(os.path.join(plat_dir, file)) 127 | full_path = os.path.join(plat_dir, '../../../include/site/python%d.%d/PySysC/PyScModule.h' % sys.version_info[:2]) 128 | if debug: logging.debug('found %s, looking for %s'%(file, full_path)) 129 | if full_path and os.path.isfile(full_path): 130 | cppyy.include(full_path) 131 | return 132 | # check site packages first to check for venv 133 | for site_dir in site.getsitepackages(): 134 | if os.path.isdir(site_dir): 135 | if debug: logging.debug("Checking for pythonization lib in site package dir %s"%site_dir) 136 | for file in os.listdir(site_dir): 137 | if re.match(r'pysyscsc.*\.so', file): 138 | cppyy.load_library(os.path.join(site_dir, file)) 139 | full_path = find_file('PyScModule.h', site.PREFIXES) 140 | if debug: logging.debug('found %s, looking at %s for %s'%(file, site.PREFIXES, full_path)) 141 | if full_path and os.path.isfile(full_path): 142 | cppyy.include(full_path) 143 | return 144 | if site.ENABLE_USER_SITE: 145 | #check user site packages (re.g. ~/.local) 146 | user_site_dir = site.getusersitepackages() 147 | if os.path.isdir(user_site_dir): 148 | if debug: logging.debug("Checking for pythonization lib in user site dir %s"%user_site_dir) 149 | for file in os.listdir(user_site_dir): 150 | if re.match(r'pysyscsc.*\.so', file): 151 | cppyy.load_library(os.path.join(user_site_dir, file)) 152 | user_base = site.USER_BASE 153 | full_path = user_base + '/include/python%d.%d/PySysC/PyScModule.h' % sys.version_info[:2] 154 | if debug: logging.debug('found %s, looking at %s for %s'%(file, user_base, full_path)) 155 | if os.path.isfile(full_path): 156 | cppyy.include(full_path) 157 | return 158 | # could not be found in install, maybe development environment 159 | pkgDir = os.path.join(os.path.dirname( os.path.realpath(__file__)), '..') 160 | if os.path.isdir(pkgDir): 161 | if debug: logging.debug("Checking for pythonization lib in source dir %s"%pkgDir) 162 | for file in os.listdir(pkgDir): 163 | if re.match(r'pysyscsc.*\.so', file): 164 | cppyy.load_library(os.path.join(pkgDir, file)) 165 | full_path = os.path.join(pkgDir, 'PyScModule.h') 166 | if full_path and os.path.isfile(full_path): 167 | cppyy.include(full_path) 168 | return 169 | sys.exit("No Pythonization found") 170 | 171 | def add_library(header, lib, project_dir=None): 172 | lib_path = lib 173 | if(project_dir is not None): 174 | for root, dirs, files in os.walk(project_dir): 175 | if lib in files: 176 | lib_path = os.path.join(root, lib) 177 | break 178 | buf = io.StringIO() 179 | with redirect_stdout(buf), redirect_stderr(buf): 180 | cppyy.load_library(lib_path) 181 | cppyy.include(header) 182 | return buf.getvalue() 183 | 184 | def add_include_path(incl): 185 | includeDirs.add(incl) 186 | cppyy.add_include_path(incl) 187 | 188 | def add_sys_include_path(incl): 189 | sysIncludeDirs.add(incl) 190 | cppyy.add_include_path(incl) 191 | 192 | # prepare a pythonizor 193 | def _pythonizor(clazz, name): 194 | # A pythonizor receives the freshly prepared bound C++ class, and a name stripped down to 195 | # the namespace the pythonizor is applied. Also accessible are clazz.__name__ (for the 196 | # Python name) and clazz.__cpp_name__ (for the C++ name) 197 | if name == 'sc_time': 198 | clazz.__repr__ = lambda self: repr(self.to_string()) 199 | clazz.__str__ = lambda self: self.to_string() 200 | elif name in ['sc_object', 'sc_module']: 201 | clazz.__repr__ = lambda self: repr(self.name()) 202 | elif len(name) > 8 and name[:7] == 'sc_port<': 203 | clazz.__repr__ = lambda self: repr(self.name()) 204 | elif len(name) > 10 and name[:9] == 'sc_export<': 205 | clazz.__repr__ = lambda self: repr(self.name()) 206 | 207 | # install the pythonizor as a callback on namespace 'sc_core' (default is the global namespace) 208 | cppyy.py.add_pythonization(_pythonizor, 'sc_core') 209 | 210 | # reflection methods 211 | def get_members(sc_object): 212 | def is_cpp_data_type(name, module): 213 | matches = [x for x in ['int', 'char', 'float', 'double'] if name == x] 214 | if len(matches) > 0 or module[:10] == "cppyy.gbl.": 215 | return True 216 | else: 217 | return False 218 | 219 | members = [(e, getattr(sc_object, e)) for e in dir(sc_object)] 220 | return [(k,v) for k,v in members if is_cpp_data_type(type(v).__name__, type(v).__module__)] 221 | 222 | def get_methods(sc_object): 223 | members = [(e, getattr(sc_object, e)) for e in dir(sc_object)] 224 | return [(k,v) for k,v in members if type(v).__name__=='CPPOverload'] 225 | 226 | def get_ports(module): 227 | res = [] 228 | for elem in dir(module): 229 | attr=getattr(module, elem) 230 | if isinstance(attr, cppyy.gbl.sc_core.sc_port_base) and not isinstance(attr, cppyy.gbl.tlm.tlm_base_socket_if): 231 | res.append(attr) 232 | return res 233 | 234 | def get_exports(module): 235 | res = [] 236 | for elem in dir(module): 237 | attr=getattr(module, elem) 238 | if isinstance(attr, cppyy.gbl.sc_core.sc_export_base) and not isinstance(attr, cppyy.gbl.tlm.tlm_base_socket_if): 239 | res.append(attr) 240 | return res 241 | 242 | def get_initiator_sockets(module): 243 | res = [] 244 | for elem in dir(module): 245 | attr=getattr(module, elem) 246 | if isinstance(attr, cppyy.gbl.sc_core.sc_port_base) and isinstance(attr, cppyy.gbl.tlm.tlm_base_socket_if): 247 | res.append(attr) 248 | return res 249 | 250 | def get_target_sockets(module): 251 | res = [] 252 | for elem in dir(module): 253 | attr=getattr(module, elem) 254 | if isinstance(attr, cppyy.gbl.sc_core.sc_export_base) and isinstance(attr, cppyy.gbl.tlm.tlm_base_socket_if): 255 | res.append(attr) 256 | return res 257 | 258 | def get_submodules(module): 259 | res = [] 260 | for elem in dir(module): 261 | attr=getattr(module, elem) 262 | if isinstance(attr, cppyy.gbl.sc_core.sc_module): 263 | res.append(attr) 264 | return res 265 | 266 | import time 267 | 268 | class timewith(): 269 | def __init__(self, name=''): 270 | self.name = name 271 | self.start = time.time() 272 | 273 | @property 274 | def elapsed(self): 275 | return time.time() - self.start 276 | 277 | def checkpoint(self, name=''): 278 | return '{timer} {checkpoint} took {elapsed} seconds'.format(timer=self.name, checkpoint=name, elapsed=self.elapsed).strip() 279 | 280 | def __enter__(self): 281 | return self 282 | 283 | def __exit__(self, mytype, value, traceback): 284 | print(self.checkpoint('finished')) 285 | pass 286 | --------------------------------------------------------------------------------