Move "pending" set to C
authorMikolaj Wielgus <wielgusmikolaj@gmail.com>
Thu, 23 Dec 2021 00:39:04 +0000 (00:39 +0000)
committerMikolaj Wielgus <wielgusmikolaj@gmail.com>
Thu, 23 Dec 2021 00:39:04 +0000 (00:39 +0000)
Finally something works.

src/openpower/decoder/test/_pyrtl.py
src/openpower/decoder/test/pysim.py

index bb0a38bc9af75c3c8b654f3733fb0325024e8def..2a241fd02db43859edc5ca9761dc4f6010389ec6 100644 (file)
@@ -1,8 +1,4 @@
 import os
-import shutil
-import tempfile
-import importlib
-from cffi import FFI
 from contextlib import contextmanager
 
 from nmigen.hdl.ast import SignalSet
@@ -13,11 +9,10 @@ __all__ = ["PyRTLProcess"]
 
 
 class PyRTLProcess(BaseProcess):
-    __slots__ = ("is_comb", "runnable", "passive", "crtl")
+    __slots__ = ("is_comb", "runnable", "passive", "name", "filename", "crtl", "run")
 
     def __init__(self, *, is_comb):
         self.is_comb  = is_comb
-
         self.reset()
 
     def reset(self):
@@ -412,60 +407,6 @@ class _StatementCompiler(StatementVisitor, _Compiler):
             emitter.append(f"slots[{signal_index}].set(next_{signal_index})")
         return emitter.flush()
 
-code_includes = """\
-#include <stdint.h>
-"""
-
-code_cdef = """\
-typedef struct signal_t
-{
-    uint64_t curr;
-    uint64_t next;
-} signal_t;
-
-uint64_t capture(uint64_t id);
-uint64_t get_curr(uint64_t id);
-uint64_t get_next(uint64_t id);
-void run(void);
-"""
-
-code_header = code_includes
-code_header += "\n"
-code_header += code_cdef
-code_header += """
-signal_t slots[%d] =
-{
-"""
-
-code_footer = """\
-};
-
-uint64_t capture(uint64_t id)
-{
-    if (slots[id].curr == slots[id].next)
-        return 0;
-
-    slots[id].curr = slots[id].next;
-
-    return 1;
-}
-
-uint64_t get_curr(uint64_t id)
-{
-    return slots[id].curr;
-}
-
-uint64_t get_next(uint64_t id)
-{
-    return slots[id].next;
-}
-
-static void set(signal_t *slot, uint64_t value)
-{
-    slot->next = value;
-}
-"""
-
 
 class _FragmentCompiler:
     def __init__(self, state):
@@ -478,8 +419,10 @@ class _FragmentCompiler:
             domain_stmts = LHSGroupFilter(domain_signals)(fragment.statements)
             domain_process = PyRTLProcess(is_comb=domain_name is None)
 
+            domain_process.name = f"{id(fragment)}_{domain_name or ''}_{index}"
+
             emitter = _PythonEmitter()
-            emitter.append(f"void run(void)")
+            emitter.append(f"void run_{domain_process.name}(void)")
             with emitter.nest():
                 if domain_name is None:
                     for signal in domain_signals:
@@ -508,13 +451,10 @@ class _FragmentCompiler:
 
                 for signal in domain_signals:
                     signal_index = self.state.get_signal(signal)
-                    emitter.append(f"set(&slots[{signal_index}], next_{signal_index});")
+                    emitter.append(f"set({signal_index}, next_{signal_index});")
 
-            # create code header, slots, footer, followed by emit actual code
-            code = code_header % len(self.state.slots)
-            for slot in self.state.slots:
-                code += "    {%s, %s},\n" % (str(slot.signal.reset), str(slot.signal.reset))
-            code += code_footer
+            code = "#include <stdint.h>\n"
+            code += "#include \"common.h\"\n"
             code += emitter.flush()
 
             try:
@@ -522,22 +462,10 @@ class _FragmentCompiler:
             except FileExistsError:
                 pass
 
-            basename = f"{id(fragment)}_{domain_name or ''}_{index}"
-
-            file = open(f"crtl/{basename}.c", "w")
+            file = open(f"crtl/{domain_process.name}.c", "w")
             file.write(code)
             file.close()
 
-            ffibuilder = FFI()
-            ffibuilder.cdef(code_cdef)
-            ffibuilder.set_source(f"crtl._{basename}",
-                                  code_cdef,
-                                  sources=[f"crtl/{basename}.c"],
-                                  include_dirs=["/usr/include/python3.7m"])
-            ffibuilder.compile(verbose=True)
-
-            #domain_process.run = importlib.import_module(f"crtl._{basename}").lib.run
-            domain_process.crtl = importlib.import_module(f"crtl._{basename}").lib
             processes.add(domain_process)
 
         for subfragment_index, (subfragment, subfragment_name) in enumerate(fragment.subfragments):
index 9014ef8fa71cb4d65ae17706331f7e30dea4c038..914dd8e5a4f7f99e4af252b62d91a65e54f83f5c 100644 (file)
@@ -10,6 +10,8 @@ from openpower.decoder.test._pyrtl import _FragmentCompiler
 from nmigen.sim._pycoro import PyCoroProcess
 from nmigen.sim._pyclock import PyClockProcess
 
