This is an automated email from Gerrit. "liangzhen <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9352
-- gerrit commit 3503f1ea3be2149debbd7b9163c5f9751bf28766 Author: liangzhen <[email protected]> Date: Thu Dec 18 15:17:19 2025 +0800 target/riscv: flush cache for sysbus write Introduce RISC-V-sepecific `configure` parameter `-sba-flush-cache`, which determines whether OpenOCD will automatically execute cbo.flush instructions to flush cache when write memory via sysbus. Change-Id: Iaa6cc56c2dce3643b12363cb922b7cb5f4253fb1 Signed-off-by: liangzhen <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index f623d716e9..cad0fd9232 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11674,6 +11674,16 @@ action pairs. @end itemize @end itemize +@itemize +@item @code{-sba-flush-cache} @option{off}|@option{on} -- determines whether +OpenOCD will automatically execute RISC-V CMO instructions (@var{cbo.flush}) +when write memory via RISC-V Debug System Bus interface. Defaults to @option{on}. + +@itemize +@item @code{cget} returns the currently configured state for @code{-sba-flush-cache}. +@end itemize +@end itemize + @subsection RISC-V Debug Configuration Commands @deffn {Command} {riscv dump_sample_buf} [base64] diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h index e4efc5b05f..15520f34c1 100644 --- a/src/target/riscv/opcodes.h +++ b/src/target/riscv/opcodes.h @@ -446,4 +446,12 @@ static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2, return (vm ? (1u << 25) : 0u) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX; } +static uint32_t cbo_flush(unsigned int rs1) __attribute__((unused)); +static uint32_t cbo_flush(unsigned int rs1) +{ + assert(rs1 <= MAX_GPR_NUM); + + return MATCH_CBO_FLUSH | inst_rs1(rs1); +} + #endif /* OPENOCD_TARGET_RISCV_OPCODES_H */ diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 6fa5e025be..6741a49eba 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -3098,6 +3098,88 @@ static int execute_autofence(struct target *target) return ERROR_FAIL; } +#define CACHE_LINE_SIZE 64 + +static int flush_cache(struct target *target, target_addr_t address, uint32_t size) +{ + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + + if (has_sufficient_progbuf(target, 3)) { + riscv_program_init(&program, target); + if (riscv_program_insert(&program, cbo_flush(GDB_REGNO_S0)) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, CACHE_LINE_SIZE) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; + + if (register_write_direct(target, GDB_REGNO_S0, address) != ERROR_OK) + return ERROR_FAIL; + + target_addr_t next_addr = address; + target_addr_t end_addr = address + size; + while (next_addr < end_addr) { + uint32_t cmderr; + if (riscv013_execute_progbuf(target, &cmderr) != ERROR_OK) { + if (cmderr != DM_ABSTRACTCS_CMDERR_EXCEPTION) { + LOG_TARGET_ERROR(target, "Unexpected error during cbo.flush execution"); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "Unable to execute cbo.flush"); + } + next_addr += CACHE_LINE_SIZE; + } + + LOG_TARGET_DEBUG(target, "Successfully executed cbo.flush"); + return ERROR_OK; + } + + if (has_sufficient_progbuf(target, 2)) { + riscv_program_init(&program, target); + if (riscv_program_insert(&program, cbo_flush(GDB_REGNO_S0)) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; + + target_addr_t next_addr = address; + target_addr_t end_addr = address + size; + while (next_addr < end_addr) { + if (register_write_direct(target, GDB_REGNO_S0, next_addr) != ERROR_OK) + return ERROR_FAIL; + + uint32_t cmderr; + if (riscv013_execute_progbuf(target, &cmderr) != ERROR_OK) { + if (cmderr != DM_ABSTRACTCS_CMDERR_EXCEPTION) { + LOG_TARGET_ERROR(target, "Unexpected error during cbo.flush execution"); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "Unable to execute cbo.flush"); + } + next_addr += CACHE_LINE_SIZE; + } + + LOG_TARGET_DEBUG(target, "Successfully executed cbo.flush"); + return ERROR_OK; + } + + return ERROR_FAIL; +} + static void log_memory_access128(target_addr_t address, uint64_t value_h, uint64_t value_l, bool is_read) { @@ -4634,6 +4716,10 @@ static int write_memory_bus_v0(struct target *target, const struct riscv_mem_acc riscv_addr_t t_addr = 0; const uint8_t *t_buffer = args.write_buffer + offset; + struct riscv_private_config *config = target->private_config; + if (config->sba_flush_cache) + flush_cache(target, args.address, args.count * args.size); + /* B.8 Writing Memory, single write check if we write in one go */ if (args.count == 1) { /* count is in bytes here */ value = buf_get_u64(t_buffer, 0, 8 * args.size); @@ -4683,6 +4769,10 @@ static int write_memory_bus_v1(struct target *target, const struct riscv_mem_acc sbcs = set_field(sbcs, DM_SBCS_SBAUTOINCREMENT, 1); dm_write(target, DM_SBCS, sbcs); + struct riscv_private_config *config = target->private_config; + if (config->sba_flush_cache) + flush_cache(target, args.address, args.count * args.size); + target_addr_t next_address = args.address; target_addr_t end_address = args.address + args.count * args.size; diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 8054a1c9b7..c66a5c91f4 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -486,6 +486,8 @@ static struct riscv_private_config *alloc_default_riscv_private_config(void) for (unsigned int i = 0; i < ARRAY_SIZE(config->dcsr_ebreak_fields); ++i) config->dcsr_ebreak_fields[i] = true; + config->sba_flush_cache = true; + return config; } @@ -525,6 +527,12 @@ static struct jim_nvp nvp_ebreak_mode_opts[] = { { .name = NULL, .value = RISCV_EBREAK_MODE_INVALID } }; +static struct jim_nvp nvp_on_off_opts[] = { + { .name = "off", .value = false }, + { .name = "on", .value = true }, + { .name = NULL, .value = -1 } +}; + static int jim_configure_ebreak(struct riscv_private_config *config, struct jim_getopt_info *goi) { if (goi->argc == 0) { @@ -613,11 +621,13 @@ static int jim_report_ebreak_config(const struct riscv_private_config *config, enum riscv_cfg_opts { RISCV_CFG_EBREAK, + RISCV_CFG_SBA_FLUSH_CACHE, RISCV_CFG_INVALID = -1 }; static struct jim_nvp nvp_config_opts[] = { { .name = "-ebreak", .value = RISCV_CFG_EBREAK }, + { .name = "-sba-flush-cache", .value = RISCV_CFG_SBA_FLUSH_CACHE }, { .name = NULL, .value = RISCV_CFG_INVALID } }; @@ -654,10 +664,24 @@ static int riscv_jim_configure(struct target *target, return goi->is_configure ? jim_configure_ebreak(config, goi) : jim_report_ebreak_config(config, goi->interp); + case RISCV_CFG_SBA_FLUSH_CACHE: + if (goi->is_configure) { + struct jim_nvp *opt_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &opt_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->sba_flush_cache = opt_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->sba_flush_cache)->name, -1); + } + break; default: assert(false && "'jim_getopt_nvp' should have returned an error."); } - return JIM_ERR; + return JIM_OK; } static int riscv_init_target(struct command_context *cmd_ctx, diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 2a0a9b95f0..aa14ad022c 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -378,6 +378,7 @@ enum riscv_priv_mode { struct riscv_private_config { bool dcsr_ebreak_fields[N_RISCV_MODE]; + bool sba_flush_cache; }; static inline struct riscv_private_config --
