--- /dev/null
+import sys
+import json
+import argparse
+import importlib
+
+from .hdl import Signal, Elaboratable
+from .back import rtlil
+
+
+__all__ = ["main"]
+
+
+def _collect_modules(names):
+ modules = {}
+ for name in names:
+ py_module_name, py_class_name = name.rsplit(".", 1)
+ py_module = importlib.import_module(py_module_name)
+ if py_class_name == "*":
+ for py_class_name in py_module.__all__:
+ py_class = py_module.__dict__[py_class_name]
+ if not issubclass(py_class, Elaboratable):
+ continue
+ modules["{}.{}".format(py_module_name, py_class_name)] = py_class
+ else:
+ py_class = py_module.__dict__[py_class_name]
+ if not isinstance(py_class, type) or not issubclass(py_class, Elaboratable):
+ raise TypeError("{}.{} is not a class inheriting from Elaboratable"
+ .format(py_module_name, py_class_name))
+ modules[name] = py_class
+ return modules
+
+
+def _serve_yosys(modules):
+ while True:
+ request_json = sys.stdin.readline()
+ if not request_json: break
+ request = json.loads(request_json)
+
+ if request["method"] == "modules":
+ response = {"modules": list(modules.keys())}
+
+ elif request["method"] == "derive":
+ module_name = request["module"]
+
+ args, kwargs = [], {}
+ for parameter_name, parameter in request["parameters"].items():
+ if parameter["type"] == "unsigned":
+ parameter_value = int(parameter["value"], 2)
+ elif parameter["type"] == "signed":
+ width = len(parameter["value"])
+ parameter_value = int(parameter["value"], 2)
+ if parameter_value & (1 << (width - 1)):
+ parameter_value = -((1 << width) - value)
+ elif parameter["type"] == "string":
+ parameter_value = parameter["value"]
+ elif parameter["type"] == "real":
+ parameter_value = float(parameter["value"])
+ else:
+ raise NotImplementedError("Unrecognized parameter type {}"
+ .format(parameter_name))
+ if parameter_name.startswith("$"):
+ index = int(parameter_name[1:])
+ while len(args) < index:
+ args.append(None)
+ args[index] = parameter_value
+ if parameter_name.startswith("\\"):
+ kwargs[parameter_name[1:]] = parameter_value
+
+ try:
+ elaboratable = modules[module_name](*args, **kwargs)
+ def has_port(elaboratable, port_name):
+ # By convention, any public attribute that is a Signal is considered a port.
+ return (not port_name.startswith("_") and
+ isinstance(getattr(elaboratable, port_name), Signal))
+ ports = [getattr(elaboratable, port_name)
+ for port_name in dir(elaboratable)
+ if has_port(elaboratable, port_name)]
+ rtlil_text = rtlil.convert(elaboratable, name=module_name, ports=ports)
+ response = {"frontend": "ilang", "source": rtlil_text}
+ except Exception as error:
+ response = {"error": "{}: {}".format(type(error).__name__, str(error))}
+
+ else:
+ return {"error": "Unrecognized method {!r}".format(request["method"])}
+
+ sys.stdout.write(json.dumps(response))
+ sys.stdout.write("\n")
+ sys.stdout.flush()
+
+
+def main():
+ parser = argparse.ArgumentParser(description=r"""
+ The nMigen RPC server allows a HDL synthesis program to request an nMigen module to
+ be elaborated on demand using the parameters it provides. For example, using Yosys together
+ with the nMigen RPC server allows instantiating parametric nMigen modules directly
+ from Verilog.
+ """)
+ def add_modules_arg(parser):
+ parser.add_argument("modules", metavar="MODULE", type=str, nargs="+",
+ help="import and provide MODULES")
+ protocols = parser.add_subparsers(metavar="PROTOCOL", dest="protocol", required=True)
+ protocol_yosys = protocols.add_parser("yosys", help="use Yosys JSON-based RPC protocol")
+ add_modules_arg(protocol_yosys)
+
+ args = parser.parse_args()
+ modules = _collect_modules(args.modules)
+ if args.protocol == "yosys":
+ _serve_yosys(modules)
+
+
+if __name__ == "__main__":
+ main()