1

I encountered some problems while trying to make a synchronous function that calls asynchronous functions.(python 3.6.9, cocotb 1.4.0)

As shown in the following example code. The read_cb function will call read function(in FakeDriver class).

After running, I get the error

yield self._fake_lock()
RuntimeError: Task got bad yield: <cocotb.decorators.RunningCoroutine object at 0x7f7fecdbfe10>

What I want is

init FakerDriver
locking... 
locking done
read...
addr: 0x01 
unlocking... 
unlocking done
read done
import cocotb
import asyncio
from cocotb.decorators import coroutine
from cocotb.triggers import Event

class FakeDriver():
    def __init__(self):
        print("init FakeDriver")
        self.busy_event = Event("driver_busy")
        self.busy = False

    @coroutine
    def read(self, addr):
        print("read...")

        yield self._fake_lock()
        print("addr: ", addr)
        self._fake_unlock()

        print("read done")

    @coroutine
    def _fake_lock(self):
        print("locking...")
        if self.busy:
            yield self.busy_event.wait()

        self.busy_event.clear()
        self.busy = True
        print("locking done")

    def _fake_unlock(self):
        print("unlocking...")
        self.busy = False
        self.busy_event.set()
        print("unlocking done")

def read_cb():
    dri = FakeDriver()
    loop = asyncio.get_event_loop()
    task = loop.create_task(dri.read("0x01"))
    ret = loop.run_until_complete(task)
    loop.close()

if __name__ == "__main__":
    read_cb()

  • What version of python are you using? Are you trying to support an old version of python 3? The @coroutine decorator (of asyncio) is deprecated. And you should use `async def` syntax instead. This will probably true for the cocotb library too. See https://docs.python.org/3.8/library/asyncio-task.html#asyncio.coroutine If you are trying to use an old version of python, then you should probably be doing `yield from` rather than `yield`. – Dunes Dec 29 '21 at 11:06
  • The old version of python is 3.4. In 3.5+ you should use `async def` and `await`, and python 3.3 doesn't have asyncio. – Dunes Dec 29 '21 at 11:14
  • @Dunes Hi, I am using python3.6.9 cocotb 1.4.0. If using `yield from`, there will be a error: `yield from self._fake_lock() TypeError: iter() returned non-iterator of type 'RunningCoroutine'` – Just a little noob Dec 29 '21 at 11:34
  • 1
    Did you try `yield from` for both coroutines? Also, did you try using stuff like `async def read(self, addr)` (without @coroutine) and `await self._fake_lock()`? Again, you'll need to do that for both functions. – Dunes Dec 29 '21 at 11:42
  • @Dunes, As you said. It works. Thank you so much for you advice. – Just a little noob Dec 30 '21 at 03:03

1 Answers1

1

Do not mix up cocotb's own implementation of coroutines with asyncio's. In your case, get rid of the asyncio import completely and, with your oldish cocotb 1.4.0, use cocotb's fork() instead of create_task() (as described in https://docs.cocotb.org/en/v1.4.0/quickstart.html?highlight=fork#parallel-and-sequential-execution).

Before you create lots of new code using the deprecated yield etc. in cocotb, consider upgrading cocotb to 1.6.1 and use async def/await (again from cocotb, not asyncio), and instead of fork(), use start_soon() (see https://docs.cocotb.org/en/v1.6.1/coroutines.html#concurrent-execution and https://www.fossi-foundation.org/2021/10/20/cocotb-1-6-0).

(I now see you also asked this in https://github.com/cocotb/cocotb/issues/2819)

cmarqu
  • 493
  • 3
  • 10
  • I'm not writing code from scratch. This project already has a lot of code. Due to historical reasons, I can't upgrade cocotb yet. In the above code, if I change `read_cb` function to`def read_cb(): dri = FakeDriver() ret = cocotb.fork(dri.read("0x22"))` there will be an error: `File "fake_driver.py", line 66, in read_cb ret = cocotb.fork(dri.read("0x22")) File "/home/pengfei/.local/lib/python3.6/site-packages/cocotb/__init__.py", line 135, in fork return scheduler.add(coro) AttributeError: 'NoneType' object has no attribute 'add'` Do you have any advice? – Just a little noob Dec 30 '21 at 03:14
  • 1
    The only way `scheduler` could be `None` is if you are not running an HDL simulator. cocotb *only* runs within an HDL simulator. – ktb Dec 30 '21 at 20:53
  • @ktb Thank you for your guidance. You're absolutely right. Since this is my first time using cocotb, I don't know this:-) – Just a little noob Dec 31 '21 at 04:22
  • @ktb Another question, if `read_cb` function is `def read_cb(): dri = FakeDriver() ret = cocotb.fork(dri.read("0x22"))`. How can I get the return value of the `dri.read("0x22")` function? The type of `ret` is `RunningCoroutine`, not a return value from `dri.read("0x22")`. – Just a little noob Dec 31 '21 at 07:01
  • `fork` runs `read` concurrently and independently, allowing the function calling `fork` to continue running. You must `yield dri.read("0x22")` if you want to wait until `read` finishes running, *or* you can `yield task` some time later. – ktb Dec 31 '21 at 07:12
  • But the `read_cb` function is not a coroutine.(it's a callback function from C code). It seems that I can not use `yield` or `await` in `read_cb` function. Is there any way to get the return value of `dri.read("0x22")` in `read_cb` ? – Just a little noob Dec 31 '21 at 07:27
  • Everything in the `read_cb` function you posted is incorrect. If you want to run a cocotb test in a simulator you will have to create a `@cocotb.test()` coroutine function, where you create your driver and do `yield dri.read()`. Please have a look at the examples in the repo. – ktb Dec 31 '21 at 07:41
  • In my case, I need to make a library(a wrapper of C code) instead of a cocotb test. The library will be used by cocotb tests. The functions in this library are ordinary Python functions, not coroutine. Functions in this library need to call the read/write operations in the driver in a blocking way. The driver read/write are implemented based on cocotb. I need this library to interact with this driver. Do you have any suggestions? I can think of the worst solution is to use socket. Is there a better way? – Just a little noob Dec 31 '21 at 08:22