back.pysim: allow coroutines as processes.
authorwhitequark <whitequark@whitequark.org>
Wed, 21 Aug 2019 03:28:48 +0000 (03:28 +0000)
committerwhitequark <whitequark@whitequark.org>
Wed, 21 Aug 2019 03:30:37 +0000 (03:30 +0000)
This is a somewhat obscure use case, but it is possible to use async
functions with pysim by carefully using @asyncio.coroutine. That is,
async functions can call back into pysim if they are declared in
a specific way:

  @asyncio.coroutine
  def do_something(self, value):
    yield self.reg.eq(value)

which may then be called from elsewhere with:

  async def test_case(self):
    await do_something(0x1234)

This approach is unfortunately limited in that async functions
cannot yield directly. It should likely be improved by using async
generators, but supporting coroutines in pysim is unobtrustive and
allows existing code that made use of this feature in oMigen to work.

nmigen/back/pysim.py

index a89556fc9a8e21e6ddd3c49d5bd0401037216066..25189d7f60b688d5b34fc53a0dbfdc3c32176f60 100644 (file)
@@ -402,7 +402,7 @@ class Simulator:
     def _check_process(process):
         if inspect.isgeneratorfunction(process):
             process = process()
-        if not inspect.isgenerator(process):
+        if not (inspect.isgenerator(process) or inspect.iscoroutine(process)):
             raise TypeError("Cannot add a process '{!r}' because it is not a generator or "
                             "a generator function"
                             .format(process))
@@ -412,7 +412,10 @@ class Simulator:
         if process in self._process_loc:
             return self._process_loc[process]
         else:
-            frame = process.gi_frame
+            if inspect.isgenerator(process):
+                frame = process.gi_frame
+            if inspect.iscoroutine(process):
+                frame = process.cr_frame
             return "{}:{}".format(inspect.getfile(frame), inspect.getlineno(frame))
 
     def add_process(self, process):