From 3cb5f63aba974fce55519b04efdd8f84fb6d7dcc Mon Sep 17 00:00:00 2001 From: Catherine Date: Sun, 11 Feb 2024 17:24:23 +0000 Subject: [PATCH] _toolchain.yosys: add JavaScript (Pyodide) support. In this environment it's not feasible, or at least it's not documented how, to distribute JavaScript code by packaging it as a wheel; only Wasm code (as shared objects) can be distributed this way. The current `amaranth-yosys` strategy would not work even though wheels can be installed on Pyodide, and Yosys will need to be explicitly provided by the environment instead. The implementation is sufficiently generic that non-Pyodide hosts could potentially make use of it, though it doesn't seem like any exist at the moment. --- amaranth/_toolchain/yosys.py | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/amaranth/_toolchain/yosys.py b/amaranth/_toolchain/yosys.py index eacac92..1c29f66 100644 --- a/amaranth/_toolchain/yosys.py +++ b/amaranth/_toolchain/yosys.py @@ -177,6 +177,47 @@ class _SystemYosys(YosysBinary): return cls._process_result(popen.returncode, stdout, stderr, ignore_warnings, src_loc_at) +class _JavaScriptYosys(YosysBinary): + """ + This toolchain proxy is compatible with Pyodide_. The JavaScript environment must include + the following function: + + .. code:: + + runAmaranthYosys(args: string[], stdin: string): (exit_code: int, stdout: string, stderr: string); + + .. _Pyodide: https://pyodide.org/ + """ + + @classmethod + def available(cls): + try: + return hasattr(__import__("js"), "runAmaranthYosys") + except ImportError: + return False + + @classmethod + def version(cls): + version = cls.run(["-V"]) + match = re.match(r"^Yosys (\d+)\.(\d+)(?:\+(\d+))?", version) + if match: + return (int(match[1]), int(match[2]), int(match[3] or 0)) + else: + return None + + @classmethod + def data_dir(cls): + # Not yet clear how this could work in a design with Wasm components. Most likely, + # the component would have to export its filesystem wholesale, and this method would + # return some kind of non-filesystem path-like object. + raise NotImplementedError + + @classmethod + def run(cls, args, stdin="", *, ignore_warnings=False, src_loc_at=0): + exit_code, stdout, stderr = __import__("js").runAmaranthYosys(args, stdin) + return cls._process_result(exit_code, stdout, stderr, ignore_warnings, src_loc_at) + + def find_yosys(requirement): """Find an available Yosys executable of required version. @@ -202,6 +243,8 @@ def find_yosys(requirement): proxies.append(_BuiltinYosys) elif clause == "system": proxies.append(_SystemYosys) + elif clause == "javascript": + proxies.append(_JavaScriptYosys) else: raise YosysError("The AMARANTH_USE_YOSYS environment variable contains " "an unrecognized clause {!r}"