riscv tunes: ISA Implementation of RISC-V tune features

This implements the following base ISAs:

* rv32i, rv64i
* rv32e, rv64i

The following ABIs:
* ilp32, ilp32e, ilp32f, ilp32d
* lp64, lp64e, lp64f, lp64d

The following ISA extension are also implemented:
* M - Integer Multiplication and Division Extension
* A - Atomic Memory Extension
* F - Single-Precision Floating-Point Extension
* D - Double-Precision Floating-Point Extension
* C - Compressed Extension
* B - Bit Manipulation Extension (implies Zba, Zbb, Zbs)
* V - Vector Operations Extension
* Zicsr - Control and Status Register Access Extension
* Zifencei - Instruction-Fetch Fence Extension
* Zba - Address bit manipulation extension
* Zbb - Basic bit manipulation extension
* Zbc - Carry-less multiplication extension
* Zbs - Single-bit manipulation extension
* Zicbom - Cache-block management extension

The existing processors tunes are preserved:
* riscv64 (rv64gc)
* riscv32 (rv32gc)
* riscv64nf (rv64imac_zicsr_zifencei)
* riscv32nf (rv32imac_zicsr_zifencei)
* riscv64nc (rv64imafd_zicsr_zifencei)

Previously defined feature 'big-endian' has been removed as it was not used.

(From OE-Core rev: bcaf298a146dfd10e4c8f44223ea083bc4baf45c)

Signed-off-by: Mark Hatle <mark.hatle@amd.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Mark Hatle 2025-06-17 18:39:38 -05:00 committed by Richard Purdie
parent 63fb85ec37
commit 3c5c4cfa6b
6 changed files with 349 additions and 38 deletions

View File

@ -0,0 +1,122 @@
2025/06/08 - Mark Hatle <mark.hatle@amd.com>
- Initial Revision
The RISC-V ISA is broken into two parts, a base ISA and extensions. As
of the writing of this document these are documented at:
https://lf-riscv.atlassian.net/wiki/spaces/HOME/pages/16154769/RISC-V+Technical+Specifications
Specifically "The RISC-V Instruction Set Manual Volume I: Unprivileged ISA"
was used to create this implementation.
Requirements
------------
As RISC-V is a “variable” ISA (a base isa plus numerous extensions), a
mechanism is required to specify a series of ISA features that a user or
tune can use to specify a specific CPU instantiation.
Not all ratified or draft features should or can be implemented with the
available resources.
The implementation should work for Linux, baremetal (newlib), zephyr and
other operating systems. Supported extensions should be based on
real-world examples.
Linux
-----
Linux required base and support extensions should be available. Linux
requires:
* Base: rv32ima & rv64ima
* Optional FPU: fd
* Optional RISCV_ISA_C: c
* Optiona RISCV_ISA_V: v
* Required additional: _zicsr_zifencei
* Optional RISCV_ISA_ZBA: _zba
* Optional RISCV_ISA_ZBB: _zbb
* Optional RISCV_ISA_ZBC: _zbc (not supported by current QEMU design)
See: https://git.yoctoproject.org/linux-yocto/tree/arch/riscv/Makefile?h=v6.12/base
Baremetal
---------
AMD Microblaze-V FPGA support uses the following static configurations:
Base: rv32e, rv32i, rv64i
Extensions: m, a, f, d, c, b, zicsr, zifencei
Zephyr
------
AMD Microblaze-V development for Zephyr is the same as Baremetal, with a
few additional extensions: zbc, zicbom
ABI
---
The following ABIs are supported GNU tools and some combination of systems.
* ilp32 - Integer, long and pointer are 32-bit
* lp64 - Long and pointer are 64-bit (integer is 32-bit)
The ABI is dependent upon the core system implementation, as ilp32 can
only used on an rv32 system, while lp64 can only be used on an rv64
system.
There are additional variations of each ABI:
* e - used with the Reduced register extension
* f - used when single precision floating point (but not double precision) is
enabled
* d - used when both single and double precision floating point is enabled
Based on the above, the ABI should be automatically determined based on
the selected Base ISA and Extensions.
Implementation
--------------
To make it easier to generate the RISC-V canonical arch, ISA based -march,
and the ABI string, a few new variables are added for specific RISC-V items.
TUNE_RISCV_ARCH - This contains the canonical GNU style arch, generally this
will evaluate to "riscv32" or "riscv64".
TUNE_RISCV_MARCH - This will contain an ISA based -march string compatible
with gcc and similar toolchains. For example:
rv32imacfd_zicsr_zifencei
TUNE_RISCV_ABI - This is the generated ABI that corresponds to the ARCH and
MARCH/ISA values. For riscv32, the value will be ilp32
(int, long and pointer is 32-bit) with the ISA
variation. For riscv64, the value will be lp64 (long
and pointer are 64-bit bit, while int is 32-bit) with the
ISA variation. The ISA affects the ABI when the 'e', 'f'
and 'd' extension are used.
TUNE_RISCV_PKGARCH - This is the generated PKGARCH value.
The standard variables are defined as:
TUNE_CCARGS = "${@ '-march=${TUNE_RISCV_MARCH} -mabi=${TUNE_RISCV_ABI}' if not d.getVar('TUNE_CCARGS:tune-${DEFAULTTUNE}') else 'TUNE_CCARGS:tune-${DEFAULTTUNE}'}"
The above will allow the user to specify an implementation specific
TUNE_CCARGS for a given processor tune if the default implementtion is
not adequate for some reason. It is expected that most, if not all,
implementations will use the default behavior.
TUNE_ARCH = "${TUNE_RISCV_ARCH}"
TUNE_PKGARCH = "${TUNE_RISCV_PKGARCH}"
The above two will always base their setting off the standard TUNE_FEATURES.
Ratified and draft extensions should be implemented as TUNE_FEATURES in
the arch-riscv.inc file.
Vendor specific extensions and processor specific settings should go
into a 'tune-<vendor>.inc' file, with tune-riscv.inc being reserved for
general purpose tunes.
TUNE_FEATURE Helper
-------------------
A special helper function has been written that will convert RISC-V ISA
notation into TUNE_FEATURE notion, for example:
rv32g -> rv 32 i m a f d zicsr zifencei
The helper can be called using oe.tune.riscv_isa_to_tune("<ISA>") such as
oe.tune.riscv_isa_to_tune("rv64gc") which would return:
rv 64 i m a f d c zicsr zifencei

