def add_process(self, process):
        """Add a process to the simulation.

        Adds a process that is evaluated as a part of the :py:`toplevel` elaboratable and is able to
        replace circuits with Python code.

        The behavior of the process is defined by its *constructor function*, which is
        an :py:`async` function that takes a single argument, the :class:`SimulatorContext`: ::

            async def process(ctx):
                async for clk_edge, rst, ... in ctx.tick().sample(...):
                    ...

            sim.add_process(process)

        This method does not accept coroutines. Rather, the provided :py:`constructor` coroutine
        function is called immediately when the procss is added to create a coroutine, as well as
        by the :meth:`reset` method.

        Processes can be *critical* or *background*, and are always background when added.
        The :meth:`run` method will continue advancing the simulation while any critical testbenches
        or processes are running, and will exit when only background testbenches or processes
        remain. A background process can temporarily become critical using
        the :meth:`~SimulatorContext.critical` context manager.

        At each point in time, all of the non-waiting processes are executed in an arbitrary order
        that may be different between individual simulation runs.

        .. warning::

            If two processes share state, they must do so in a way that does not rely on
            a particular order of execution for correctness.

            Preferably, the shared state would be stored in :class:`~amaranth.hdl.Signal`\\ s (even
            if it is not intended to be a part of a circuit), with access to it synchronized using
            :py:`await ctx.tick().sample(...)`. Such state is visible in a waveform viewer,
            simplifying debugging.

        Raises
        ------
        :exc:`RuntimeError`
            If the simulation has been advanced since its creation or last reset.
        """
        if self._running:
            raise RuntimeError(r"Cannot add a process to a running simulation")
        process = self._check_function(process, kind="process")
        if inspect.iscoroutinefunction(process):
            self._engine.add_async_process(self, process)
        else:
            def wrapper():
                # Only start a process after comb settling, so that the initial values are correct.
                yield Active()
                yield object.__new__(Settle)
                yield from process()
            # TODO(amaranth-0.6): remove
            warnings.warn(
                f"Generator-based processes are deprecated per RFC 36. Use async "
                f"processes instead.",
                DeprecationWarning, stacklevel=1)
            wrap_process = coro_wrapper(wrapper, testbench=False)
            self._engine.add_async_process(self, wrap_process)
Add a process to the simulation.

        Adds a process that is evaluated as a part of the :py:`toplevel` elaboratable and is able to
        replace circuits with Python code.

        The behavior of the process is defined by its *constructor function*, which is
        an :py:`async` function that takes a single argument, the :class:`SimulatorContext`: ::

            async def process(ctx):
                async for clk_edge, rst, ... in ctx.tick().sample(...):
                    ...

            sim.add_process(process)

        This method does not accept coroutines. Rather, the provided :py:`constructor` coroutine
        function is called immediately when the procss is added to create a coroutine, as well as
        by the :meth:`reset` method.

        Processes can be *critical* or *background*, and are always background when added.
        The :meth:`run` method will continue advancing the simulation while any critical testbenches
        or processes are running, and will exit when only background testbenches or processes
        remain. A background process can temporarily become critical using
        the :meth:`~SimulatorContext.critical` context manager.

        At each point in time, all of the non-waiting processes are executed in an arbitrary order
        that may be different between individual simulation runs.

        .. warning::

            If two processes share state, they must do so in a way that does not rely on
            a particular order of execution for correctness.

            Preferably, the shared state would be stored in :class:`~amaranth.hdl.Signal`\ s (even
            if it is not intended to be a part of a circuit), with access to it synchronized using
            :py:`await ctx.tick().sample(...)`. Such state is visible in a waveform viewer,
            simplifying debugging.

        Raises
        ------
        :exc:`RuntimeError`
            If the simulation has been advanced since its creation or last reset.
        
amaranth/sim/core.py