.. _embedding:

Embedding the interpreter
#########################

PyStim mainly focuses on extending SystemVerilog using Python, by embedding the
Python interpreter into a SystemVerilog program. This allows the SystemVerilog
program to execute Python code and interact with Python objects. All of the
other documentation pages still apply here, so refer to them for general PyStim usage.


Getting started
===============

A basic simulation with an embedded interpreter can be created with just a few
lines of bash code and the ``pystim_pkg`` package methods, as shown below. For more
information, see :doc:`/compiling`.

.. code-block:: bash

    source ../../scripts/environment.sh
    ./run.sh questa


The essential structure of the ``hello_world.sv`` file looks like this:

.. code-block:: systemverilog

    import pystim_pkg::*;

    module hello_world();
        typedef pystim py;

        initial begin
            py::initialize_interpreter();
            py::print(py::str_("Hello world."));
            py::finalize_interpreter();
        end
    endmodule


The interpreter must be initialized before using any Python, which includes
all the functions and classes in PyStim. The methods class ``initialize_interpreter()``, and
``finalize_interpreter()``  are used to manage interpreter lifetime. After the ``finalize_interpreter()`` call,
the Python interpreter shuts down and clears its memory. No Python functions can be called after this.


Importing modules
=================

Python modules can be imported using ``py_module::import_()``:

.. code-block:: systemverilog

    py_module sys = py_module::import_("sys");
    py::print(sys.attr("path").obtain());

For convenience, the current working directory is included in ``sys.path`` when
embedding the interpreter. This makes it easy to import local Python files:

.. code-block:: python

    """calc.py located in the working directory"""


    def add(i, j):
        return i + j


.. code-block:: systemverilog

        begin
            py_module calc = py_module::import_("add_numbers");
            py_object res = calc.attr("add").call(py::int_(1), py::float_(2.2));
            int n = res.cast_int().get_value();
            assert (n == 3.2) 
        end



Interpreter lifetime
====================

The Python interpreter starts up with ``initialize_interpreter`` and shuts down
when ``finalize_interpreter`` is called. HDL simulation permits only one pair of 
``initialize_interpreter`` and ``finalize_interpreter`` function calls. After this, 
creating a new instance of interpreter not possible. The ``initialize_interpreter``
/ ``finalize_interpreter`` methods can be used anywhere in the SystemVerilog code 
to set the state of the interpreter at any time.

.. warning::

    Starting two python interpreters is a fatal error. So is
    calling ``initialize_interpreter`` for a second time after the interpreter
    has already been initialized will case a fatal error. The same applies to
    calling ``finalize_interpreter`` for a second time after the interpreter has
    already been finalized.

    Do not use the raw CPython API functions ``initialize_interpreter`` and
    ``finalize_interpreter`` as these do not properly handle the lifetime of
    PyStim's internal data.



Restarting the interpter
========================

When using the HDL simulator GUI (intractive mode) for mixed-language (HDL and C/Python)
simulations, it's essential to understand how the simulation state for HDL and C/Python
code are managed during a simulation restart in the already running context.

In HDL simulators, restarting the simulation in the GUI primarily affects the HDL side
of the simulation. It resets the state of the HDL simulation, including all signal
values, registers, and memories to their initial state as defined at simulation zero time. 
However, the state of the C/Python code used in the simulation (for instance, through DPI,
VPI, or PLI) is not automatically reset by this action.

While the HDL simulation is restarted in already running context, PyStim library
does automatically restart the Python interpreter. However, this may not apply to
third-party extension Python modules.The issue is that Python itself cannot completely
unload extension modules and there are several caveats with regard to interpreter
restarting. In short, not all memory may be freed, either due to Python reference 
cycles or user-created global data. All the details can be found in the CPython 
documentation.

Recommendation for Developers:
 - Plan Initialization and Reset: Ensure your Python code includes well-defined 
initialization and reset functions that can be called from HDL.
 - Simulation Hooks: Use simulator-specific mechanisms (e.g., callbacks or $restart commands) to integrate the reset process.
 - Testing: Regularly test the behavior of your mixed-language simulation during restarts to verify proper synchronization between HDL and Python states.

By understanding this distinction and explicitly managing the Python code state during
HDL simulation restarts, you can maintain consistency and avoid unintended 
simulation behavior.

Sub-interpreter support
=======================

Creating multiple copies of interpreter is not possible because it
represents the main Python interpreter. Sub-interpreters are something different
and they do permit the existence of multiple interpreters. This is an advanced
feature of the CPython API and should be handled with care. PyStim does not
currently offer a interface for sub-interpreters, so refer to the CPython
documentation for all the details regarding this feature.

