class PyRTLProcess(BaseProcess):
- __slots__ = ("is_comb", "runnable", "passive", "name", "filename", "crtl", "run")
+ __slots__ = ("is_comb", "runnable", "passive", "name", "crtl", "run")
def __init__(self, *, is_comb):
self.is_comb = is_comb
if self.inputs is not None:
self.inputs.add(value)
+ macro = self.state.get_signal_macro(value)
if self.mode == "curr":
- return f"slots[{self.state.get_signal(value)}].{self.mode}"
+ return f"slots[{macro}].{self.mode}"
else:
- return f"next_{self.state.get_signal(value)}"
+ return f"next_{macro}"
def on_Operator(self, value):
def mask(value):
def gen(arg):
value_mask = (1 << len(value)) - 1
- name = ''
- # TODO: useful trick, actually put the name into the c code
- # but this has to be done consistently right across the board.
- # all occurrences of next_{....} have to use the same trick
- # but at least then the names in the auto-generated c-code
- # are readable...
- #if hasattr(value, "name") and value.name is not None:
- # name = value.name
if value.shape().signed:
value_sign = f"sign({value_mask} & {arg}, {-1 << (len(value) - 1)})"
else: # unsigned
value_sign = f"{value_mask} & {arg}"
- self.emitter.append(f"next_{name}{self.state.get_signal(value)} = {value_sign};")
+
+ macro = self.state.get_signal_macro(value)
+ self.emitter.append(f"next_{macro} = {value_sign};")
return gen
def on_Operator(self, value):
@classmethod
def compile(cls, state, stmt):
- output_indexes = [state.get_signal(signal) for signal in stmt._lhs_signals()]
+ output_macros = \
+ [state.get_signal_macro(signal) for signal in stmt._lhs_signals()]
emitter = _PythonEmitter()
- for signal_index in output_indexes:
- emitter.append(f"uint64_t next_{signal_index} = slots[{signal_index}].next")
+ for macro in output_macros:
+ emitter.append(f"uint64_t next_{macro} = slots[{macro}].next")
compiler = cls(state, emitter)
compiler(stmt)
- for signal_index in output_indexes:
- emitter.append(f"slots[{signal_index}].set(next_{signal_index})")
+ for macro in output_macros:
+ emitter.append(f"set({macro}, next_{macro})")
return emitter.flush()
def __init__(self, state):
self.state = state
- def __call__(self, fragment):
+ def __call__(self, fragment, fragment_name):
processes = set()
for index, (domain_name, domain_signals) in enumerate(fragment.drivers.items()):
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}"
+ domain_process.name = \
+ f"{fragment_name}__{domain_name or ''}" \
+ f"_{id(fragment)}_{index}"
emitter = _PythonEmitter()
emitter.append(f"void run_{domain_process.name}(void)")
with emitter.nest():
if domain_name is None:
for signal in domain_signals:
- signal_index = self.state.get_signal(signal)
- emitter.append(f"uint64_t next_{signal_index} = {signal.reset};")
+ macro = self.state.get_signal_macro(signal)
+ emitter.append(
+ f"uint64_t next_{macro} = {signal.reset};")
inputs = SignalSet()
_StatementCompiler(self.state, emitter, inputs=inputs)(domain_stmts)
self.state.add_trigger(domain_process, domain.rst, trigger=rst_trigger)
for signal in domain_signals:
- signal_index = self.state.get_signal(signal)
- emitter.append(f"uint64_t next_{signal_index} = slots[{signal_index}].next;")
+ macro = self.state.get_signal_macro(signal)
+ emitter.append(
+ f"uint64_t next_{macro} = slots[{macro}].next;")
_StatementCompiler(self.state, emitter)(domain_stmts)
for signal in domain_signals:
- signal_index = self.state.get_signal(signal)
- emitter.append(f"set({signal_index}, next_{signal_index});")
+ macro = self.state.get_signal_macro(signal)
+ emitter.append(f"set({macro}, next_{macro});")
code = "#include <stdint.h>\n"
code += "#include \"common.h\"\n"
for subfragment_index, (subfragment, subfragment_name) in enumerate(fragment.subfragments):
if subfragment_name is None:
subfragment_name = "U${}".format(subfragment_index)
- processes.update(self(subfragment))
+ processes.update(self(subfragment, subfragment_name))
return processes
from nmigen.sim._pyclock import PyClockProcess
import os
+import re
import sys
import shutil
import importlib
+
from cffi import FFI
from os.path import dirname, join
from collections import namedtuple
self.slots.append(_PySignalState(signal, index, self))
self.signals[signal] = index
return index
+
+ def get_signal_macro(self, signal):
+ index = self.get_signal(signal)
+
+ # Replace dots with double underscores, and change everything else that
+ # cannot be put in a C macro identifier to a single underscore.
+ sanitized_name = re.sub(r"\W|^(?=\d)", "_", signal.name)
+
+ return f"{sanitized_name}_{index}"
def add_trigger(self, process, signal, *, trigger=None):
index = self.get_signal(signal)
class PySimEngine(BaseEngine):
- _crtl_counter = 1
+ _crtl_counter = 0
def __init__(self, fragment):
self._state = _PySimulation()
# blow away and recreate crtl subdirectory. (hope like hell
# nobody is using this in their current working directory)
- if PySimEngine._crtl_counter == 1:
+ if PySimEngine._crtl_counter == 0:
shutil.rmtree("crtl", True)
try:
os.mkdir("crtl")
# "Processes" are the compiled modules. Each module ends up
# with its own run() function
- self._processes = _FragmentCompiler(self._state)(self._fragment)
+ self._processes = _FragmentCompiler(self._state)(self._fragment, "TOP")
# get absolute path of this directory as the base
filedir = os.path.dirname(os.path.abspath(__file__))
# fill in the template
cdef = template % (len(self._state.slots), len(self._state.slots))
+
+ # macros to make signals readable
+ cdef += "\n"
+ for signal in self._state.signals:
+ macro = self._state.get_signal_macro(signal)
+ index = self._state.get_signal(signal)
+ cdef += f"#define {macro} {index}\n"
+
+ # run() functions of processes
+ cdef += "\n"
for process in self._processes:
cdef += f"void run_{process.name}(void);\n"