new file mode 100644
@@ -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')
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 <tgamblin@baylibre.com> --- meta/lib/oeqa/selftest/cases/riscvtune.py | 96 +++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+)