aboutsummaryrefslogtreecommitdiff
path: root/riscv
diff options
context:
space:
mode:
Diffstat (limited to 'riscv')
-rw-r--r--riscv/insns/amocas_d.h37
-rw-r--r--riscv/insns/amocas_q.h34
-rw-r--r--riscv/insns/amocas_w.h2
-rw-r--r--riscv/isa_parser.cc6
-rw-r--r--riscv/isa_parser.h1
-rw-r--r--riscv/mmu.h11
-rw-r--r--riscv/processor.cc5
-rw-r--r--riscv/riscv.mk.in6
8 files changed, 102 insertions, 0 deletions
diff --git a/riscv/insns/amocas_d.h b/riscv/insns/amocas_d.h
new file mode 100644
index 0000000..e002e6a
--- /dev/null
+++ b/riscv/insns/amocas_d.h
@@ -0,0 +1,37 @@
+require_extension(EXT_ZACAS);
+
+if (xlen == 32) {
+ // RV32: the spec defines two 32-bit comparisons. Since we're
+ // loading 64-bit for memory we have to adjust for endianness.
+ uint64_t comp, swap, res;
+
+ require_align(insn.rd(), 2);
+ require_align(insn.rs2(), 2);
+ if (insn.rd() == 0) {
+ comp = 0;
+ } else if (MMU.is_target_big_endian()) {
+ comp = (uint32_t)READ_REG(insn.rd() + 1) | (RD << 32);
+ } else {
+ comp = (uint32_t)RD | (READ_REG(insn.rd() + 1) << 32);
+ }
+ if (insn.rs2() == 0) {
+ swap = 0;
+ } else if (MMU.is_target_big_endian()) {
+ swap = (uint32_t)READ_REG(insn.rs2() + 1) | (RS2 << 32);
+ } else {
+ swap = (uint32_t)RS2 | (READ_REG(insn.rs2() + 1) << 32);
+ }
+ res = MMU.amo_compare_and_swap<uint64_t>(RS1, comp, swap);
+ if (insn.rd() != 0) {
+ if (MMU.is_target_big_endian()) {
+ WRITE_REG(insn.rd() + 1, sext32((uint32_t)res));
+ WRITE_REG(insn.rd(), sext32(res >> 32));
+ } else {
+ WRITE_REG(insn.rd(), sext32((uint32_t)res));
+ WRITE_REG(insn.rd() + 1, sext32(res >> 32));
+ }
+ }
+ } else {
+ // RV64
+ WRITE_RD(MMU.amo_compare_and_swap<uint64_t>(RS1, RD, RS2));
+}
diff --git a/riscv/insns/amocas_q.h b/riscv/insns/amocas_q.h
new file mode 100644
index 0000000..0b7593b
--- /dev/null
+++ b/riscv/insns/amocas_q.h
@@ -0,0 +1,34 @@
+require_extension(EXT_ZACAS);
+require_rv64;
+require_align(insn.rd(), 2);
+require_align(insn.rs2(), 2);
+
+// The spec defines two 64-bit comparisons. Since we're loading
+// 128-bit for memory we have to adjust for endianness.
+
+uint128_t comp, swap, res;
+
+if (insn.rd() == 0) {
+ comp = 0;
+} else if (MMU.is_target_big_endian()) {
+ comp = READ_REG(insn.rd() + 1) | ((uint128_t)RD << 64);
+} else {
+ comp = RD | ((uint128_t)READ_REG(insn.rd() + 1) << 64);
+}
+if (insn.rs2() == 0) {
+ swap = 0;
+} else if (MMU.is_target_big_endian()) {
+ swap = READ_REG(insn.rs2() + 1) | ((uint128_t)RS2 << 64);
+} else {
+ swap = RS2 | ((uint128_t)READ_REG(insn.rs2() + 1) << 64);
+}
+res = MMU.amo_compare_and_swap<uint128_t>(RS1, comp, swap);
+if (insn.rd() != 0) {
+ if (MMU.is_target_big_endian()) {
+ WRITE_REG(insn.rd(), res >> 64);
+ WRITE_REG(insn.rd() + 1, res);
+ } else {
+ WRITE_REG(insn.rd(), res);
+ WRITE_REG(insn.rd() + 1, res >> 64);
+ }
+}
diff --git a/riscv/insns/amocas_w.h b/riscv/insns/amocas_w.h
new file mode 100644
index 0000000..a78c21c
--- /dev/null
+++ b/riscv/insns/amocas_w.h
@@ -0,0 +1,2 @@
+require_extension(EXT_ZACAS);
+WRITE_RD(sext32(MMU.amo_compare_and_swap<uint32_t>(RS1, RD, RS2)));
diff --git a/riscv/isa_parser.cc b/riscv/isa_parser.cc
index 8bb8c49..1c4300c 100644
--- a/riscv/isa_parser.cc
+++ b/riscv/isa_parser.cc
@@ -120,6 +120,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv)
// HINTs encoded in base-ISA instructions are always present.
} else if (ext_str == "zihintntl") {
// HINTs encoded in base-ISA instructions are always present.
+ } else if (ext_str == "zacas") {
+ extension_table[EXT_ZACAS] = true;
} else if (ext_str == "zmmul") {
extension_table[EXT_ZMMUL] = true;
} else if (ext_str == "zba") {
@@ -301,6 +303,10 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv)
bad_isa_string(str, "'Zcf/Zcd/Zcb/Zcmp/Zcmt' extensions require 'Zca' extension");
}
+ if (extension_table[EXT_ZACAS] && !extension_table['A']) {
+ bad_isa_string(str, "'Zacas' extension requires 'A' extension");
+ }
+
std::string lowercase = strtolower(priv);
bool user = false, supervisor = false;
diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h
index 4e68561..3cbee7d 100644
--- a/riscv/isa_parser.h
+++ b/riscv/isa_parser.h
@@ -61,6 +61,7 @@ typedef enum {
EXT_ZVFBFMIN,
EXT_ZVFBFWMA,
EXT_SSTC,
+ EXT_ZACAS,
EXT_INTERNAL_ZFH_MOVE,
NUM_ISA_EXTENSIONS
} isa_extension_t;
diff --git a/riscv/mmu.h b/riscv/mmu.h
index efc6e9d..46c54ce 100644
--- a/riscv/mmu.h
+++ b/riscv/mmu.h
@@ -187,6 +187,17 @@ public:
})
}
+ template<typename T>
+ T amo_compare_and_swap(reg_t addr, T comp, T swap) {
+ convert_load_traps_to_store_traps({
+ store_slow_path(addr, sizeof(T), nullptr, {false, false, false}, false, true);
+ auto lhs = load<T>(addr);
+ if (lhs == comp)
+ store<T>(addr, swap);
+ return lhs;
+ })
+ }
+
void store_float128(reg_t addr, float128_t val)
{
if (unlikely(addr & (sizeof(float128_t)-1)) && !is_misaligned_enabled()) {
diff --git a/riscv/processor.cc b/riscv/processor.cc
index a75b0ff..1d5675a 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -47,6 +47,11 @@ processor_t::processor_t(const isa_parser_t *isa, const cfg_t *cfg,
fprintf(stderr, "V extension is not supported on platforms without __int128 type\n");
abort();
}
+
+ if (isa->extension_enabled(EXT_ZACAS) && isa->get_max_xlen() == 64) {
+ fprintf(stderr, "Zacas extension is not supported on 64-bit platforms without __int128 type\n");
+ abort();
+ }
#endif
parse_varch_string(cfg->varch());
diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in
index db63290..6472982 100644
--- a/riscv/riscv.mk.in
+++ b/riscv/riscv.mk.in
@@ -1335,6 +1335,11 @@ riscv_insn_ext_bf16 = \
$(riscv_insn_ext_zvfbfmin) \
$(riscv_insn_ext_zvfbfwma) \
+riscv_insn_ext_zacas = \
+ amocas_w \
+ amocas_d \
+ $(if $(HAVE_INT128),amocas_q)
+
riscv_insn_list = \
$(riscv_insn_ext_a) \
$(riscv_insn_ext_c) \
@@ -1360,6 +1365,7 @@ riscv_insn_list = \
$(riscv_insn_ext_cmo) \
$(riscv_insn_ext_zicond) \
$(riscv_insn_ext_bf16) \
+ $(riscv_insn_ext_zacas) \
riscv_gen_srcs = $(addsuffix .cc,$(riscv_insn_list))