def write_vcd(self, vcd_file, gtkw_file=None, *, traces=(), fs_per_delta=0):
        # `fs_per_delta`` is not currently documented; it is not clear if we want to expose
        # the concept of "delta cycles" in the surface API. Something like `fs_per_step` might be
        # more appropriate.
        """write_vcd(vcd_file, gtkw_file=None, *, traces=())

        Capture waveforms to a file.

        This context manager captures waveforms for each signal and memory that is referenced from
        :py:`toplevel`, as well as any additional signals or memories specified in :py:`traces`,
        and saves them to :py:`vcd_file`. If :py:`gtkw_file` is provided, it is populated with
        a GTKWave save file displaying :py:`traces` when opened.

        Use this context manager to wrap a call to :meth:`run` or :meth:`run_until`: ::

            with sim.write_vcd("simulation.vcd"):
                sim.run()

        The :py:`vcd_file` and :py:`gtkw_file` arguments accept either a :term:`python:file object`
        or a filename. If a file object is provided, it is closed when exiting the context manager
        (once the simulation completes or encounters an error).

        The :py:`traces` argument accepts a *trace specification*, which can be one of:

        * A :class:`~amaranth.hdl.ValueLike` object, such as a :class:`~amaranth.hdl.Signal`;
        * A :class:`~amaranth.hdl.MemoryData` object or an individual row retrieved from one;
        * A :class:`tuple` or :class:`list` containing trace specifications;
        * A :class:`dict` associating :class:`str` names to trace specifications;
        * An :ref:`interface object <wiring>`.

        Raises
        ------
        :exc:`TypeError`
            If a trace specification refers to a signal with a private name.
        """
        if self._engine.now != 0:
            for file in (vcd_file, gtkw_file):
                if hasattr(file, "close"):
                    file.close()

            # FIXME: can this restriction be lifted?
            raise ValueError("Cannot start writing waveforms after advancing simulation time")

        def traverse_traces(traces):
            if isinstance(traces, ValueLike):
                trace_cast = Value.cast(traces)
                if isinstance(trace_cast, MemoryData._Row):
                    return
                for trace_signal in trace_cast._rhs_signals():
                    if trace_signal.name == "":
                        if trace_signal is traces:
                            raise TypeError("Cannot trace signal with private name")
                        else:
                            raise TypeError(
                                f"Cannot trace signal with private name (within {traces!r})")
            elif isinstance(traces, (list, tuple)):
                for trace in traces:
                    traverse_traces(trace)
            elif isinstance(traces, dict):
                for trace in traces.values():
                    traverse_traces(trace)

        traverse_traces(traces)

        return self._engine.write_vcd(
            vcd_file=vcd_file, gtkw_file=gtkw_file, traces=traces, fs_per_delta=fs_per_delta)
write_vcd(vcd_file, gtkw_file=None, *, traces=())

        Capture waveforms to a file.

        This context manager captures waveforms for each signal and memory that is referenced from
        :py:`toplevel`, as well as any additional signals or memories specified in :py:`traces`,
        and saves them to :py:`vcd_file`. If :py:`gtkw_file` is provided, it is populated with
        a GTKWave save file displaying :py:`traces` when opened.

        Use this context manager to wrap a call to :meth:`run` or :meth:`run_until`: ::

            with sim.write_vcd("simulation.vcd"):
                sim.run()

        The :py:`vcd_file` and :py:`gtkw_file` arguments accept either a :term:`python:file object`
        or a filename. If a file object is provided, it is closed when exiting the context manager
        (once the simulation completes or encounters an error).

        The :py:`traces` argument accepts a *trace specification*, which can be one of:

        * A :class:`~amaranth.hdl.ValueLike` object, such as a :class:`~amaranth.hdl.Signal`;
        * A :class:`~amaranth.hdl.MemoryData` object or an individual row retrieved from one;
        * A :class:`tuple` or :class:`list` containing trace specifications;
        * A :class:`dict` associating :class:`str` names to trace specifications;
        * An :ref:`interface object <wiring>`.

        Raises
        ------
        :exc:`TypeError`
            If a trace specification refers to a signal with a private name.
        
amaranth/sim/core.py