From 7a1e1f466f0dab53b87bd0d94b5a88be060216e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20B=C3=A5nvik?= Date: Tue, 17 Dec 2024 09:50:00 +0100 Subject: [PATCH] Implement warning for non-deterministic selection of virtual cores --- fusesoc/coremanager.py | 22 +++++ tests/capi2_cores/virtual/top_conflict.core | 22 +++++ .../virtual/top_non_deterministic.core | 19 ++++ tests/test_coremanager.py | 87 +++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 tests/capi2_cores/virtual/top_conflict.core create mode 100644 tests/capi2_cores/virtual/top_non_deterministic.core diff --git a/fusesoc/coremanager.py b/fusesoc/coremanager.py index 2f82b8ff..4666d306 100644 --- a/fusesoc/coremanager.py +++ b/fusesoc/coremanager.py @@ -225,14 +225,36 @@ def eq_vln(this, that): except NoPackageFound as e: raise DependencyError(top_core.name) + virtual_selection = {} objdict = {} if len(transaction.operations) > 1: for op in transaction.operations: + package_name = self._package_name(op.package.core.name) + virtuals = op.package.core.get_virtuals(_flags) for p in op.package.provides: + for virtual in virtuals: + if p[0] == self._package_name(virtual): + # Add virtual core selection to dictionary + virtual_selection[package_name] = ( + op.package.core.name, + virtual, + ) objdict[p[0]] = str(op.package.core.name) + for p in op.package.install_requires: + if p[0] in virtual_selection: + # If package that implements a virtual core is required, remove from the dictionary + del virtual_selection[p[0]] op.package.core.direct_deps = [ objdict[n[0]] for n in op.package.install_requires ] + # Print a warning for all virtual selections that has no concrete requirement selection + for virtual in virtual_selection.values(): + logger.warning( + "Non-deterministic selection of virtual core {} selected {}".format( + virtual[1], virtual[0] + ) + ) + result = [op.package.core for op in transaction.operations] # Cache the solution for further lookups diff --git a/tests/capi2_cores/virtual/top_conflict.core b/tests/capi2_cores/virtual/top_conflict.core new file mode 100644 index 00000000..bf49d8e6 --- /dev/null +++ b/tests/capi2_cores/virtual/top_conflict.core @@ -0,0 +1,22 @@ +CAPI=2: +# Copyright FuseSoC contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +name: ::top_conflict:0 +filesets: + rtl: + depend: + - ::user:0 + - ::impl1:0 + - ::impl2:0 + files: + - top_conflict.sv + file_type: systemVerilogSource + + +targets: + default: + filesets: + - rtl + toplevel: top_impl2 diff --git a/tests/capi2_cores/virtual/top_non_deterministic.core b/tests/capi2_cores/virtual/top_non_deterministic.core new file mode 100644 index 00000000..27b6c8d0 --- /dev/null +++ b/tests/capi2_cores/virtual/top_non_deterministic.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright FuseSoC contributors +# Licensed under the 2-Clause BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-2-Clause + +name: ::top_non_deterministic:0 +filesets: + rtl: + depend: + - ::user:0 + files: + - top_impl1.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - rtl + toplevel: top_impl1 diff --git a/tests/test_coremanager.py b/tests/test_coremanager.py index bc0db591..679371fb 100644 --- a/tests/test_coremanager.py +++ b/tests/test_coremanager.py @@ -278,3 +278,90 @@ def test_virtual(): deps_names = [str(c) for c in deps] assert deps_names == expected_deps + + +def test_virtual_conflict(): + """ + Test virtual core selection when there are more than one selected implementation. + This shall result in a conflict of cores. + """ + import os + import tempfile + + from fusesoc.config import Config + from fusesoc.coremanager import CoreManager, DependencyError + from fusesoc.edalizer import Edalizer + from fusesoc.librarymanager import Library + from fusesoc.vlnv import Vlnv + + flags = {"tool": "icarus"} + + build_root = tempfile.mkdtemp(prefix="export_") + work_root = os.path.join(build_root, "work") + + core_dir = os.path.join(os.path.dirname(__file__), "capi2_cores", "virtual") + + cm = CoreManager(Config()) + cm.add_library(Library("virtual", core_dir), []) + + root_core = cm.get_core(Vlnv("::top_conflict")) + + edalizer = Edalizer( + toplevel=root_core.name, + flags=flags, + core_manager=cm, + work_root=work_root, + ) + with pytest.raises(DependencyError) as _: + edalizer.run() + + +def test_virtual_non_deterministic_virtual(caplog): + """ + Test virtual core selection when there are no selected implementations. + This shall result in a warning that the virtual core selection is non-deteministic. + """ + import logging + import os + import tempfile + + from fusesoc.config import Config + from fusesoc.coremanager import CoreManager + from fusesoc.edalizer import Edalizer + from fusesoc.librarymanager import Library + from fusesoc.vlnv import Vlnv + + flags = {"tool": "icarus"} + + build_root = tempfile.mkdtemp(prefix="export_") + work_root = os.path.join(build_root, "work") + + core_dir = os.path.join(os.path.dirname(__file__), "capi2_cores", "virtual") + + cm = CoreManager(Config()) + cm.add_library(Library("virtual", core_dir), []) + + root_core = cm.get_core(Vlnv("::top_non_deterministic")) + + edalizer = Edalizer( + toplevel=root_core.name, + flags=flags, + core_manager=cm, + work_root=work_root, + ) + edalizer.run() + + with caplog.at_level(logging.WARNING): + edalizer.run() + assert "Non-deterministic selection of virtual core" in caplog.text + + deps = cm.get_depends(root_core.name, {}) + deps_names = [str(c) for c in deps] + + for dependency in deps_names: + assert dependency in [ + "::impl1:0", + "::impl2:0", + "::user:0", + "::top_non_deterministic:0", + ]