From 33de2512cfaa13ad8f9489a9d1118eb2268684dc Mon Sep 17 00:00:00 2001 From: Mark Zhuang Date: Tue, 29 Oct 2024 17:00:05 +0800 Subject: [PATCH] target/riscv: support disable auto fence Support disable automatic fence, it's useful for debug some cache related issue. --- doc/openocd.texi | 8 +++++++ src/target/riscv/riscv-013.c | 22 ++++++++++++++---- src/target/riscv/riscv.c | 45 ++++++++++++++++++++++++++++++++++++ src/target/riscv/riscv.h | 3 +++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index b2236dd32..0c4d6cce4 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11327,6 +11327,10 @@ allows to use it directly with TCL's `array set` function. In case obtaining an info point failed, the corresponding value is displayed as "unavailable". @end deffn +@deffn {Command} {riscv fence} +Execute fence.i and fence rw, rw. +@end deffn + @deffn {Command} {riscv reset_delays} [wait] OpenOCD learns how many Run-Test/Idle cycles are required between scans to avoid encountering the target being busy. This command resets those learned values @@ -11440,6 +11444,10 @@ Keep in mind, disabling the option does not guarantee that single stepping will To make that happen, dcsr.stepie would have to be written to 1 as well. @end deffn +@deffn {Command} {riscv set_auto_fence} [on|off] +When on (default), some situations, such as stepi, will automatically execute fence. +@end deffn + @deffn {Command} {riscv set_ebreakm} [on|off] Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 6003064e4..1633d2f1d 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -70,6 +70,7 @@ static int write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer); static bool riscv013_get_impebreak(const struct target *target); static unsigned int riscv013_get_progbufsize(const struct target *target); +static int execute_fence(struct target *target, bool force); typedef enum { HALT_GROUP, @@ -2175,6 +2176,11 @@ static COMMAND_HELPER(riscv013_print_info, struct target *target) return 0; } +static COMMAND_HELPER(riscv013_exec_fence, struct target *target) +{ + return execute_fence(target, true); +} + static int try_set_vsew(struct target *target, unsigned int *debug_vsew) { RISCV_INFO(r); @@ -2765,6 +2771,7 @@ static int init_target(struct command_context *cmd_ctx, generic_info->print_info = &riscv013_print_info; generic_info->get_impebreak = &riscv013_get_impebreak; generic_info->get_progbufsize = &riscv013_get_progbufsize; + generic_info->exec_fence = &riscv013_exec_fence; generic_info->handle_became_unavailable = &handle_became_unavailable; generic_info->tick = &tick; @@ -2913,11 +2920,15 @@ static int deassert_reset(struct target *target) return dm_write(target, DM_DMCONTROL, control); } -static int execute_fence(struct target *target) +static int execute_fence(struct target *target, bool force) { if (dm013_select_target(target) != ERROR_OK) return ERROR_FAIL; + RISCV_INFO(r); + if (!r->auto_fence && !force) + return ERROR_OK; + /* FIXME: For non-coherent systems we need to flush the caches right * here, but there's no ISA-defined way of doing that. */ struct riscv_program program; @@ -2941,6 +2952,7 @@ static int execute_fence(struct target *target) } LOG_TARGET_DEBUG(target, "Unable to execute fence"); } + LOG_TARGET_DEBUG(target, "Successfully executed fence"); return ERROR_OK; } @@ -2954,6 +2966,7 @@ static int execute_fence(struct target *target) } LOG_TARGET_DEBUG(target, "Unable to execute fence.i"); } + LOG_TARGET_DEBUG(target, "Successfully executed fence.i"); riscv_program_init(&program, target); riscv_program_fence_rw_rw(&program); @@ -2964,6 +2977,7 @@ static int execute_fence(struct target *target) } LOG_TARGET_DEBUG(target, "Unable to execute fence rw, rw"); } + LOG_TARGET_DEBUG(target, "Successfully executed fence rw, rw"); return ERROR_OK; } @@ -4273,7 +4287,7 @@ read_memory_progbuf(struct target *target, target_addr_t address, memset(buffer, 0, count*size); - if (execute_fence(target) != ERROR_OK) + if (execute_fence(target, false) != ERROR_OK) return MEM_ACCESS_SKIPPED_FENCE_EXEC_FAILED; uint64_t mstatus = 0; @@ -4864,7 +4878,7 @@ write_memory_progbuf(struct target *target, target_addr_t address, if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) return MEM_ACCESS_FAILED; - if (execute_fence(target) != ERROR_OK) + if (execute_fence(target, false) != ERROR_OK) return MEM_ACCESS_SKIPPED_FENCE_EXEC_FAILED; return result == ERROR_OK ? MEM_ACCESS_OK : MEM_ACCESS_FAILED; @@ -5354,7 +5368,7 @@ static int riscv013_get_dmi_scan_length(struct target *target) static int maybe_execute_fence_i(struct target *target) { if (has_sufficient_progbuf(target, 2)) - return execute_fence(target); + return execute_fence(target, false); return ERROR_OK; } diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index aa4bc6757..f06c1c1a9 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -4521,6 +4521,23 @@ COMMAND_HANDLER(riscv_set_maskisr) return ERROR_OK; } +COMMAND_HANDLER(riscv_set_auto_fence) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC != 1) { + command_print(CMD, "auto_fence enabled: %s", r->auto_fence ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->auto_fence); + return ERROR_OK; + } + + LOG_ERROR("Command takes 0 or 1 parameters"); + return ERROR_COMMAND_SYNTAX_ERROR; +} + COMMAND_HANDLER(riscv_set_ebreakm) { struct target *target = get_current_target(CMD_CTX); @@ -4998,6 +5015,17 @@ COMMAND_HANDLER(handle_info) return 0; } +COMMAND_HANDLER(handle_fence) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (r->exec_fence) + return CALL_COMMAND_HANDLER(r->exec_fence, target); + + return 0; +} + COMMAND_HANDLER(riscv_exec_progbuf) { if (CMD_ARGC < 1 || CMD_ARGC > 16) { @@ -5190,6 +5218,13 @@ static const struct command_registration riscv_exec_command_handlers[] = { .usage = "", .help = "Displays some information OpenOCD detected about the target." }, + { + .name = "fence", + .handler = handle_fence, + .mode = COMMAND_ANY, + .usage = "", + .help = "Execute fence.i and fence rw, rw." + }, { .name = "memory_sample", .handler = handle_memory_sample_command, @@ -5350,6 +5385,14 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "mask riscv interrupts", .usage = "['off'|'steponly']", }, + { + .name = "set_auto_fence", + .handler = riscv_set_auto_fence, + .mode = COMMAND_ANY, + .usage = "[on|off]", + .help = "When on (default), some situations, such as stepi, will " + "automatically execute fence." + }, { .name = "set_ebreakm", .handler = riscv_set_ebreakm, @@ -5569,6 +5612,8 @@ static void riscv_info_init(struct target *target, struct riscv_info *r) r->wp_allow_equality_match_trigger = true; r->wp_allow_ge_lt_trigger = true; r->wp_allow_napot_trigger = true; + + r->auto_fence = true; } static int riscv_resume_go_all_harts(struct target *target) diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 0b65f5f95..44c2a2b4d 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -262,6 +262,7 @@ struct riscv_info { unsigned int (*data_bits)(struct target *target); COMMAND_HELPER((*print_info), struct target *target); + COMMAND_HELPER((*exec_fence), struct target *target); /* Storage for arch_info of non-custom registers. */ riscv_reg_info_t shared_reg_info; @@ -321,6 +322,8 @@ struct riscv_info { bool wp_allow_equality_match_trigger; bool wp_allow_napot_trigger; bool wp_allow_ge_lt_trigger; + + bool auto_fence; }; COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,