Repository: cocotb/cocotb

Issue:
Setting arbitrary clock phase
Is there a convenient way to generate a clock with an arbitrary clock phase? From [this pr](https://github.com/cocotb/cocotb/pull/1036) it looks like it's possible to set the clock polarity. Is it possible to generalize this and allow arbitrary phase setting? One example where this would be useful is when simulating clocks synchronized via a PLL.


Discussion:
This should be possible, it just looks like nobody has needed it yet.
While we are at it, setting the duty cycle may also be useful - we need to take care though to not slow down the "simple" use case. 
Note that at a certain point it might just be easier to write your own coroutine, it's really not hard, and it gives you complete freedom to change the frequency and period mid simulation:
```python
@cocotb.coroutine
async def my_custom_clock():
    await Timer(my_initial_delay)
    while True:
        sig <= 1
        await Timer(my_high_delay)
        sig <= 0
        await Timer(low)
```
@eric-wieser BTW, would it be a bit more efficient to create the two high/low Timer instances outside of the loop and await those inside the loop, or does it not matter? 
You're right, it would

On Sat, Oct 19, 2019, 13:15 Colin Marquardt <notifications@github.com>
wrote:

> @eric-wieser <https://github.com/eric-wieser> BTW, would it be a bit more
> efficient to create the two high/low Timer instances outside of the loop
> and await those inside the loop, or does it not matter?
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/cocotb/cocotb/issues/1158?email_source=notifications&email_token=AADH2LDOF37ZTUGWZKGSN43QPL26NA5CNFSM4JCNOU6KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBXODYQ#issuecomment-544137698>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AADH2LECR6LWEAEVHMQTKW3QPL26NANCNFSM4JCNOU6A>
> .
>

Maybe we should just put that example into the docstring of ``Clock()``.
Thanks for posting the manual solution, that works for me. I agree it would be nice to have it in the docstring.

Resolving patch:
diff --git a/cocotb/clock.py b/cocotb/clock.py
--- a/cocotb/clock.py
+++ b/cocotb/clock.py
@@ -1,7 +1,7 @@
 # Copyright (c) 2013 Potential Ventures Ltd
 # Copyright (c) 2013 SolarFlare Communications Inc
 # All rights reserved.
-# 
+#
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are met:
 #     * Redistributions of source code must retain the above copyright
@@ -13,7 +13,7 @@
 #       SolarFlare Communications Inc nor the
 #       names of its contributors may be used to endorse or promote products
 #       derived from this software without specific prior written permission.
-# 
+#
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -48,9 +48,9 @@ def log(self):
 
 
 class Clock(BaseClock):
-    """Simple 50:50 duty cycle clock driver.
+    r"""Simple 50:50 duty cycle clock driver.
 
-    Instances of this class should call its :meth:`start` method and fork the
+    Instances of this class should call its :meth:`start` method and :func:`fork` the
     result.  This will create a clocking thread that drives the signal at the
     desired period/frequency.
 
@@ -65,12 +65,51 @@ class Clock(BaseClock):
         signal: The clock pin/signal to be driven.
         period (int): The clock period. Must convert to an even number of
             timesteps.
-        units (str, optional): One of 
+        units (str, optional): One of
             ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``.
             When no *units* is given (``None``) the timestep is determined by
             the simulator.
+
+
+    If you need more features like a phase shift and an asymmetric duty cycle,
+    it is simple to create your own clock generator (that you then :func:`fork`):
+
+    .. code-block:: python
+
+        @cocotb.coroutine
+        def custom_clock():
+            # pre-construct triggers for performance
+            high_time = Timer(high_delay, units="ns")
+            low_time = Timer(low_delay, units="ns")
+            yield Timer(initial_delay, units="ns")
+            while True:
+                dut.clk <= 1
+                yield high_time
+                dut.clk <= 0
+                yield low_time
+
+    If you also want to change the timing during simulation,
+    use this slightly more inefficient example instead where 
+    the :class:`Timer`\ s inside the while loop are created with 
+    current delay values:
+
+    .. code-block:: python
+
+        @cocotb.coroutine
+        def custom_clock():
+            while True:
+                dut.clk <= 1
+                yield Timer(high_delay, units="ns")
+                dut.clk <= 0
+                yield Timer(low_delay, units="ns")
+
+        high_delay = low_delay = 100
+        cocotb.fork(custom_clock())
+        yield Timer(1000, units="ns")
+        high_delay = low_delay = 10  # change the clock speed
+        yield Timer(1000, units="ns")
     """
-    
+
     def __init__(self, signal, period, units=None):
         BaseClock.__init__(self, signal)
         self.period = get_sim_steps(period, units)
@@ -83,12 +122,12 @@ def __init__(self, signal, period, units=None):
 
     @cocotb.coroutine
     def start(self, cycles=None, start_high=True):
-        """Clocking coroutine.  Start driving your clock by forking a 
+        r"""Clocking coroutine.  Start driving your clock by :func:`fork`\ ing a
         call to this.
 
         Args:
             cycles (int, optional): Cycle the clock *cycles* number of times,
-                or if ``None`` then cycle the clock forever. 
+                or if ``None`` then cycle the clock forever.
                 Note: ``0`` is not the same as ``None``, as ``0`` will cycle no times.
             start_high (bool, optional): Whether to start the clock with a ``1``
                 for the first half of the period.


Test patch: