|
@@ -2427,6 +2427,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,
|
|
@@ -2472,6 +2479,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)
|
|
@@ -2624,6 +2749,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
|
|
|
*
|
|
@@ -2635,11 +2781,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
|