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

-- 

Reply via email to