+import importlib
+from cffi import FFI
 
 __all__ = ["PySimEngine"]
 
@@ -200,57 +202,36 @@ class _Timeline:
 
 
 class _PySignalState:
-    __slots__ = ("signal", "waiters", "pending", "_crtl", "_id", "_curr", "_next")
+    __slots__ = ("signal", "waiters", "sim_state", "index")
 
-    def __init__(self, signal, pending, id, crtl):
+    def __init__(self, signal, index, sim_state):
         self.signal = signal
-        self.pending = pending
         self.waiters = dict()
-        self._id = id
-        self._crtl = crtl 
-
-        if self._crtl is None:
-            self._curr = self._next = signal.reset
+        self.index = index
+        self.sim_state = sim_state # Ugly. We just need it to have a reference to crtl.
 
     def set(self, value):
-        if self._crtl is not None:
-            # Shouldn't be called from Python if the signal is implemented through CRTL.
-            raise NotImplementedError
-
-        if self._next == value:
-            return
-
-        self._next = value
-        self.pending.add(self)
+        self.sim_state.crtl.set(self.index, value)
 
     def commit(self):
-        if self._crtl is not None:
-            if self._crtl.capture(self._id) == 0:
-                return False
-        else:
-            if self._curr == self._next:
-                return False
-            self._curr = self._next
+        if self.sim_state.crtl.capture(self.index) == 0:
+            return False
 
+        # Waiters are not implemented in C yet.
         awoken_any = False
         for process, trigger in self.waiters.items():
             if trigger is None or trigger == self.curr:
                 process.runnable = awoken_any = True
+
         return awoken_any
     
     @property
     def curr(self):
-        if self._crtl is not None:
-            return self._crtl.get_curr(self._id)
-        
-        return self._curr
+        return self.sim_state.crtl.get_curr(self.index)
 
     @property
     def next(self):
-        if self._crtl is not None:
-            return self._crtl.get_next(self._id)
-
-        return self._next
+        return self.sim_state.crtl.get_next(self.index)
 
 
 class _PySimulation:
@@ -259,7 +240,7 @@ class _PySimulation:
         self.signals  = SignalDict()
         self.slots    = []
         self.pending  = set()
-        self.crtl = None
+        self.crtl     = None # Initialized later.
 
     def reset(self):
         self.timeline.reset()
@@ -271,10 +252,10 @@ class _PySimulation:
         try:
             return self.signals[signal]
         except KeyError:
-            id = len(self.slots)
-            self.slots.append(_PySignalState(signal, self.pending, id, self.crtl))
-            self.signals[signal] = id
-            return id
+            index = len(self.slots)
+            self.slots.append(_PySignalState(signal, index, self))
+            self.signals[signal] = index
+            return index
 
     def add_trigger(self, process, signal, *, trigger=None):
         index = self.get_signal(signal)
@@ -292,14 +273,19 @@ class _PySimulation:
 
     def commit(self, changed=None):
         converged = True
-        for signal_state in self.pending:
+
+        for pending_index in range(self.crtl.pending_count):
+            index = self.crtl.pending[pending_index]
+            signal_state = self.slots[index]
+
             if signal_state.commit():
                 converged = False
-        if changed is not None:
-            changed.update(self.pending)
-        self.pending.clear()
-        return converged
 
+            if changed is not None:
+                changed.add(signal_state)
+
+        self.crtl.clear_pending()
+        return converged
 
 class PySimEngine(BaseEngine):
     def __init__(self, fragment):
@@ -308,6 +294,38 @@ class PySimEngine(BaseEngine):
 
         self._fragment = fragment
         self._processes = _FragmentCompiler(self._state)(self._fragment)
+
+        cdef_file = open("crtl_template.h")
+        cdef = cdef_file.read() % (len(self._state.slots), len(self._state.slots))
+        for process in self._processes:
+            cdef += f"void run_{process.name}(void);\n"
+        cdef_file.close()
+
+        cdef_file = open("crtl/common.h", "w")
+        cdef_file.write(cdef)
+        cdef_file.close()
+
+        src_file = open("crtl_template.c")
+        src = src_file.read() % (len(self._state.slots), len(self._state.slots))
+        src_file.close()
+
+        src_file = open("crtl/common.c", "w")
+        src_file.write(src)
+        src_file.close()
+
+        ffibuilder = FFI()
+        ffibuilder.cdef(cdef)
+        ffibuilder.set_source("crtl.crtl",
+                              cdef,
+                              sources=["crtl/common.c"]
+                                      + [f"crtl/{process.name}.c" for process in self._processes])
+        ffibuilder.compile(verbose=True)
+
+        self._state.crtl = importlib.import_module(f"crtl.crtl").lib
+        for process in self._processes:
+            process.crtl = self._state.crtl
+            process.run = getattr(process.crtl, f"run_{process.name}")
+
         self._vcd_writers = []
 
     def add_coroutine_process(self, process, *, default_cmd):
@@ -333,11 +351,7 @@ class PySimEngine(BaseEngine):
             for process in self._processes:
                 if process.runnable:
                     process.runnable = False
-
-                    if hasattr(process, "crtl"):
-                        process.crtl.run()
-                    else:
-                        process.run()
+                    process.run()
 
             # 2. commit: apply every queued signal change, waking up any waiting processes
             converged = self._state.commit(changed)