diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 6ab409bcd5..9f4f12c1b8 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -128,6 +128,17 @@ struct trigger { int unique_id; }; +struct tdata2_cache { + struct list_head elem_tdata2; + riscv_reg_t tdata2; +}; + +struct tdata1_cache { + riscv_reg_t tdata1; + struct list_head tdata2_cache_head; + struct list_head elem_tdata1; +}; + /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; @@ -485,6 +496,26 @@ static void riscv_free_registers(struct target *target) } } + +static void free_wp_triggers_cache(struct target *target) +{ + RISCV_INFO(r); + + for (unsigned int i = 0; i < r->trigger_count; ++i) { + struct tdata1_cache *elem_1, *tmp_1; + list_for_each_entry_safe(elem_1, tmp_1, &r->wp_triggers_cache[i], elem_tdata1) { + struct tdata2_cache *elem_2, *tmp_2; + list_for_each_entry_safe(elem_2, tmp_2, &elem_1->tdata2_cache_head, elem_tdata2) { + list_del(&elem_2->elem_tdata2); + free(elem_2); + } + list_del(&elem_1->elem_tdata1); + free(elem_1); + } + } + free(r->wp_triggers_cache); +} + static void riscv_deinit_target(struct target *target) { LOG_TARGET_DEBUG(target, "riscv_deinit_target()"); @@ -499,6 +530,7 @@ static void riscv_deinit_target(struct target *target) tt->deinit_target(target); riscv_free_registers(target); + free_wp_triggers_cache(target); if (!info) return; @@ -633,8 +665,8 @@ static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdat LOG_TARGET_DEBUG(target, "wrote 0x%" PRIx64 " to tdata2 but read back 0x%" PRIx64, tdata2, tdata2_rb); - - riscv_set_register(target, GDB_REGNO_TDATA1, 0); + if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK) + return ERROR_FAIL; return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -700,6 +732,99 @@ static void log_trigger_request_info(struct trigger_request_info trig_info) trig_info.tdata1, trig_info.tdata2, trig_info.tdata1_ignore_mask); }; +static struct tdata1_cache *create_new_tdata1_cache(struct list_head *tdata1_cache_head, riscv_reg_t tdata1) +{ + struct tdata1_cache *elem = (struct tdata1_cache *)calloc(1, sizeof(struct tdata1_cache)); + elem->tdata1 = tdata1; + INIT_LIST_HEAD(&elem->tdata2_cache_head); + list_add_tail(&elem->elem_tdata1, tdata1_cache_head); + return elem; +} + +static void create_new_tdata2_cache(struct list_head *tdata2_cache_head, riscv_reg_t tdata2) +{ + struct tdata2_cache * const elem = calloc(1, sizeof(struct tdata2_cache)); + elem->tdata2 = tdata2; + list_add(&elem->elem_tdata2, tdata2_cache_head); +} + +struct tdata2_cache *find_call_in_tdata2_cache(struct list_head *tdata2_cache_head, riscv_reg_t find_tdata2) +{ + struct tdata2_cache *elem_2; + list_for_each_entry(elem_2, tdata2_cache_head, elem_tdata2) { + if (elem_2->tdata2 == find_tdata2) + return elem_2; + } + return NULL; +} + +struct tdata1_cache *find_tdata1(struct list_head *tdata1_cache_head, riscv_reg_t find_tdata1) +{ + struct tdata1_cache *elem_1; + list_for_each_entry(elem_1, tdata1_cache_head, elem_tdata1) { + if (elem_1->tdata1 == find_tdata1) + return elem_1; + } + return NULL; +} + +static void create_wp_trigger_cache(struct target *target) +{ + RISCV_INFO(r); + + r->wp_triggers_cache = (struct list_head *)calloc(r->trigger_count, + sizeof(struct list_head)); + for (unsigned int i = 0; i < r->trigger_count; ++i) + INIT_LIST_HEAD(&r->wp_triggers_cache[i]); +} + +static void add_use_wp_trigger_in_cache(struct target *target, unsigned int idx, riscv_reg_t tdata1, + riscv_reg_t tdata2, int error_code) +{ + RISCV_INFO(r); + + if (error_code != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return; + + struct tdata1_cache *tdata1_cache = find_tdata1(&r->wp_triggers_cache[idx], tdata1); + if (!tdata1_cache) { + tdata1_cache = create_new_tdata1_cache(&r->wp_triggers_cache[idx], tdata1); + } else { + struct tdata2_cache *tdata2_cache = find_call_in_tdata2_cache(&tdata1_cache->tdata2_cache_head, tdata2); + if (tdata2_cache) { + list_move(&tdata2_cache->elem_tdata2, &tdata1_cache->tdata2_cache_head); + return; + } + } + create_new_tdata2_cache(&tdata1_cache->tdata2_cache_head, tdata2); +} + +static bool is_use_wp_trigger_in_cache(struct target *target, unsigned int idx, + riscv_reg_t tdata1, riscv_reg_t tdata2) +{ + RISCV_INFO(r); + + struct tdata1_cache *tdata1_cache = find_tdata1(&r->wp_triggers_cache[idx], tdata1); + if (!tdata1_cache) + return false; + struct tdata2_cache *tdata2_cache = find_call_in_tdata2_cache(&tdata1_cache->tdata2_cache_head, tdata2); + if (!tdata2_cache) + return false; + assert(tdata1_cache->tdata1 == tdata1 && tdata2_cache->tdata2 == tdata2); + return true; +} + +static int try_use_trigger_and_cache_result(struct target *target, unsigned int idx, riscv_reg_t tdata1, + riscv_reg_t tdata2, riscv_reg_t tdata1_ignore_mask) +{ + if (is_use_wp_trigger_in_cache(target, idx, tdata1, tdata2)) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + int ret = set_trigger(target, idx, tdata1, tdata2, tdata1_ignore_mask); + add_use_wp_trigger_in_cache(target, idx, tdata1, tdata2, ret); + return ret; +} + static int try_setup_single_match_trigger(struct target *target, struct trigger *trigger, struct trigger_request_info trig_info) { @@ -715,8 +840,9 @@ static int try_setup_single_match_trigger(struct target *target, for (unsigned int idx = 0; find_next_free_trigger(target, trigger_type, false, &idx) == ERROR_OK; ++idx) { - ret = set_trigger(target, idx, trig_info.tdata1, trig_info.tdata2, - trig_info.tdata1_ignore_mask); + ret = try_use_trigger_and_cache_result(target, idx, trig_info.tdata1, trig_info.tdata2, + trig_info.tdata1_ignore_mask); + if (ret == ERROR_OK) { r->trigger_unique_id[idx] = trigger->unique_id; return ERROR_OK; @@ -743,12 +869,21 @@ static int try_setup_chained_match_triggers(struct target *target, for (unsigned int idx = 0; find_next_free_trigger(target, trigger_type, true, &idx) == ERROR_OK; ++idx) { - ret = set_trigger(target, idx, t1.tdata1, t1.tdata2, - t1.tdata1_ignore_mask); - if (ret != ERROR_OK) + ret = try_use_trigger_and_cache_result(target, idx, t1.tdata1, t1.tdata2, + t1.tdata1_ignore_mask); + + switch (ret) { + case ERROR_OK: + break; + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: continue; - ret = set_trigger(target, idx + 1, t2.tdata1, t2.tdata2, - t2.tdata1_ignore_mask); + default: + return ret; + } + + ret = try_use_trigger_and_cache_result(target, idx + 1, t2.tdata1, t2.tdata2, + t2.tdata1_ignore_mask); + if (ret == ERROR_OK) { r->trigger_unique_id[idx] = trigger->unique_id; r->trigger_unique_id[idx + 1] = trigger->unique_id; @@ -5182,6 +5317,7 @@ int riscv_enumerate_triggers(struct target *target) r->triggers_enumerated = true; r->trigger_count = t; LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count); + create_wp_trigger_cache(target); return ERROR_OK; } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index a9bfe339f2..39b83fd7a0 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -137,6 +137,8 @@ struct riscv_info { /* The number of triggers per hart. */ unsigned int trigger_count; + struct list_head *wp_triggers_cache; + /* record the tinfo of each trigger */ unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];