@@ -215,6 +215,8 @@ const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12),
ISA_EXT_DATA_ENTRY(supm, PRIV_VERSION_1_13_0, ext_supm),
ISA_EXT_DATA_ENTRY(svade, PRIV_VERSION_1_11_0, ext_svade),
+ ISA_EXT_DATA_ENTRY(smctr, PRIV_VERSION_1_12_0, ext_smctr),
+ ISA_EXT_DATA_ENTRY(ssctr, PRIV_VERSION_1_12_0, ext_ssctr),
ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu),
ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval),
ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot),
@@ -1598,6 +1600,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false),
MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false),
MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false),
+ MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false),
+ MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false),
MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true),
MULTI_EXT_CFG_BOOL("zicfilp", ext_zicfilp, false),
MULTI_EXT_CFG_BOOL("zicfiss", ext_zicfiss, false),
@@ -2803,6 +2807,26 @@ static RISCVCPUImpliedExtsRule SSPM_IMPLIED = {
},
};
+static RISCVCPUImpliedExtsRule SMCTR_IMPLIED = {
+ .ext = CPU_CFG_OFFSET(ext_smctr),
+ .implied_misa_exts = RVS,
+ .implied_multi_exts = {
+ CPU_CFG_OFFSET(ext_sscsrind),
+
+ RISCV_IMPLIED_EXTS_RULE_END
+ },
+};
+
+static RISCVCPUImpliedExtsRule SSCTR_IMPLIED = {
+ .ext = CPU_CFG_OFFSET(ext_ssctr),
+ .implied_misa_exts = RVS,
+ .implied_multi_exts = {
+ CPU_CFG_OFFSET(ext_sscsrind),
+
+ RISCV_IMPLIED_EXTS_RULE_END
+ },
+};
+
RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = {
&RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED,
&RVM_IMPLIED, &RVV_IMPLIED, NULL
@@ -2821,7 +2845,7 @@ RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = {
&ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED,
&ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED,
&ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, &SSCFG_IMPLIED,
- &SUPM_IMPLIED, &SSPM_IMPLIED,
+ &SUPM_IMPLIED, &SSPM_IMPLIED, &SMCTR_IMPLIED, &SSCTR_IMPLIED,
NULL
};
@@ -2431,6 +2431,13 @@ static bool xiselect_cd_range(target_ulong isel)
return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST);
}
+static bool xiselect_ctr_range(int csrno, target_ulong isel)
+{
+ /* MIREG-MIREG6 for the range 0x200-0x2ff are not used by CTR. */
+ return CTR_ENTRIES_FIRST <= isel && isel <= CTR_ENTRIES_LAST &&
+ csrno < CSR_MIREG;
+}
+
static int rmw_iprio(target_ulong xlen,
target_ulong iselect, uint8_t *iprio,
target_ulong *val, target_ulong new_val,
@@ -2476,6 +2483,124 @@ static int rmw_iprio(target_ulong xlen,
return 0;
}
+static int rmw_ctrsource(CPURISCVState *env, int isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ /*
+ * CTR arrays are treated as circular buffers and TOS always points to next
+ * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
+ * 0 is always the latest one, traversal is a bit different here. See the
+ * below example.
+ *
+ * Depth = 16.
+ *
+ * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * TOS H
+ * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
+ */
+ const uint64_t entry = isel - CTR_ENTRIES_FIRST;
+ const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint64_t idx;
+
+ /* Entry greater than depth-1 is read-only zero */
+ if (entry >= depth) {
+ if (val) {
+ *val = 0;
+ }
+ return 0;
+ }
+
+ idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+ idx = (idx - entry - 1) & (depth - 1);
+
+ if (val) {
+ *val = env->ctr_src[idx];
+ }
+
+ env->ctr_src[idx] = (env->ctr_src[idx] & ~wr_mask) | (new_val & wr_mask);
+
+ return 0;
+}
+
+static int rmw_ctrtarget(CPURISCVState *env, int isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ /*
+ * CTR arrays are treated as circular buffers and TOS always points to next
+ * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
+ * 0 is always the latest one, traversal is a bit different here. See the
+ * below example.
+ *
+ * Depth = 16.
+ *
+ * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * head H
+ * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
+ */
+ const uint64_t entry = isel - CTR_ENTRIES_FIRST;
+ const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint64_t idx;
+
+ /* Entry greater than depth-1 is read-only zero */
+ if (entry >= depth) {
+ if (val) {
+ *val = 0;
+ }
+ return 0;
+ }
+
+ idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+ idx = (idx - entry - 1) & (depth - 1);
+
+ if (val) {
+ *val = env->ctr_dst[idx];
+ }
+
+ env->ctr_dst[idx] = (env->ctr_dst[idx] & ~wr_mask) | (new_val & wr_mask);
+
+ return 0;
+}
+
+static int rmw_ctrdata(CPURISCVState *env, int isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ /*
+ * CTR arrays are treated as circular buffers and TOS always points to next
+ * empty slot, keeping TOS - 1 always pointing to latest entry. Given entry
+ * 0 is always the latest one, traversal is a bit different here. See the
+ * below example.
+ *
+ * Depth = 16.
+ *
+ * idx [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [A] [B] [C] [D] [E] [F]
+ * head H
+ * entry 6 5 4 3 2 1 0 F E D C B A 9 8 7
+ */
+ const uint64_t entry = isel - CTR_ENTRIES_FIRST;
+ const uint64_t mask = wr_mask & CTRDATA_MASK;
+ const uint64_t depth = 16 << get_field(env->sctrdepth, SCTRDEPTH_MASK);
+ uint64_t idx;
+
+ /* Entry greater than depth-1 is read-only zero */
+ if (entry >= depth) {
+ if (val) {
+ *val = 0;
+ }
+ return 0;
+ }
+
+ idx = get_field(env->sctrstatus, SCTRSTATUS_WRPTR_MASK);
+ idx = (idx - entry - 1) & (depth - 1);
+
+ if (val) {
+ *val = env->ctr_data[idx];
+ }
+
+ env->ctr_data[idx] = (env->ctr_data[idx] & ~mask) | (new_val & mask);
+
+ return 0;
+}
+
static RISCVException rmw_xireg_aia(CPURISCVState *env, int csrno,
target_ulong isel, target_ulong *val,
target_ulong new_val, target_ulong wr_mask)
@@ -2628,6 +2753,27 @@ done:
return ret;
}
+static int rmw_xireg_ctr(CPURISCVState *env, int csrno,
+ target_ulong isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
+{
+ if (!riscv_cpu_cfg(env)->ext_smctr && !riscv_cpu_cfg(env)->ext_ssctr) {
+ return -EINVAL;
+ }
+
+ if (csrno == CSR_SIREG || csrno == CSR_VSIREG) {
+ return rmw_ctrsource(env, isel, val, new_val, wr_mask);
+ } else if (csrno == CSR_SIREG2 || csrno == CSR_VSIREG2) {
+ return rmw_ctrtarget(env, isel, val, new_val, wr_mask);
+ } else if (csrno == CSR_SIREG3 || csrno == CSR_VSIREG3) {
+ return rmw_ctrdata(env, isel, val, new_val, wr_mask);
+ } else if (val) {
+ *val = 0;
+ }
+
+ return 0;
+}
+
/*
* rmw_xireg_csrind: Perform indirect access to xireg and xireg2-xireg6
*
@@ -2639,11 +2785,13 @@ static int rmw_xireg_csrind(CPURISCVState *env, int csrno,
target_ulong isel, target_ulong *val,
target_ulong new_val, target_ulong wr_mask)
{
- int ret = -EINVAL;
bool virt = csrno == CSR_VSIREG ? true : false;
+ int ret = -EINVAL;
if (xiselect_cd_range(isel)) {
ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask);
+ } else if (xiselect_ctr_range(csrno, isel)) {
+ ret = rmw_xireg_ctr(env, csrno, isel, val, new_val, wr_mask);
} else {
/*
* As per the specification, access to unimplented region is undefined
@@ -681,6 +681,17 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
return;
}
+ if ((cpu->cfg.ext_smctr || cpu->cfg.ext_ssctr) &&
+ (!riscv_has_ext(env, RVS) || !cpu->cfg.ext_sscsrind)) {
+ if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_smctr)) ||
+ cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ssctr))) {
+ error_setg(errp, "Smctr and Ssctr require S-mode and Sscsrind");
+ return;
+ }
+ cpu->cfg.ext_smctr = false;
+ cpu->cfg.ext_ssctr = false;
+ }
+
/*
* Disable isa extensions based on priv spec after we
* validated and set everything we need.