Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1621ceb65a72
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 52f36025a918
Choose a head ref
  • 1 commit
  • 2 files changed
  • 1 contributor

Commits on Sep 30, 2019

  1. rpc: add support for Yosys RPC protocol.

    whitequark committed Sep 30, 2019
    Copy the full SHA
    52f3602 View commit details
Showing with 117 additions and 0 deletions.
  1. +112 −0 nmigen/rpc.py
  2. +5 −0 setup.py
112 changes: 112 additions & 0 deletions nmigen/rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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()
5 changes: 5 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -23,6 +23,11 @@ def local_scheme(version):
setup_requires=["setuptools_scm"],
install_requires=["setuptools", "pyvcd>=0.1.4", "bitarray", "Jinja2"],
packages=find_packages(),
entry_points={
"console_scripts": [
"nmigen-rpc = nmigen.rpc:main",
]
},
project_urls={
#"Documentation": "https://nmigen.readthedocs.io/",
"Source Code": "https://github.com/m-labs/nmigen",