From: Emmanuel Blot <eblot@rivosinc.com>
This extension was not ratified with the Zb[abcs] bitmanip extensions.
This is the latest draft version (0.93) as implemented by the Ibex core.
These instructions are in the reserved encoding space but have not been
ratified and could conflict with future ratified instructions. For this
reason they are added as a vendor extension to support Ibex's impl.
Signed-off-by: James Wainwright <james.wainwright@lowrisc.org>
---
target/riscv/bitmanip_helper.c | 20 +++++++
target/riscv/cpu.c | 4 +-
target/riscv/cpu_cfg.h | 1 +
target/riscv/cpu_cfg_fields.h.inc | 1 +
target/riscv/helper.h | 2 +
target/riscv/insn_trans/trans_xbr0p93.c.inc | 55 ++++++++++++++++++
target/riscv/meson.build | 1 +
target/riscv/translate.c | 3 +
target/riscv/xbr0p93.decode | 42 ++++++++++++++
tests/tcg/riscv64/Makefile.softmmu-target | 5 ++
tests/tcg/riscv64/test-crc32.S | 64 +++++++++++++++++++++
11 files changed, 197 insertions(+), 1 deletion(-)
create mode 100644 target/riscv/insn_trans/trans_xbr0p93.c.inc
create mode 100644 target/riscv/xbr0p93.decode
create mode 100644 tests/tcg/riscv64/test-crc32.S
diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c
index e9c8d7f778..1156a87dd3 100644
--- a/target/riscv/bitmanip_helper.c
+++ b/target/riscv/bitmanip_helper.c
@@ -23,6 +23,8 @@
#include "exec/target_long.h"
#include "exec/helper-proto.h"
#include "tcg/tcg.h"
+#include "qemu/crc32.h"
+#include "qemu/crc32c.h"
target_ulong HELPER(clmul)(target_ulong rs1, target_ulong rs2)
{
@@ -129,3 +131,21 @@ target_ulong HELPER(xperm8)(target_ulong rs1, target_ulong rs2)
{
return do_xperm(rs1, rs2, 3);
}
+
+target_ulong HELPER(crc32)(target_ulong rs1, target_ulong sz)
+{
+ for (target_ulong i = 0; i < sz; i++) {
+ rs1 = crc32_table[rs1 & 0xFF] ^ (rs1 >> 8);
+ }
+
+ return rs1;
+}
+
+target_ulong HELPER(crc32c)(target_ulong rs1, target_ulong sz)
+{
+ for (target_ulong i = 0; i < sz; i++) {
+ rs1 = crc32c_table[rs1 & 0xFF] ^ (rs1 >> 8);
+ }
+
+ return rs1;
+}
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index e56470a374..025ff79b7d 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -1370,6 +1370,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = {
MULTI_EXT_CFG_BOOL("xmipscbop", ext_xmipscbop, false),
MULTI_EXT_CFG_BOOL("xmipscmov", ext_xmipscmov, false),
MULTI_EXT_CFG_BOOL("xmipslsp", ext_xmipslsp, false),
+ MULTI_EXT_CFG_BOOL("xbr0p93", ext_xbr0p93, false),
{ },
};
@@ -3056,7 +3057,8 @@ static const TypeInfo riscv_cpu_type_infos[] = {
.cfg.ext_zba = true,
.cfg.ext_zbb = true,
.cfg.ext_zbc = true,
- .cfg.ext_zbs = true
+ .cfg.ext_zbs = true,
+ .cfg.ext_xbr0p93 = true
),
DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E31, TYPE_RISCV_CPU_SIFIVE_E,
diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
index cd1cba797c..5e7ad0300f 100644
--- a/target/riscv/cpu_cfg.h
+++ b/target/riscv/cpu_cfg.h
@@ -69,5 +69,6 @@ MATERIALISE_EXT_PREDICATE(xtheadmemidx)
MATERIALISE_EXT_PREDICATE(xtheadmempair)
MATERIALISE_EXT_PREDICATE(xtheadsync)
MATERIALISE_EXT_PREDICATE(XVentanaCondOps)
+MATERIALISE_EXT_PREDICATE(xbr0p93);
#endif
diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc
index 70ec650abf..573ca70739 100644
--- a/target/riscv/cpu_cfg_fields.h.inc
+++ b/target/riscv/cpu_cfg_fields.h.inc
@@ -153,6 +153,7 @@ BOOL_FIELD(ext_XVentanaCondOps)
BOOL_FIELD(ext_xmipscbop)
BOOL_FIELD(ext_xmipscmov)
BOOL_FIELD(ext_xmipslsp)
+BOOL_FIELD(ext_xbr0p93)
BOOL_FIELD(mmu)
BOOL_FIELD(pmp)
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index b785456ee0..7722c590bd 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -84,6 +84,8 @@ DEF_HELPER_FLAGS_1(unzip, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_1(zip, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_2(xperm4, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_FLAGS_2(xperm8, TCG_CALL_NO_RWG_SE, tl, tl, tl)
+DEF_HELPER_FLAGS_2(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl)
+DEF_HELPER_FLAGS_2(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl)
/* Floating Point - Half Precision */
DEF_HELPER_FLAGS_3(fadd_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
diff --git a/target/riscv/insn_trans/trans_xbr0p93.c.inc b/target/riscv/insn_trans/trans_xbr0p93.c.inc
new file mode 100644
index 0000000000..a79cceaba6
--- /dev/null
+++ b/target/riscv/insn_trans/trans_xbr0p93.c.inc
@@ -0,0 +1,55 @@
+/*
+ * RISC-V translation routines for xbr0p93 matching the unratified Zbr CRC32
+ * bitmanip extension v0.93.
+ *
+ * Copyright (c) 2026 Rivos Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define REQUIRE_XBR0P93(ctx) do { \
+ if (!ctx->cfg_ptr->ext_xbr0p93) { \
+ return false; \
+ } \
+} while (0)
+
+static bool gen_crc(DisasContext *ctx, arg_r2 *a,
+ void (*func)(TCGv, TCGv, TCGv), TCGv tsz)
+{
+ REQUIRE_XBR0P93(ctx);
+ TCGv dest = dest_gpr(ctx, a->rd);
+ TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE);
+
+ func(dest, src1, tsz);
+ gen_set_gpr(ctx, a->rd, dest);
+
+ return true;
+}
+
+#define TRANS_CRC32(NAME, SIZE) \
+ static bool trans_crc32_##NAME(DisasContext *ctx, arg_r2 *a) \
+ { if (SIZE == 8) { REQUIRE_64BIT(ctx); }; \
+ return gen_crc(ctx, a, gen_helper_crc32, tcg_constant_tl(SIZE)); }
+#define TRANS_CRC32C(NAME, SIZE) \
+ static bool trans_crc32c_##NAME(DisasContext *ctx, arg_r2 *a) \
+ { if (SIZE == 8) { REQUIRE_64BIT(ctx); }; \
+ return gen_crc(ctx, a, gen_helper_crc32c, tcg_constant_tl(SIZE)); }
+
+TRANS_CRC32(b, 1);
+TRANS_CRC32(h, 2);
+TRANS_CRC32(w, 4);
+TRANS_CRC32(d, 8);
+TRANS_CRC32C(b, 1);
+TRANS_CRC32C(h, 2);
+TRANS_CRC32C(w, 4);
+TRANS_CRC32C(d, 8);
diff --git a/target/riscv/meson.build b/target/riscv/meson.build
index 3842c7c1a8..7d19a605ef 100644
--- a/target/riscv/meson.build
+++ b/target/riscv/meson.build
@@ -5,6 +5,7 @@ gen = [
decodetree.process('xthead.decode', extra_args: '--static-decode=decode_xthead'),
decodetree.process('XVentanaCondOps.decode', extra_args: '--static-decode=decode_XVentanaCodeOps'),
decodetree.process('xmips.decode', extra_args: '--static-decode=decode_xmips'),
+ decodetree.process('xbr0p93.decode', extra_args: '--static-decode=decode_xbr0p93'),
]
riscv_ss = ss.source_set()
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index cb4f443601..05a6916f17 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -1213,9 +1213,11 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
#include "insn_trans/trans_rvbf16.c.inc"
#include "decode-xthead.c.inc"
#include "decode-xmips.c.inc"
+#include "decode-xbr0p93.c.inc"
#include "insn_trans/trans_xthead.c.inc"
#include "insn_trans/trans_xventanacondops.c.inc"
#include "insn_trans/trans_xmips.c.inc"
+#include "insn_trans/trans_xbr0p93.c.inc"
/* Include the auto-generated decoder for 16 bit insn */
#include "decode-insn16.c.inc"
@@ -1235,6 +1237,7 @@ const RISCVDecoder decoder_table[] = {
{ has_xmips_p, decode_xmips},
{ has_xthead_p, decode_xthead},
{ has_XVentanaCondOps_p, decode_XVentanaCodeOps},
+ { has_xbr0p93_p, decode_xbr0p93},
};
const size_t decoder_table_size = ARRAY_SIZE(decoder_table);
diff --git a/target/riscv/xbr0p93.decode b/target/riscv/xbr0p93.decode
new file mode 100644
index 0000000000..8dafb86db6
--- /dev/null
+++ b/target/riscv/xbr0p93.decode
@@ -0,0 +1,42 @@
+#
+# Translation routines for the instructions of the xbr0p93 ISA extension
+# (matching the draft encodings in the standard reserved encoding space for the
+# unratified Zbr CRC32 bitmanip extension version 0.93).
+#
+# Copyright (c) 2026 Rivos Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2 or later, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Fields:
+%rs1 15:5
+%rd 7:5
+
+# Argument sets:
+&r2 rd rs1 !extern
+
+# Formats 32:
+@r2 ....... ..... ..... ... ..... ....... &r2 %rs1 %rd
+
+# *** RV32 xbr0p93 extension ***
+crc32_b 0110000 10000 ..... 001 ..... 0010011 @r2
+crc32_h 0110000 10001 ..... 001 ..... 0010011 @r2
+crc32_w 0110000 10010 ..... 001 ..... 0010011 @r2
+crc32c_b 0110000 11000 ..... 001 ..... 0010011 @r2
+crc32c_h 0110000 11001 ..... 001 ..... 0010011 @r2
+crc32c_w 0110000 11010 ..... 001 ..... 0010011 @r2
+
+# *** RV64 xbr0p93 extension (in addition to RV32) ***
+crc32_d 0110000 10011 ..... 001 ..... 0010011 @r2
+crc32c_d 0110000 11011 ..... 001 ..... 0010011 @r2
diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target
index eb1ce6504a..2328af3746 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -36,5 +36,10 @@ run-plugin-interruptedmemory: interruptedmemory
$(QEMU) -plugin ../plugins/libdiscons.so -d plugin -D $<.pout \
$(QEMU_OPTS)$<)
+EXTRA_RUNS += run-test-crc32
+comma:= ,
+run-test-crc32: test-crc32
+ $(call run-test, $<, $(QEMU) -cpu rv64$(comma)xbr0p93=true $(QEMU_OPTS)$<)
+
# We don't currently support the multiarch system tests
undefine MULTIARCH_TESTS
diff --git a/tests/tcg/riscv64/test-crc32.S b/tests/tcg/riscv64/test-crc32.S
new file mode 100644
index 0000000000..70d70b16a9
--- /dev/null
+++ b/tests/tcg/riscv64/test-crc32.S
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2026 lowRISC CIC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#define crc32(op, rd, rs1) .insn r 19, 1, 48, rd, rs1, x##op
+
+#define crc32_b(rd, rs1) crc32(16, rd, rs1)
+#define crc32_h(rd, rs1) crc32(17, rd, rs1)
+#define crc32_w(rd, rs1) crc32(18, rd, rs1)
+#define crc32_d(rd, rs1) crc32(19, rd, rs1)
+#define crc32c_b(rd, rs1) crc32(24, rd, rs1)
+#define crc32c_h(rd, rs1) crc32(25, rd, rs1)
+#define crc32c_w(rd, rs1) crc32(26, rd, rs1)
+#define crc32c_d(rd, rs1) crc32(27, rd, rs1)
+
+ .option norvc
+
+ .text
+ .globl _start
+_start:
+ lla t0, trap
+ csrw mtvec, t0
+
+ li t0, 0x34e24a2cd65650d4
+
+ crc32_b (t0, t0)
+ crc32_h (t0, t0)
+ crc32_w (t0, t0)
+ crc32_d (t0, t0)
+ crc32c_b (t0, t0)
+ crc32c_h (t0, t0)
+ crc32c_w (t0, t0)
+ crc32c_d (t0, t0)
+
+ li t1, 0x68167e78
+
+ li a0, 0
+ beq t0, t1, _exit
+fail:
+ li a0, 1
+_exit:
+ lla a1, semiargs
+ li t0, 0x20026 # ADP_Stopped_ApplicationExit
+ sd t0, 0(a1)
+ sd a0, 8(a1)
+ li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED
+
+ # Semihosting call sequence
+ .balign 16
+ slli zero, zero, 0x1f
+ ebreak
+ srai zero, zero, 0x7
+ j .
+
+ .data
+ .balign 16
+semiargs:
+ .space 16
+
+trap:
+ csrr t0, mepc
+ addi t0, t0, 4
+ mret
--
2.48.1
© 2016 - 2026 Red Hat, Inc.