View File

@ -1,14 +1,140 @@
# RISCV Architecture definition
DEFAULTTUNE ?= "riscv64"
# Based on the RISC-V Instruction Set Manual Volume I: Unprivileged ISA from May 2025
# As well as the RISC-V options for using GCC (as of June 2025)
TUNE_ARCH = "${TUNE_ARCH:tune-${DEFAULTTUNE}}"
TUNE_PKGARCH = "${TUNE_PKGARCH:tune-${DEFAULTTUNE}}"
TUNE_CCARGS:append = "${@bb.utils.contains('TUNE_FEATURES', 'riscv64nf', ' -mabi=lp64', ' ', d)}"
TUNE_CCARGS:append = "${@bb.utils.contains('TUNE_FEATURES', 'riscv32nf', ' -mabi=ilp32', ' ', d)}"
# Note: the following should be implemented in the order that GCC expects
# -march= values to be defined in.
TUNE_CCARGS:append = "${@bb.utils.contains('TUNE_FEATURES', 'riscv64nc', ' -march=rv64imafd', ' ', d)}"
# Base ISA
# All supported march strings must start with rv32 or rv64
TUNEVALID[rv] = "RISC-V"
TUNE_RISCV_ARCH = "${@bb.utils.contains("TUNE_FEATURES", "rv", "riscv", "", d)}"
TUNE_RISCV_MARCH = "${@bb.utils.contains("TUNE_FEATURES", "rv", "rv", "", d)}"
TUNE_RISCV_ABI = ""
# There are two primary ABIs, ilp32 and lp64
# There are variants of both, that appears to be based on extensions above
# For example:
# rv32i uses ilp32, rv32e uses ilp32e, rv32f uses ilp32f
# rv64i uses lp64, rv64if uses lp64f, rv64id uses lp64d
TUNEVALID[32] = "ISA XLEN - 32-bit"
TUNECONFLICTS[32] = "64"
TUNE_RISCV_ARCH .= "${@bb.utils.contains("TUNE_FEATURES", "32", "32", "", d)}"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "32", "32", "", d)}"
TUNE_RISCV_ABI .= "${@bb.utils.contains("TUNE_FEATURES", "32", "ilp32", "", d)}"
TUNEVALID[64] = "ISA XLEN - 64-bit"
TUNECONFLICTS[64] = "32"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "64", "64", "", d)}"
TUNE_RISCV_ARCH .= "${@bb.utils.contains("TUNE_FEATURES", "64", "64", "", d)}"
TUNE_RISCV_ABI .= "${@bb.utils.contains("TUNE_FEATURES", "64", "lp64", "", d)}"
# The package arch starts with the canonical arch, but adds some extensions to make
# package compatibility clear
TUNE_RISCV_PKGARCH = "${TUNE_RISCV_ARCH}"
# i, e, or g are defined by gcc, but 'g' refers to 'i' + extensions 'MAFD Zicsr Zifencei'
# So 'g' will not be defined here as it is an abbreviation of the expanded version
TUNEVALID[e] = "Reduced register base integer extension"
TUNECONFLICTS[e] = "i"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "e", "e", "", d)}"
TUNE_RISCV_ABI .= "${@bb.utils.contains("TUNE_FEATURES", "e", "e", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "e", "e", "", d)}"
TUNEVALID[i] = "Base integer extension"
TUNECONFLICTS[i] = "e"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "i", "i", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "i", "i", "", d)}"
# Extensions
TUNEVALID[m] = "Integer multiplication and division extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "m", "m", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "m", "m", "", d)}"
TUNEVALID[a] = "Atomic extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "a", "a", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "a", "a", "", d)}"
TUNEVALID[f] = "Single-precision floating-point extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "f d", "f", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "f d", "f", "", d)}"
TUNEVALID[d] = "Double-precision floating-point extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "d", "d", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "d", "d", "", d)}"
# Only f OR d, but just one
TUNE_RISCV_ABI .= "${@bb.utils.contains("TUNE_FEATURES", "d", "d", bb.utils.contains("TUNE_FEATURES", "f", "f", "", d), d)}"
TUNEVALID[c] = "Compressed extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "c", "c", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "c", "c", "", d)}"
TUNEVALID[b] = "Bit Manipulation extension"
# Handled below via zba, zbb, zbs
# This matches current Linux kernel behavior
#TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "b", "b", "", d)}"
#TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "b", "b", "", d)}"
TUNEVALID[v] = "Vector operations extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "v", "v", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "v", "v", "", d)}"
# Now the special Z extensions
TUNEVALID[zicbom] = "Cache-block management extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "zicbom", "_zicbom", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "zicbom", "_zicbom", "", d)}"
TUNEVALID[zicsr] = "Control and status register access extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "zicsr f d", "_zicsr", "", d)}"
# If zicsr (or zifencei) is in the path, OpenSBI fails to use the extensions, do to (Makefile):
# # Check whether the assembler and the compiler support the Zicsr and Zifencei extensions
# CC_SUPPORT_ZICSR_ZIFENCEI := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) -nostdlib -march=rv$(OPENSBI_CC_XLEN)imafd_zicsr_zifencei -x c /dev/null -o /dev/null 2>&1 | grep -e "zicsr" -e "zifencei" > /dev/null && echo n || echo y)
# this will match on the path containing zicsr or zifencei when an error is reported, which
# will always happens in this check.
#
# Yocto Project Bugzilla 15897
#
#TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "zicsr f d", "_zicsr", "", d)}"
TUNEVALID[zifencei] = "Instruction-fetch fence extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains("TUNE_FEATURES", "zifencei", "_zifencei", "", d)}"
# See above Bug 15897
#TUNE_RISCV_PKGARCH .= "${@bb.utils.contains("TUNE_FEATURES", "zifencei", "_zifencei", "", d)}"
TUNEVALID[zba] = "Address bit manipulation extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "b zba", "_zba", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "b zba", "_zba", "", d)}"
TUNEVALID[zbb] = "Basic bit manipulation extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "b zbb", "_zbb", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "b zbb", "_zbb", "", d)}"
TUNEVALID[zbc] = "Carry-less multiplication extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "zbc", "_zbc", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "zbc", "_zbc", "", d)}"
TUNEVALID[zbs] = "Single-bit manipulation extension"
TUNE_RISCV_MARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "b zbs", "_zbs", "", d)}"
TUNE_RISCV_PKGARCH .= "${@bb.utils.contains_any("TUNE_FEATURES", "b zbs", "_zbs", "", d)}"
# Construct TUNE_CCARGS
# This should result in a CCARG similar to:
# -march=rv32imac -mabi=ilp32
TUNE_CCARGS = "${@ '-march=${TUNE_RISCV_MARCH} -mabi=${TUNE_RISCV_ABI}' if not d.getVar('TUNE_CCARGS:tune-${DEFAULTTUNE}') else 'TUNE_CCARGS:tune-${DEFAULTTUNE}'}"
# Construct TUNE_ARCH
# This should result in an arch string similar to:
# riscv32
TUNE_ARCH = "${TUNE_RISCV_ARCH}"
# Construct TUNE_PKGARCH
# This should result in a package are like:
# riscv32imac
TUNE_PKGARCH = "${TUNE_RISCV_PKGARCH}"
# Misc settings
# Fix: ld: unrecognized option '--hash-style=sysv'
LINKER_HASH_STYLE:libc-newlib = ""
LINKER_HASH_STYLE:libc-picolibc = ""

