[PATCH 0/7] RISC-V: Support version controling for ISA standard extensions and CSR

Nelson Chu
2020-04-18T11:08:14Z

Hi binutils and gdb,

I know every RISCV contirbutors all do the best to maintain the compatibility
between different spec versions. But the specs are changed frequently, and
sometimes it is very hard to keep the compatibility if we don't have a good
way to deal with versioning problems. Since different ISA specs define
different instructions, and different privilege specs define different CSR,
both of them need the version checking and controling mechanism.

For the ISA versions, you can set them by the RISCV ELF architecture attribute
and assembler option -march, but you have to set them one by one for each
extension. It seems not so friendly for user, so Kito Cheng proposed a good
solution to resolve the problem. That is, support the new assembler options and
default configure options to reduce the burdens when setting ISA versions.
The original idea and discussion are come from the following link,

https://groups.google.com/a/groups.riscv.org/forum/#!msg/sw-dev/aZhMG7NIVTk/PTZEaTWiAwAJ)

=== New Assembler Options ===

* -misa-spec = [2p0|2p1|2p2|20190608|20191213]
You can simply choose the ISA spec by this option, and then assembler will set
the version for the standard extensions if you don’t set in the ELF arch
attributes or -march option.

* -mriscv-isa-version = <ISA-string>
The syntax of <ISA-string> is same as -march option, but without rv32 or rv64
prefix. There are two reasons that you may need this option,

1. Some versions of extensions defined in the ISA spec are not matched to the
toolchain’s implementation. For example, V-ext is defined to version 0.7 in
ISA spec, but we only have implementations for 0.8 and 0.9. Therefore, you
have to use this option to choose the correct version for V-ext.

2. You want to update the version for the specific standard extensions if the
version are not set in the ELF arch attributes and -march option.

=== New Default Configure Options ===

* --with-arch = <ISA-string>
The syntax of <ISA-string> is same as -march option. Assembler will check this
if -march option and ELF arch attributes aren’t set.

* --with-isa-spec = [2p0|2p1|2p2|20190608|20191213]
The syntax is same as -misa-spec option. Assembler will check this if
-misa-spec option isn’t set.

* --with-riscv-isa-version = <ISA-string>
The syntax of <ISA-string> is same as -mriscv-isa-version option. Assembler
will check this if -mriscv-isa-version option isn’t set.

=== The Priority of these options ===

* ELF arch attributes > Assembler options > Default configure options
* For GAS options, -march > -mriscv-isa-version > -misa-spec
* For configure options, --with-arch > --with-riscv-isa-version > --with-isa-spec

=== Example ===

$ cat test.s
.attribute arch, "rv32i1p9fv_zicsr"

$ riscv32-unknown-elf-as -march-attr -misa-spec=2p2 -mriscv-isa-version=i2p1a2p1v0p9 test.s -o test.o

$ riscv32-unknown-elf-readelf -A test.o
Attribute Section: riscv
File Attributes
Tag_RISCV_arch: "rv32i1p9_f2p0_v0p9_zicsr0p0"
...

The above example covers most cases.

* For i-ext, we already set the version 1p9 in elf attribute, so -misa-spec and
-mriscv-isa-version don't update the version to 2p0 and 2p1.

* For f-ext, we don't set the version to it, so use the default version 2p0
according to the ISA spec 2p2.

* For v-ext, the default version is 0p7 according to ISA spec 2p2, but we use
-mriscv-isa-version to update it to 0p9.

* For a-ext, it isn't enabled by -march or elf attribute, so we don't update it's
version even if -mriscv-isa-version is set.

* As for the zicsr extension, it is defined in the lastest ISA spec rather than 2p2,
so set it's version to 0p0.

As for the privilege version, I get the related request in the following link,
https://github.com/riscv/riscv-binutils-gdb/issues/202

