Understanding Coroutines

At the heart of Cocotb lies the concept of Coroutines. Learn how `async` and `await` enable concurrent hardware simulation in a single-threaded Python environment.

What is a Coroutine?

Hardware is inherently parallel—multiple blocks work simultaneously. Python is inherently sequential. Coroutines bridge this gap.

A coroutine is a function that can pause execution and return control to the scheduler (the simulator). When it pauses, other coroutines can run. This mimics parallelism.

# Standard Function (Blocking)
def my_func():
    return 10
# Coroutine (Can Pause)
async def my_driver(dut):
    dut.a.value = 1
    await Timer(10, "ns") # <--- Pauses here!
    dut.a.value = 0

The `async` and `await` Keywords

  • `async def`: Defines a function as a coroutine. Crucial for any test or driver.
  • `await`: The magic pause button. You MUST `await` a Trigger (like a Timer or Edge) to advance time.

Common Mistake

If you call `Timer(10)` without `await`, nothing happens! You just created a Timer object but didn't tell the scheduler to wait for it.

Running Parallel Tasks

To run multiple things at once (e.g., driving a clock AND driving data), use cocotb.start_soon().

@cocotb.test()
async def parallel_example(dut):
    # This starts the clock in the background
    cocotb.start_soon(Clock(dut.clk, 10, "ns").start())
    # This runs in "parallel" with the clock
    dut.rst.value = 1
    await RisingEdge(dut.clk)
    dut.rst.value = 0