View File

@ -1,41 +1,23 @@
require conf/machine/include/riscv/arch-riscv.inc
TUNEVALID[riscv64] = "Enable 64-bit RISC-V optimizations"
TUNEVALID[riscv32] = "Enable 32-bit RISC-V optimizations"
TUNEVALID[riscv64nf] = "Enable 64-bit RISC-V optimizations no floating point"
TUNEVALID[riscv32nf] = "Enable 32-bit RISC-V optimizations no floating point"
TUNEVALID[riscv64nc] = "Enable 64-bit RISC-V optimizations without compressed instructions"
TUNEVALID[bigendian] = "Big endian mode"
DEFAULTTUNE ?= "riscv64"
AVAILTUNES += "riscv64 riscv32 riscv64nc riscv64nf riscv32nf"
# Default
TUNE_FEATURES:tune-riscv64 = "riscv64"
TUNE_ARCH:tune-riscv64 = "riscv64"
TUNE_PKGARCH:tune-riscv64 = "riscv64"
PACKAGE_EXTRA_ARCHS:tune-riscv64 = "riscv64"
TUNE_FEATURES:tune-riscv64 := "${@oe.tune.riscv_isa_to_tune("rv64gc")}"
PACKAGE_EXTRA_ARCHS:tune-riscv64 = "${TUNE_RISCV_PKGARCH}"
TUNE_FEATURES:tune-riscv32 = "riscv32"
TUNE_ARCH:tune-riscv32 = "riscv32"
TUNE_PKGARCH:tune-riscv32 = "riscv32"
PACKAGE_EXTRA_ARCHS:tune-riscv32 = "riscv32"
TUNE_FEATURES:tune-riscv32 := "${@oe.tune.riscv_isa_to_tune("rv32gc")}"
PACKAGE_EXTRA_ARCHS:tune-riscv32 = "${TUNE_RISCV_PKGARCH}"
# No float
TUNE_FEATURES:tune-riscv64nf = "${TUNE_FEATURES:tune-riscv64} riscv64nf"
TUNE_ARCH:tune-riscv64nf = "riscv64"
TUNE_PKGARCH:tune-riscv64nf = "riscv64nf"
PACKAGE_EXTRA_ARCHS:tune-riscv64nf = "riscv64nf"
TUNE_FEATURES:tune-riscv64nf := "${@oe.tune.riscv_isa_to_tune("rv64imac_zicsr_zifencei")}"
PACKAGE_EXTRA_ARCHS:tune-riscv64nf = "${TUNE_RISCV_PKGARCH}"
TUNE_FEATURES:tune-riscv32nf = "${TUNE_FEATURES:tune-riscv32} riscv32nf"
TUNE_ARCH:tune-riscv32nf = "riscv32"
TUNE_PKGARCH:tune-riscv32nf = "riscv32nf"
PACKAGE_EXTRA_ARCHS:tune-riscv32nf = "riscv32nf"
TUNE_FEATURES:tune-riscv32nf := "${@oe.tune.riscv_isa_to_tune("rv32imac_zicsr_zifencei")}"
PACKAGE_EXTRA_ARCHS:tune-riscv32nf = "${TUNE_RISCV_PKGARCH}"
# no compressed
TUNE_FEATURES:tune-riscv64nc = "${TUNE_FEATURES:tune-riscv64} riscv64nc"
TUNE_ARCH:tune-riscv64nc = "riscv64"
TUNE_PKGARCH:tune-riscv64nc = "riscv64nc"
PACKAGE_EXTRA_ARCHS:tune-riscv64nc = "riscv64nc"
TUNE_FEATURES:tune-riscv64nc := "${@oe.tune.riscv_isa_to_tune("rv64imafd_zicsr_zifencei")}"
PACKAGE_EXTRA_ARCHS:tune-riscv64nc = "${TUNE_RISCV_PKGARCH}"

