Hi Daniel, On Mon, Feb 02, 2026 at 02:39:09PM -0300, Daniel Henrique Barboza wrote:
On 1/30/2026 3:00 AM, Chao Liu wrote:
RISC-V Debug Specification: https://github.com/riscv/riscv-debug-spec/releases/tag/1.0
Add architectural state for Sdext Debug Mode: debug_mode, dcsr, dpc and dscratch0/1. Wire up CSR access for dcsr/dpc/dscratch and gate them to Debug Mode (or host debugger access).
The Sdext is not fully implemented, so it is disabled by default.
Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com> --- target/riscv/cpu.c | 11 +++ target/riscv/cpu.h | 4 + target/riscv/cpu_bits.h | 33 ++++++++ target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/csr.c | 126 ++++++++++++++++++++++++++++++ target/riscv/machine.c | 20 +++++ 6 files changed, 195 insertions(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9fa4e09f17..9b08ef9fb0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -210,6 +210,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), + ISA_EXT_DATA_ENTRY(sdext, PRIV_VERSION_1_12_0, ext_sdext), ISA_EXT_DATA_ENTRY(sdtrig, PRIV_VERSION_1_12_0, ext_sdtrig), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha), @@ -782,6 +783,12 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) env->vill = true; #ifndef CONFIG_USER_ONLY + env->debug_mode = false; + env->dcsr = DCSR_DEBUGVER(4); + env->dpc = 0; + env->dscratch[0] = 0; + env->dscratch[1] = 0; + if (cpu->cfg.ext_sdtrig) { riscv_trigger_reset_hold(env); } @@ -1132,6 +1139,9 @@ static void riscv_cpu_init(Object *obj) */ cpu->cfg.ext_sdtrig = true; + /* sdext is not fully implemented, so it is disabled by default. */ + cpu->cfg.ext_sdext = false; +
We don't need to specify ext_sdext as default false here. You've already set it to false in MULTI_EXT_CFG_BOOL below, and that covers the rv64 defaults. For every other CPU, an ext is always assumed as 'false' unless it's explicitly set to 'true' somewhere.
Thanks for the review! You're absolutely right - the explicit `cpu->cfg.ext_sdext = false;`is redundant since it's already set to false in MULTI_EXT_CFG_BOOL. I'll remove those 3 lines in next version. Thanks, Chao
Everything else LGTM, so with these 3 lines above removed:
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
if (mcc->def->profile) { mcc->def->profile->enabled = true; } @@ -1246,6 +1256,7 @@ 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("sdext", ext_sdext, false), MULTI_EXT_CFG_BOOL("sdtrig", ext_sdtrig, true), MULTI_EXT_CFG_BOOL("smctr", ext_smctr, false), MULTI_EXT_CFG_BOOL("ssctr", ext_ssctr, false), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4c0676ed53..2a265faae5 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -476,6 +476,10 @@ struct CPUArchState { /* True if in debugger mode. */ bool debugger; + bool debug_mode; + target_ulong dcsr; + target_ulong dpc; + target_ulong dscratch[2]; uint64_t mstateen[SMSTATEEN_MAX_COUNT]; uint64_t hstateen[SMSTATEEN_MAX_COUNT]; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index b62dd82fe7..bb59f7ff56 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -467,6 +467,39 @@ #define CSR_DCSR 0x7b0 #define CSR_DPC 0x7b1 #define CSR_DSCRATCH 0x7b2 +#define CSR_DSCRATCH1 0x7b3 + +/* DCSR fields */ +#define DCSR_XDEBUGVER_SHIFT 28 +#define DCSR_XDEBUGVER_MASK (0xfu << DCSR_XDEBUGVER_SHIFT) +#define DCSR_DEBUGVER(val) ((target_ulong)(val) << DCSR_XDEBUGVER_SHIFT) +#define DCSR_EXTCAUSE_SHIFT 24 +#define DCSR_EXTCAUSE_MASK (0x7u << DCSR_EXTCAUSE_SHIFT) +#define DCSR_CETRIG BIT(19) +#define DCSR_PELP BIT(18) +#define DCSR_EBREAKVS BIT(17) +#define DCSR_EBREAKVU BIT(16) +#define DCSR_EBREAKM BIT(15) +#define DCSR_EBREAKS BIT(13) +#define DCSR_EBREAKU BIT(12) +#define DCSR_STEPIE BIT(11) +#define DCSR_STOPCOUNT BIT(10) +#define DCSR_STOPTIME BIT(9) +#define DCSR_CAUSE_SHIFT 6 +#define DCSR_CAUSE_MASK (0x7u << DCSR_CAUSE_SHIFT) +#define DCSR_V BIT(5) +#define DCSR_MPRVEN BIT(4) +#define DCSR_NMIP BIT(3) +#define DCSR_STEP BIT(2) +#define DCSR_PRV_MASK 0x3u + +#define DCSR_CAUSE_EBREAK 1 +#define DCSR_CAUSE_TRIGGER 2 +#define DCSR_CAUSE_HALTREQ 3 +#define DCSR_CAUSE_STEP 4 +#define DCSR_CAUSE_RESET 5 +#define DCSR_CAUSE_GROUP 6 +#define DCSR_CAUSE_OTHER 7 /* Performance Counters */ #define CSR_MHPMCOUNTER3 0xb03 diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 1e9162281f..e0d70fe8c7 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -46,6 +46,7 @@ BOOL_FIELD(ext_zilsd) BOOL_FIELD(ext_zimop) BOOL_FIELD(ext_zcmop) BOOL_FIELD(ext_ztso) +BOOL_FIELD(ext_sdext) BOOL_FIELD(ext_sdtrig) BOOL_FIELD(ext_smstateen) BOOL_FIELD(ext_sstc) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 870fad87ac..3e38c943e0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3136,6 +3136,126 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +#if !defined(CONFIG_USER_ONLY) +static RISCVException sdext(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_sdext) { + return RISCV_EXCP_ILLEGAL_INST; + } + + if (!env->debug_mode && !env->debugger) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static target_ulong dcsr_visible_mask(CPURISCVState *env) +{ + target_ulong mask = (target_ulong)-1; + RISCVCPU *cpu = env_archcpu(env); + + if (!riscv_has_ext(env, RVH)) { + mask &= ~(DCSR_EBREAKVS | DCSR_EBREAKVU | DCSR_V); + } + if (!riscv_has_ext(env, RVS)) { + mask &= ~DCSR_EBREAKS; + } + if (!riscv_has_ext(env, RVU)) { + mask &= ~DCSR_EBREAKU; + } + if (!cpu->cfg.ext_zicfilp) { + mask &= ~DCSR_PELP; + } + if (!cpu->cfg.ext_smdbltrp) { + mask &= ~DCSR_CETRIG; + } + + return mask; +} + +static RISCVException read_dcsr(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->dcsr & dcsr_visible_mask(env); + return RISCV_EXCP_NONE; +} + +static target_ulong dcsr_writable_mask(CPURISCVState *env) +{ + target_ulong mask = DCSR_EBREAKM | DCSR_EBREAKS | DCSR_EBREAKU | + DCSR_STEPIE | DCSR_STOPCOUNT | DCSR_STOPTIME | + DCSR_STEP | DCSR_PRV_MASK; + RISCVCPU *cpu = env_archcpu(env); + + mask |= DCSR_MPRVEN; + + if (riscv_has_ext(env, RVH)) { + mask |= DCSR_EBREAKVS | DCSR_EBREAKVU | DCSR_V; + } + if (riscv_has_ext(env, RVS)) { + mask |= DCSR_EBREAKS; + } + if (riscv_has_ext(env, RVU)) { + mask |= DCSR_EBREAKU; + } + if (cpu->cfg.ext_zicfilp) { + mask |= DCSR_PELP; + } + if (cpu->cfg.ext_smdbltrp) { + mask |= DCSR_CETRIG; + } + + return mask; +} + +static RISCVException write_dcsr(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + target_ulong mask = dcsr_writable_mask(env); + target_ulong new_val = env->dcsr; + + new_val &= ~mask; + new_val |= val & mask; + new_val &= ~DCSR_XDEBUGVER_MASK; + new_val |= DCSR_DEBUGVER(4); + env->dcsr = new_val; + return RISCV_EXCP_NONE; +} + +static RISCVException read_dpc(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->dpc & get_xepc_mask(env); + return RISCV_EXCP_NONE; +} + +static RISCVException write_dpc(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + env->dpc = val & get_xepc_mask(env); + return RISCV_EXCP_NONE; +} + +static RISCVException read_dscratch(CPURISCVState *env, int csrno, + target_ulong *val) +{ + int index = (csrno == CSR_DSCRATCH1) ? 1 : 0; + + *val = env->dscratch[index]; + return RISCV_EXCP_NONE; +} + +static RISCVException write_dscratch(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) +{ + int index = (csrno == CSR_DSCRATCH1) ? 1 : 0; + + env->dscratch[index] = val; + return RISCV_EXCP_NONE; +} +#endif /* !CONFIG_USER_ONLY */ + /* Execution environment configuration setup */ static RISCVException read_menvcfg(CPURISCVState *env, int csrno, target_ulong *val) @@ -6297,6 +6417,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata }, [CSR_TINFO] = { "tinfo", debug, read_tinfo, write_ignore }, [CSR_MCONTEXT] = { "mcontext", debug, read_mcontext, write_mcontext }, +#if !defined(CONFIG_USER_ONLY) + [CSR_DCSR] = { "dcsr", sdext, read_dcsr, write_dcsr }, + [CSR_DPC] = { "dpc", sdext, read_dpc, write_dpc }, + [CSR_DSCRATCH] = { "dscratch0", sdext, read_dscratch, write_dscratch }, + [CSR_DSCRATCH1] = { "dscratch1", sdext, read_dscratch, write_dscratch }, +#endif [CSR_MCTRCTL] = { "mctrctl", ctr_mmode, NULL, NULL, rmw_xctrctl }, [CSR_SCTRCTL] = { "sctrctl", ctr_smode, NULL, NULL, rmw_xctrctl }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 62c51c8033..52264cf047 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -248,6 +248,25 @@ static const VMStateDescription vmstate_sdtrig = { VMSTATE_UINTTL_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS), VMSTATE_UINTTL_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS), VMSTATE_UINTTL_ARRAY(env.tdata3, RISCVCPU, RV_MAX_TRIGGERS), + VMSTATE_BOOL_V(env.debug_mode, RISCVCPU, 3), + VMSTATE_UINTTL_V(env.dcsr, RISCVCPU, 3), + VMSTATE_UINTTL_V(env.dpc, RISCVCPU, 3), + VMSTATE_UINTTL_ARRAY_V(env.dscratch, RISCVCPU, 2, 3), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_sdext = { + .name = "cpu/sdext", + .version_id = 1, + .minimum_version_id = 1, + .needed = sdtrig_needed, + .post_load = sdtrig_post_load, + .fields = (const VMStateField[]) { + VMSTATE_BOOL_V(env.debug_mode, RISCVCPU, 3), + VMSTATE_UINTTL_V(env.dcsr, RISCVCPU, 3), + VMSTATE_UINTTL_V(env.dpc, RISCVCPU, 3), + VMSTATE_UINTTL_ARRAY_V(env.dscratch, RISCVCPU, 2, 3), VMSTATE_END_OF_LIST() } }; @@ -499,6 +518,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_ctr, &vmstate_sstc, &vmstate_sdtrig, + &vmstate_sdext, NULL } };