Add a test suite to ensure that all RISC-V tunes (default or 'unusual')
yield valid pkgarch combinations and that PACKAGES_EXTRA_ARCHS lists
only compatible tunings.

AI-Generated: Uses Claude Opus 5.5

Signed-off-by: Trevor Gamblin <[email protected]>
---
 meta/lib/oeqa/selftest/cases/riscvtune.py | 96 +++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/meta/lib/oeqa/selftest/cases/riscvtune.py 
b/meta/lib/oeqa/selftest/cases/riscvtune.py
new file mode 100644
index 0000000000..2716b5bd02
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/riscvtune.py
@@ -0,0 +1,96 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+from oe.tune import riscv_implied, riscv_isa_to_tune
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars
+
+class RiscvTuneTests(OESelftestTestCase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        cls.orig_machine = get_bb_var('MACHINE')
+
+    def machine_config(self, machine):
+        # Builds set up by bitbake-setup select the machine with a builtin
+        # fragment, which is fatal alongside a MACHINE assignment, so drop it
+        return 'OE_FRAGMENTS:remove = "machine/%s"\nMACHINE = "%s"\n' % 
(self.orig_machine, machine)
+
+    def test_riscv_default_tunes(self):
+        """
+        Parse every RISC-V tune and check it yields a usable package arch, and
+        that PACKAGE_EXTRA_ARCHS only lists archs of other tunes whose
+        extensions are all available in this one.
+        """
+        self.write_config(self.machine_config('qemuriscv64'))
+        tunes = get_bb_var('AVAILTUNES').split()
+
+        vars = {}
+        for tune in tunes:
+            machine = 'qemuriscv32' if 'riscv32' in tune else 'qemuriscv64'
+            self.write_config(self.machine_config(machine) + 'DEFAULTTUNE = 
"%s"\n' % tune)
+            vars[tune] = get_bb_vars(['TUNE_FEATURES', 'TUNE_PKGARCH', 
'PACKAGE_EXTRA_ARCHS', 'PACKAGE_ARCHS'])
+
+        pkgarch_features = {v['TUNE_PKGARCH']: set(v['TUNE_FEATURES'].split()) 
for v in vars.values()}
+
+        for tune, v in vars.items():
+            with self.subTest(tune=tune):
+                self.assertTrue(v['TUNE_FEATURES'], 'empty TUNE_FEATURES')
+                self.assertTrue(v['TUNE_PKGARCH'], 'empty TUNE_PKGARCH')
+                self.assertIn(v['TUNE_PKGARCH'], v['PACKAGE_ARCHS'].split())
+                # sstate/package names get unwieldy past this
+                self.assertLessEqual(len(v['TUNE_PKGARCH']), 64, 
v['TUNE_PKGARCH'])
+
+                features = set(v['TUNE_FEATURES'].split())
+                features |= riscv_implied(features)
+                for arch in v['PACKAGE_EXTRA_ARCHS'].split():
+                    self.assertIn(arch, pkgarch_features, '%s is not the 
package arch of any tune' % arch)
+                    self.assertLessEqual(pkgarch_features[arch], features, '%s 
needs extensions %s lacks' % (arch, tune))
+
+        # Profile tunes are named after the profile
+        for profile in ['rva20u64', 'rva22u64', 'rva23u64']:
+            self.assertEqual(vars[profile]['TUNE_PKGARCH'], profile + 
'_zifencei')
+
+        # Legacy tune names behave exactly like the tunes they alias
+        for alias, target in [('riscv64', 'riscv64gc'), ('riscv32', 
'riscv32gc')]:
+            with self.subTest(alias=alias):
+                self.assertIn(alias, tunes)
+                self.assertEqual(vars[alias], vars[target])
+
+    def test_riscv_unusual_tunes(self):
+        """
+        Define custom tunes with unusual extension combinations, as a BSP
+        might, and check each one builds: the sanity checks accept the
+        package arch, gcc and binutils accept the -march string, and the
+        package arch fits in the sstate object names.
+        """
+        def profile(name, drop=None):
+            return ' '.join(f for f in riscv_isa_to_tune(name).split() if f != 
drop) + ' zifencei'
+        tunes = {
+            # no float (lp64 ABI), with bitmanip
+            'rv64-nofloat-bitmanip': 
riscv_isa_to_tune('rv64imac_zicsr_zifencei_zba_zbb_zbs'),
+            # no profile, scalar extensions on top of gc
+            'rv64gc-scalar-extras': 
riscv_isa_to_tune('rv64gc_zicbom_zicboz_zicond_zfa_zfhmin'),
+            # profile plus vector crypto extensions
+            'rva20-vector-crypto': profile('rva20u64') + ' v zvbb zvkt',
+            # a profile missing one extension, falling back to the previous one
+            'rva22-no-zkt': profile('rva22u64', 'zkt'),
+            # rva23u64 without the full vector extension
+            'rva23-no-v': profile('rva23u64', 'v'),
+        }
+
+        config = self.machine_config('qemuriscv64')
+        for tune, features in tunes.items():
+            config += 'AVAILTUNES += "%s"\n' % tune
+            config += 'TUNE_FEATURES:tune-%s = "%s"\n' % (tune, features)
+            config += 'PACKAGE_EXTRA_ARCHS:tune-%s = 
"${TUNE_RISCV_PKGARCH}"\n' % tune
+
+        for tune in tunes:
+            with self.subTest(tune=tune):
+                self.write_config(config + 'DEFAULTTUNE = "%s"\n' % tune)
+                # small, and compiled with TUNE_CCARGS. It has no do_build, and
+                # populate_sysroot also writes an sstate object.
+                bitbake('libgcc-initial -c populate_sysroot')

-- 
2.55.0

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#246999): 
https://lists.openembedded.org/g/openembedded-core/message/246999
Mute This Topic: https://lists.openembedded.org/mt/121511810/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to