--- /dev/null
+import inspect
+
+from ._core import Process
+
+
+__all__ = ["PyClockProcess"]
+
+
+class PyClockProcess(Process):
+    def __init__(self, state, signal, *, phase, period):
+        assert len(signal) == 1
+
+        self.state  = state
+        self.slot   = self.state.get_signal(signal)
+        self.phase  = phase
+        self.period = period
+
+        self.reset()
+
+    def reset(self):
+        self.runnable = True
+        self.passive = True
+        self.initial = True
+
+    def run(self):
+        if self.initial:
+            self.initial = False
+            self.state.timeline.delay(self.phase, self)
+
+        else:
+            clk_state = self.state.slots[self.slot]
+            clk_state.set(not clk_state.curr)
+            self.state.timeline.delay(self.period / 2, self)
+
+        self.runnable = False
 
 from ._core import *
 from ._pyrtl import _FragmentCompiler
 from ._pycoro import PyCoroProcess
+from ._pyclock import PyClockProcess
 
 
 __all__ = ["Settle", "Delay", "Tick", "Passive", "Active", "Simulator"]
             raise ValueError("Domain {!r} already has a clock driving it"
                              .format(domain.name))
 
-        half_period = period / 2
         if phase is None:
             # By default, delay the first edge by half period. This causes any synchronous activity
             # to happen at a non-zero time, distinguishing it from the reset values in the waveform
             # viewer.
-            phase = half_period
-        def clk_process():
-            yield Passive()
-            yield Delay(phase)
-            # Behave correctly if the process is added after the clock signal is manipulated, or if
-            # its reset state is high.
-            initial = (yield domain.clk)
-            steps = (
-                domain.clk.eq(~initial),
-                Delay(half_period),
-                domain.clk.eq(initial),
-                Delay(half_period),
-            )
-            while True:
-                yield from iter(steps)
-        self._add_coroutine_process(clk_process, default_cmd=None)
+            phase = period / 2
+        self._processes.add(PyClockProcess(self._state, domain.clk, phase=phase, period=period))
         self._clocked.add(domain)
 
     def reset(self):