On Thu, Dec 5, 2024 at 9:36 PM Rajnesh Kanwal <rkan...@rivosinc.com> wrote: > > CTR entries are accessed using ctrsource, ctrtarget and ctrdata > registers using smcsrind/sscsrind extension. This commits extends > the csrind extension to support CTR registers. > > ctrsource is accessible through xireg CSR, ctrtarget is accessible > through xireg1 and ctrdata is accessible through xireg2 CSR. > > CTR supports maximum depth of 256 entries which are accessed using > xiselect range 0x200 to 0x2ff. > > This commits also adds properties to enable CTR extension. CTR can be > enabled using smctr=true and ssctr=true now. > > Signed-off-by: Rajnesh Kanwal <rkan...@rivosinc.com> > --- > target/riscv/cpu.c | 26 +++++++- > target/riscv/csr.c | 150 > ++++++++++++++++++++++++++++++++++++++++++++- > target/riscv/tcg/tcg-cpu.c | 11 ++++ > 3 files changed, 185 insertions(+), 2 deletions(-) > > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c > index > 2a4f285a974ffc62e7f3e938691dbffe376a7e46..751029e924d4690aaa5de65456fd5a5ec25b916a > 100644 > --- a/target/riscv/cpu.c > +++ b/target/riscv/cpu.c > @@ -199,6 +199,8 @@ const RISCVIsaExtData isa_edata_arr[] = { > ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), > ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), > 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), > @@ -1481,6 +1483,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),
This should be the very last patch (once everything is supported) Otherwise Acked-by: Alistair Francis <alistair.fran...@wdc.com> Alistair > MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), > MULTI_EXT_CFG_BOOL("zicfilp", ext_zicfilp, false), > MULTI_EXT_CFG_BOOL("zicfiss", ext_zicfiss, false), > @@ -2656,6 +2660,26 @@ static RISCVCPUImpliedExtsRule SSCFG_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 > @@ -2674,7 +2698,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, > - NULL > + &SMCTR_IMPLIED, &SSCTR_IMPLIED, NULL > }; > > static Property riscv_cpu_properties[] = { > diff --git a/target/riscv/csr.c b/target/riscv/csr.c > index > a399d55740c7259cd1c1d893687541e23ea3ce52..d7b520099563d3a680c5b75dc987881caab95407 > 100644 > --- a/target/riscv/csr.c > +++ b/target/riscv/csr.c > @@ -2399,6 +2399,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, > @@ -2444,6 +2451,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) > @@ -2596,6 +2721,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 > * > @@ -2607,11 +2753,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 > diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c > index > 2b57aa4d1704b176f314dbe0b120cfcc943bf4f8..575b5692c7f68a5f6d37edbc17269e41f496f682 > 100644 > --- a/target/riscv/tcg/tcg-cpu.c > +++ b/target/riscv/tcg/tcg-cpu.c > @@ -652,6 +652,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. > > -- > 2.34.1 > >