We also support new assembler options to choose the spec you want, and then
assembler will generate the correct CSR address according to the chosen spec.
If the obselete CSR name is used, then report the warning message when the
-mcsr-check is set, and use the latest defined address for the CSR. Of course,
you can also choose the spec by setting the orginal RISCV ELF priv attributes.
Beside, the disassembler can show the CSR according to the chosen privilege spec
by the new disassmebler option. However, the CSR address is showed directly if
it is invalid for the chosen spec.

=== New Assembler Option ===

* -mpriv-spec=[1p9|1p9p1|1p10|1p11]
This is used to set the privileged spec’s version, and we can decide whether
the CSR is valid or not.

=== New Default Configure Option ===

* --with-priv-spec = [1p9|1p9p1|1p10|1p11]
The syntax is same as -mpriv-spec option. Assembler will check this setting
if -mpriv-spec option isn’t set.

=== New Disassmebler Option ===

* -Mpriv-spec=[1p9|1p9p1|1p10|1p11]
The syntax is same as -mpriv-spec option. The disassembler can show the CSR
correctly according to the chosen privilege spec.

Notes,

1. I'm not sure if GDB needs the above version checking, but I think the current
modifications won't change the behavior of GDB. We still use the DECLARE_CSR
to generate the gdb/features/riscv/[32|64]bit-csr.xml, that means we only generate
the CSR which are valid in the latest privilege spec. Also, the objdump shows
the CSR according to the latest privilege spec by default.

2. The default ISA spec is set to 2p2 rather than the lastest version. The reason
is that compiler generates the ISA string with fixed 2p0 verisons only for the
RISCV ELF architecture attributes, but not for the -march option. Therefore, we
should update the compiler or linker to resolve this problem in the futrue patches.

3. How to extend the default versions and new ISA spec?
You only need to update the riscv_ext_version_table which is defined in the
opcodes/riscv-opc.c.

const struct riscv_ext_version riscv_ext_version_table[] =
{
/* name, spec name, major, minor. */
{"e", "20191213", 1, 9},
{"e", "20190608", 1, 9},
{"e", "2p2", 1, 9},

{"i", "20191213", 2, 1},
{"i", "20190608", 2, 1},
{"i", "2p2", 2, 0},
...
};

4. How to extend new privilege spec and new CSR?

* include/opcode/riscv.h
enum riscv_csr_vclass
{
CSR_CLASS_VNONE, /* Default value */

CSR_CLASS_V1P9, /* v1.9 */
CSR_CLASS_V1P9P1, /* v1.9.1 */
CSR_CLASS_V1P10, /* v1.10 */
CSR_CLASS_V1P11, /* v1.11 */
CSR_CLASS_VDRAFT
};

* opcodes/riscv-opc.c

static const struct priv_version_t priv_versions[] =
{
{"1p9", CSR_CLASS_V1P9},
{"1p9p1", CSR_CLASS_V1P9P1},
{"1p10", CSR_CLASS_V1P10},
{"1p11", CSR_CLASS_V1P11},

/* Terminate the list. */
{NULL, 0}
};

* include/opcode/riscv-opc.h

Format, DECLARE_CSR (NAME, VALUE, CLASS, DEFINE_VER, ABORT_VER)
DECLARE_CSR_ALIAS(NAME, VALUE, CLASS, DEFINE_VER, ABORT_VER)

NAME: CSR name.
VALUE: CSR address
CLASS: ISA dependency
DEFINE_VER: First defined in which privilege spec.
ABORT_VER: Aborted in which privilege spec. If the CSR is still valid in
the lastest version, then it should be CSR_CLASS_VDRAFT.

If the CSR is aborted in the latest spec, or has not only one information, then
use DECLARE_CSR_ALIAS. Otherwise, use DECLARE_CSR.

For example,
DECLARE_CSR(ustatus, CSR_USTATUS, CSR_CLASS_I, CSR_CLASS_V1P9, CSR_CLASS_VDRAFT)
DECLARE_CSR_ALIAS(ubadaddr, CSR_UTVAL, CSR_CLASS_I, CSR_CLASS_V1P9, CSR_CLASS_V1P10)

Thanks
Nelson