From 3bbe069bea6a44c520cabd8732b2b7595cb3a718 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Mon, 9 Dec 2024 11:37:46 +0200 Subject: [PATCH] gateware.platform: add support for using yowasp when yosys is not available --- luna/__init__.py | 9 ++++ luna/gateware/platform/__init__.py | 5 +- luna/gateware/platform/toolchain.py | 72 +++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 luna/gateware/platform/toolchain.py diff --git a/luna/__init__.py b/luna/__init__.py index 453b9d716..ca9e4e81d 100644 --- a/luna/__init__.py +++ b/luna/__init__.py @@ -12,6 +12,8 @@ from amaranth import Elaboratable from amaranth._unused import MustUse +from luna.gateware.platform import configure_toolchain + # Log formatting strings. LOG_FORMAT_COLOR = "\u001b[37;1m%(levelname)-8s| \u001b[0m\u001b[1m%(module)-12s|\u001b[0m %(message)s" LOG_FORMAT_PLAIN = "%(levelname)-8s:n%(module)-12s>%(message)s" @@ -108,8 +110,15 @@ def top_level_cli(fragment, *pos_args, **kwargs): join_text = "and uploading gateware to attached" if args.upload else "for" logging.info(f"Building {join_text} {platform.name}...") + # Configure toolchain. + if not configure_toolchain(platform): + logging.info(f"Failed to configure the toolchain for: {platform.toolchain}") + logging.info(f"Continuing anyway.") + # Now that we're actually building, re-enable Unused warnings. MustUse._MustUse__silence = False + + # Perform the build. products = platform.build(fragment, do_program=args.upload, build_dir=build_dir diff --git a/luna/gateware/platform/__init__.py b/luna/gateware/platform/__init__.py index de1fbc38b..ba1f3f5b1 100644 --- a/luna/gateware/platform/__init__.py +++ b/luna/gateware/platform/__init__.py @@ -16,6 +16,7 @@ from amaranth import Record from .core import NullPin, LUNAPlatform +from .toolchain import configure_toolchain def _get_platform_from_string(platform): @@ -63,11 +64,11 @@ def get_appropriate_platform() -> LUNAPlatform: "Unable to autodetect a supported platform. " "The LUNA_PLATFORM environment variable must be set.") platform = _get_platform_from_string(platform_string) - + # If possible, override the platform's device type with the detected FPGA. if fpga_device is not None: platform.device = fpga_device - + return platform diff --git a/luna/gateware/platform/toolchain.py b/luna/gateware/platform/toolchain.py new file mode 100644 index 000000000..ddf32d92e --- /dev/null +++ b/luna/gateware/platform/toolchain.py @@ -0,0 +1,72 @@ +# +# This file is part of LUNA. +# +# Copyright (c) 2020-2024 Great Scott Gadgets +# SPDX-License-Identifier: BSD-3-Clause + +""" Utilities for managing LUNA gateware toolchains. """ + +import importlib +import logging +import os +import shutil + +def configure_toolchain(platform): + """ Checks if all the required tools for the Yosys toolchain are available. + + If there are missing tools it will attempt to fall back to YoWASP instead. + + Returns: + True if a valid toolchain has been configured. Otherwise False. + """ + + if platform.has_required_tools(): + # All good, no further hanky-panky required. + return True + + # Do we have yowasp available to us? + logging.info(f"Failed to locate {platform.toolchain} toolchain, trying YoWASP.") + logging.debug("Checking for required tools:") + for tool in platform.required_tools: + logging.debug(f" {tool}") + + problems = 0 + + # Check whether yowasp-yosys is installed: + try: + import yowasp_yosys + logging.debug(f"Found module: {yowasp_yosys.__name__}") + except Exception as e: + problems += 1 + logging.warning(e) + + # Check whether yowasp-nextpnr- is installed: + try: + nextpnr = next(filter(lambda x: x.startswith("nextpnr-"), platform.required_tools)) + yowasp_nextpnr = importlib.import_module("yowasp_" + nextpnr.replace('-', '_')) + logging.debug(f"Found module: {yowasp_nextpnr.__name__}") + except Exception as e: + problems += 1 + logging.warning(e) + + # Check whether the YoWASP binaries are on the system PATH: + for tool in platform.required_tools: + env_var = tool.replace('-', '_').upper() + yowasp_tool = "yowasp-" + tool + path = shutil.which(yowasp_tool) + if not path: + problems += 1 + logging.warning(f"'{yowasp_tool}' is not on the system PATH.") + else: + logging.debug(f'Setting {env_var}="{yowasp_tool}"') + os.environ[env_var] = yowasp_tool + + if problems == 0: + logging.info("YoWASP configured successfully.") + else: + logging.info(f"{problems} problems encountered while configuring YoWASP.") + logging.info(f"You can install it with:") + logging.info(f" pip install yowasp-yosys yowasp-{nextpnr}") + return False + + return True