View File

@ -2,9 +2,9 @@
#@NAME: generic riscv32 machine
#@DESCRIPTION: Machine configuration for running a generic riscv32
require conf/machine/include/riscv/qemuriscv.inc
DEFAULTTUNE ?= "riscv32"
DEFAULTTUNE = "riscv32"
require conf/machine/include/riscv/qemuriscv.inc
PREFERRED_VERSION_openocd-native = "riscv"
PREFERRED_VERSION_openocd = "riscv"

View File

@ -12,4 +12,4 @@ __path__ = extend_path(__path__, __name__)
BBIMPORTS = ["qa", "data", "path", "utils", "types", "package", "packagedata", \
"packagegroup", "sstatesig", "lsb", "cachedpath", "license", "qemu", \
"reproducible", "rust", "buildcfg", "go", "spdx30_tasks", "spdx_common", \
"cve_check"]
"cve_check", "tune"]

81
meta/lib/oe/tune.py Normal file
View File

@ -0,0 +1,81 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
# riscv_isa_to_tune(isa)
#
# Automatically translate a RISC-V ISA string to TUNE_FEATURES
#
# Abbreviations, such as rv32g -> rv32imaffd_zicsr_zifencei are supported.
#
# Profiles, such as rva22u64, are NOT supported, you must use ISA strings.
#
def riscv_isa_to_tune(isa):
_isa = isa.lower()
feature = []
iter = 0
# rv or riscv
if _isa[iter:].startswith('rv'):
feature.append('rv')
iter = iter + 2
elif _isa[iter:].startswith('riscv'):
feature.append('rv')
iter = iter + 5
else:
# Not a risc-v ISA!
return _isa
while (_isa[iter:]):
# Skip _ and whitespace
if _isa[iter] == '_' or _isa[iter].isspace():
iter = iter + 1
continue
# Length, just capture numbers here
if _isa[iter].isdigit():
iter_end = iter
while iter_end < len(_isa) and _isa[iter_end].isdigit():
iter_end = iter_end + 1
feature.append(_isa[iter:iter_end])
iter = iter_end
continue
# Typically i, e or g is next, followed by extensions.
# Extensions are single character, except for Z, Ss, Sh, Sm, Sv, and X
# If the extension starts with 'Z', 'S' or 'X' use the name until the next _, whitespace or end
if _isa[iter] in ['z', 's', 'x']:
ext_type = _isa[iter]
iter_end = iter + 1
# Multicharacter extension, these are supposed to have a _ before the next multicharacter extension
# See 37.4 and 37.5:
# 37.4: Underscores "_" may be used to separate ISA extensions...
# 37.5: All multi-letter extensions ... must be separated from other multi-letter extensions by an underscore...
# Some extensions permit only alphabetic characters, while others allow alphanumeric chartacters
while iter_end < len(_isa) and _isa[iter_end] != "_" and not _isa[iter_end].isspace():
iter_end = iter_end + 1
feature.append(_isa[iter:iter_end])
iter = iter_end
continue
# 'g' is special, it's an abbreviation for imafd_zicsr_zifencei
# When expanding the abbreviation, any additional letters must appear before the _z* extensions
if _isa[iter] == 'g':
_isa = 'imafd' + _isa[iter+1:] + '_zicsr_zifencei'
iter = 0
continue
feature.append(_isa[iter])
iter = iter + 1
continue
# Eliminate duplicates, but preserve the order
feature = list(dict.fromkeys(feature))
return ' '.join(feature)