{"id":15943,"date":"2021-03-11T12:24:19","date_gmt":"2021-03-11T17:24:19","guid":{"rendered":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/?p=15943"},"modified":"2026-03-27T08:47:45","modified_gmt":"2026-03-27T12:47:45","slug":"introduction-to-coroutines","status":"publish","type":"post","link":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2021\/03\/11\/introduction-to-coroutines\/","title":{"rendered":"Introduction to Coroutines"},"content":{"rendered":"\n<p>In the <a href=\"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2021\/02\/08\/verification-learns-a-new-language\/\">first post of this series on Python verification<\/a> we discussed the proxy-driven testbench and how a proxy-driven architecture enables Python as a verification language.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"405\" height=\"171\" src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/54\/2021\/02\/Proxy-Testbench-1.png\" alt=\"Proxy-driven testbench\" class=\"wp-image-15808\"\/><figcaption>Proxy-driven Testbench<\/figcaption><\/figure>\n\n\n\n<p>This testbench uses <em>testbench software<\/em> to create stimulus and analyze monitored input and results. It implements predictions, scoreboards, and functional coverage.<\/p>\n\n\n\n<p>We normally write this testbench software in SystemVerilog using the UVM. But we might have an easier time writing it in Python.  If only we could get Python to talk to an RTL device-under-test (DUT). <\/p>\n\n\n\n<p>It turns out we can with an open source project named <strong><a href=\"https:\/\/docs.cocotb.org\/en\/stable\/\" target=\"_blank\" rel=\"noopener\">cocotb<\/a><\/strong>.  CoCoTB stands for <strong>CO<\/strong>routine <strong>CO<\/strong>simulation <strong>T<\/strong>est <strong>B<\/strong>ench. <\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><em>Coroutines <\/em>are a Python feature that was originally designed to implement asynchronous interfaces such as IO protocols. It turns out they are perfect for implementing simulation behavior.<\/li><li><em>Cosimulation <\/em>is running Python from an RTL simulation. <strong>cocotb<\/strong> supports Questa and other simulators this way.<\/li><li>The <em>testbench <\/em>refers to our Python program that implements the <strong>testbench software<\/strong> block in the diagram above.<\/li><\/ul>\n\n\n\n<p>In this blog post we&#8217;ll discuss coroutines and create a simple example. In the next we&#8217;ll implement a simple testbench.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Awaiting Coroutines<\/h2>\n\n\n\n<p>Coroutines are Python functions that run in an event loop provided by a module such as <strong>asyncio<\/strong>  or <strong>cocotb<\/strong>. They are normal functions defined using the <code>async<\/code> keyword:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>async def traffic():\n    for _ in range(2):\n        time.sleep(1)\n        print (\"Button pressed\")\n        await green()\n\nawait traffic()<\/code><\/pre>\n\n\n\n<p>The <code>traffic() <\/code>coroutine above is called an <em>awaitable<\/em>. The async keyword means that we cannot simply call this function. We must <code>await<\/code> it, for example in a Jupyter notebook as above, or run it using an event loop such as <code>asyncio.run(traffic())<\/code>.<\/p>\n\n\n\n<p>The <code>traffic()<\/code> awaitable loops twice (the <code>_<\/code> variable is a Pythonic way of saying that a variable is ignored.)  Each time it waits one second and then someone presses the traffic button. Now the traffic must await a green light, so we have the <code>await green()<\/code> statement.  Now let&#8217;s look at the entire traffic example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import time\n\nasync def green():\n    await red()\n    print(\"GREEN: Traffic going\")\n\nasync def yellow():\n    print(\"YELLOW: Traffic slowing\")\n    time.sleep(0.5)\n\nasync def red():\n    await yellow()\n    print(\"RED: Traffic stopped\")\n    time.sleep(2)\n\nasync def traffic():\n    for _ in range(2):\n        time.sleep(1)\n        print (\"Button pressed\")\n        await green()\n\nawait traffic()<\/code><\/pre>\n\n\n\n<p>The <code>traffic()<\/code> function awaits the <code>green()<\/code> coroutine. The lights go yellow, red, green.  So <code>green()<\/code> awaits <code>red()<\/code>, <code>red()<\/code> awaits <code>yellow()<\/code>, and <code>yellow() <\/code>finishes after two seconds ending the loop.  This means the coroutines finish in this order twice:  <code>yellow()<\/code>, <code>red()<\/code>, <code>green()<\/code>, <code>traffic()<\/code>. This gives us the following output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Button pressed\nYELLOW: Traffic slowing\nRED: Traffic stopped\nGREEN: Traffic going\nButton pressed\nYELLOW: Traffic slowing\nRED: Traffic stopped\nGREEN: Traffic going<\/code><\/pre>\n\n\n\n<p>If you have Jupyter Notebook you can copy the code in a notebook to see the delays in action.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>In this post we learned about coroutines, the basis of cocotb.  In the next post we&#8217;ll use <strong>cocotb<\/strong> to create a simple bus functional model and connect it to a testbench.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the first post of this series on Python verification we discussed the proxy-driven testbench and how a proxy-driven architecture&#8230;<\/p>\n","protected":false},"author":74314,"featured_media":15805,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spanish_translation":"","french_translation":"","german_translation":"","italian_translation":"","polish_translation":"","japanese_translation":"","chinese_translation":"","footnotes":""},"categories":[984,1,981,10],"tags":[902],"industry":[],"product":[],"coauthors":[947],"class_list":["post-15943","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cocotb","category-news","category-pyuvm","category-tips-tricks","tag-python"],"featured_image_url":"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/54\/2021\/02\/Proxy-Testbench.png","_links":{"self":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/15943","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/users\/74314"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/comments?post=15943"}],"version-history":[{"count":5,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/15943\/revisions"}],"predecessor-version":[{"id":15949,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/15943\/revisions\/15949"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media\/15805"}],"wp:attachment":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media?parent=15943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/categories?post=15943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/tags?post=15943"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/industry?post=15943"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/product?post=15943"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/coauthors?post=15943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}