From 5cb2f200f828333b9daf5a2eeb11b0e26e635288 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 9 May 2019 11:32:04 -0700 Subject: Simultaneous halt (#372) * WIP Change-Id: I4f50dced77e9ded4a58ab152824a841a73bc0dc1 * riscv_halt() only halt harts that are running. Progress towards simultaneous halt. Change-Id: I749b6d9ba5e77aa7aca4342c7af841312b78be0e * -rtos riscv passes. But dual gdb is failing again. Change-Id: I1747ba42ce3f3062f6e8c28a75ac40e17f80e980 * Dual gdb works again. -rtos riscv still works. Change-Id: Idddddda79e5918b26e181384def1a305ecceced2 * -rtos hwthread almost completely works. Change-Id: I845feb0bd93484e28ca8620f4760c234d4ce5310 * Maybe better? Change-Id: I669c67e83acf1b749bfb534d3b3c0915c129d686 * All three methods work. Change-Id: If77074fa43f6420d1ec9b594fe366415f5a41f2c * Fix hitting hardware triggers with `-rtos riscv`. Change-Id: I8d4600e1c66fa0e3b9d986b96a5973d09f40735c * Fix halting dual core E31. Change-Id: Ic2d885e027312b68e2f3c6854957fbfee09f814b * Not addressing this TODO right now. Change-Id: Ic7c0d32424068ae1de04d37d15a411c1957207c4 * Remove duplicate line. Change-Id: I14fe8d422f23c97afdaa20a02c0e3ab568219467 --- src/target/riscv/riscv-013.c | 112 +++++++++++------- src/target/riscv/riscv.c | 266 ++++++++++++++++++++++++------------------- src/target/riscv/riscv.h | 13 +-- 3 files changed, 221 insertions(+), 170 deletions(-) diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 27e20d7..41af815 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -41,7 +41,8 @@ static int riscv013_get_register(struct target *target, riscv_reg_t *value, int hid, int rid); static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value); static int riscv013_select_current_hart(struct target *target); -static int riscv013_halt_current_hart(struct target *target); +static int riscv013_halt_prep(struct target *target); +static int riscv013_halt_go(struct target *target); static int riscv013_resume_go(struct target *target); static int riscv013_step_current_hart(struct target *target); static int riscv013_on_halt(struct target *target); @@ -1586,7 +1587,7 @@ static int examine(struct target *target) bool halted = riscv_is_halted(target); if (!halted) { - if (riscv013_halt_current_hart(target) != ERROR_OK) { + if (riscv013_halt_go(target) != ERROR_OK) { LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i); return ERROR_FAIL; } @@ -1701,11 +1702,12 @@ static int init_target(struct command_context *cmd_ctx, generic_info->set_register = &riscv013_set_register; generic_info->select_current_hart = &riscv013_select_current_hart; generic_info->is_halted = &riscv013_is_halted; - generic_info->halt_current_hart = &riscv013_halt_current_hart; generic_info->resume_go = &riscv013_resume_go; generic_info->step_current_hart = &riscv013_step_current_hart; generic_info->on_halt = &riscv013_on_halt; generic_info->resume_prep = &riscv013_resume_prep; + generic_info->halt_prep = &riscv013_halt_prep; + generic_info->halt_go = &riscv013_halt_go; generic_info->on_step = &riscv013_on_step; generic_info->halt_reason = &riscv013_halt_reason; generic_info->read_debug_buffer = &riscv013_read_debug_buffer; @@ -2897,7 +2899,7 @@ struct target_type riscv013_target = { .examine = examine, .poll = &riscv_openocd_poll, - .halt = &riscv_openocd_halt, + .halt = &riscv_halt, .resume = &riscv_resume, .step = &riscv_openocd_step, @@ -2985,42 +2987,6 @@ static int riscv013_select_current_hart(struct target *target) return result; } -static int riscv013_halt_current_hart(struct target *target) -{ - RISCV_INFO(r); - LOG_DEBUG("halting hart %d", r->current_hartid); - if (riscv_is_halted(target)) - LOG_ERROR("Hart %d is already halted!", r->current_hartid); - - /* Issue the halt command, and then wait for the current hart to halt. */ - uint32_t dmcontrol; - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1); - dmi_write(target, DMI_DMCONTROL, dmcontrol); - for (size_t i = 0; i < 256; ++i) - if (riscv_is_halted(target)) - break; - - if (!riscv_is_halted(target)) { - uint32_t dmstatus; - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) - return ERROR_FAIL; - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - - LOG_ERROR("unable to halt hart %d", r->current_hartid); - LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); - LOG_ERROR(" dmstatus =0x%08x", dmstatus); - return ERROR_FAIL; - } - - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0); - dmi_write(target, DMI_DMCONTROL, dmcontrol); - - return ERROR_OK; -} - /* Select all harts that were prepped and that are selectable, clearing the * prepped flag on the harts that actually were selected. */ static int select_prepped_harts(struct target *target, bool *use_hasel) @@ -3047,6 +3013,7 @@ static int select_prepped_harts(struct target *target, bool *use_hasel) riscv013_info_t *info = get_info(t); unsigned index = info->index; LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped); + r->selected = r->prepped; if (r->prepped) { hawindow[index / 32] |= 1 << (index % 32); r->prepped = false; @@ -3072,6 +3039,63 @@ static int select_prepped_harts(struct target *target, bool *use_hasel) return ERROR_OK; } +static int riscv013_halt_prep(struct target *target) +{ + return ERROR_OK; +} + +static int riscv013_halt_go(struct target *target) +{ + bool use_hasel = false; + if (!riscv_rtos_enabled(target)) { + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; + } + + RISCV_INFO(r); + LOG_DEBUG("halting hart %d", r->current_hartid); + + /* Issue the halt command, and then wait for the current hart to halt. */ + uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_HALTREQ; + if (use_hasel) + dmcontrol |= DMI_DMCONTROL_HASEL; + dmcontrol = set_hartsel(dmcontrol, r->current_hartid); + dmi_write(target, DMI_DMCONTROL, dmcontrol); + for (size_t i = 0; i < 256; ++i) + if (riscv_is_halted(target)) + break; + + if (!riscv_is_halted(target)) { + uint32_t dmstatus; + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + return ERROR_FAIL; + if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) + return ERROR_FAIL; + + LOG_ERROR("unable to halt hart %d", r->current_hartid); + LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); + LOG_ERROR(" dmstatus =0x%08x", dmstatus); + return ERROR_FAIL; + } + + dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0); + dmi_write(target, DMI_DMCONTROL, dmcontrol); + + if (use_hasel) { + target_list_t *entry; + dm013_info_t *dm = get_dm(target); + list_for_each_entry(entry, &dm->target_list, list) { + struct target *t = entry->target; + t->state = TARGET_HALTED; + if (t->debug_reason == DBG_REASON_NOTHALTED) + t->debug_reason = DBG_REASON_DBGRQ; + } + } + /* The "else" case is handled in halt_go(). */ + + return ERROR_OK; +} + static int riscv013_resume_go(struct target *target) { bool use_hasel = false; @@ -3634,7 +3658,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target, if (step) { LOG_ERROR(" was stepping, halting"); - riscv013_halt_current_hart(target); + riscv_halt(target); return ERROR_OK; } @@ -3731,7 +3755,7 @@ int riscv013_test_compliance(struct target *target) /* TODO: test that hamask registers exist if hasel does. */ /* haltreq */ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); /* This bit is not actually readable according to the spec, so nothing to check.*/ /* DMSTATUS */ @@ -3742,7 +3766,7 @@ int riscv013_test_compliance(struct target *target) COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false)); /* Halt all harts again so the test can continue.*/ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */ uint32_t hartinfo; @@ -4107,7 +4131,7 @@ int riscv013_test_compliance(struct target *target) */ /* Halt every hart for any follow-up tests*/ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); uint32_t failed_tests = total_tests - passed_tests; if (total_tests == passed_tests) { diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 34854d0..47c8041 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -431,12 +431,6 @@ static void riscv_deinit_target(struct target *target) target->arch_info = NULL; } -static int oldriscv_halt(struct target *target) -{ - struct target_type *tt = get_target_type(target); - return tt->halt(target); -} - static void trigger_from_breakpoint(struct trigger *trigger, const struct breakpoint *breakpoint) { @@ -820,6 +814,8 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi { struct watchpoint *wp = target->watchpoints; + if (riscv_rtos_enabled(target)) + riscv_set_current_hartid(target, target->rtos->current_thread - 1); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); /*TODO instead of disassembling the instruction that we think caused the @@ -957,13 +953,124 @@ static int old_or_new_riscv_poll(struct target *target) return riscv_openocd_poll(target); } -static int old_or_new_riscv_halt(struct target *target) +int halt_prep(struct target *target) { RISCV_INFO(r); - if (r->is_halted == NULL) - return oldriscv_halt(target); - else - return riscv_openocd_halt(target); + for (int i = 0; i < riscv_count_harts(target); ++i) { + if (!riscv_hart_enabled(target, i)) + continue; + + LOG_DEBUG("prep hart %d", i); + if (riscv_set_current_hartid(target, i) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("Hart %d is already halted.", i); + } else { + if (r->halt_prep(target) != ERROR_OK) + return ERROR_FAIL; + r->prepped = true; + } + } + return ERROR_OK; +} + +int riscv_halt_go_all_harts(struct target *target) +{ + RISCV_INFO(r); + for (int i = 0; i < riscv_count_harts(target); ++i) { + if (!riscv_hart_enabled(target, i)) + continue; + + if (riscv_set_current_hartid(target, i) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("Hart %d is already halted.", i); + } else { + if (r->halt_go(target) != ERROR_OK) + return ERROR_FAIL; + } + } + + riscv_invalidate_register_cache(target); + + return ERROR_OK; +} + +int halt_go(struct target *target) +{ + riscv_info_t *r = riscv_info(target); + int result; + if (r->is_halted == NULL) { + struct target_type *tt = get_target_type(target); + result = tt->halt(target); + } else { + result = riscv_halt_go_all_harts(target); + } + target->state = TARGET_HALTED; + if (target->debug_reason == DBG_REASON_NOTHALTED) + target->debug_reason = DBG_REASON_DBGRQ; + + return result; +} + +static int halt_finish(struct target *target) +{ + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); +} + +int riscv_halt(struct target *target) +{ + RISCV_INFO(r); + + if (r->is_halted == NULL) { + struct target_type *tt = get_target_type(target); + return tt->halt(target); + } + + LOG_DEBUG("[%d] halting all harts", target->coreid); + + int result = ERROR_OK; + if (target->smp) { + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + if (halt_prep(t) != ERROR_OK) + result = ERROR_FAIL; + } + + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + riscv_info_t *i = riscv_info(t); + if (i->prepped) { + if (halt_go(t) != ERROR_OK) + result = ERROR_FAIL; + } + } + + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + if (halt_finish(t) != ERROR_OK) + return ERROR_FAIL; + } + + } else { + if (halt_prep(target) != ERROR_OK) + result = ERROR_FAIL; + if (halt_go(target) != ERROR_OK) + result = ERROR_FAIL; + if (halt_finish(target) != ERROR_OK) + return ERROR_FAIL; + } + + if (riscv_rtos_enabled(target)) { + if (r->rtos_hartid != -1) { + LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); + target->rtos->current_threadid = r->rtos_hartid + 1; + target->rtos->current_thread = r->rtos_hartid + 1; + } else + LOG_DEBUG("halt requested, but no known RTOS hartid"); + } + + return result; } static int riscv_assert_reset(struct target *target) @@ -998,6 +1105,10 @@ int riscv_resume_prep_all_harts(struct target *target) LOG_DEBUG(" hart %d requested resume, but was already resumed", i); } } + + LOG_DEBUG("[%d] mark as prepped", target->coreid); + r->prepped = true; + return ERROR_OK; } @@ -1087,6 +1198,7 @@ static int resume_finish(struct target *target) register_cache_invalidate(target->reg_cache); target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; return target_call_event_callbacks(target, TARGET_EVENT_RESUMED); } @@ -1322,7 +1434,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms); LOG_ERROR(" now = 0x%08x", (uint32_t) now); LOG_ERROR(" start = 0x%08x", (uint32_t) start); - oldriscv_halt(target); + riscv_halt(target); old_or_new_riscv_poll(target); return ERROR_TARGET_TIMEOUT; } @@ -1401,6 +1513,7 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) } else if (target->state != TARGET_RUNNING && !halted) { LOG_DEBUG(" triggered running"); target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; return RPH_DISCOVERED_RUNNING; } @@ -1457,14 +1570,21 @@ int riscv_openocd_poll(struct target *target) } LOG_DEBUG(" hart %d halted", halted_hart); - /* If we're here then at least one hart triggered. That means - * we want to go and halt _every_ hart in the system, as that's - * the invariant we hold here. Some harts might have already - * halted (as we're either in single-step mode or they also - * triggered a breakpoint), so don't attempt to halt those - * harts. */ - for (int i = 0; i < riscv_count_harts(target); ++i) - riscv_halt_one_hart(target, i); + target->state = TARGET_HALTED; + if (set_debug_reason(target, halted_hart) != ERROR_OK) + return ERROR_FAIL; + + target->rtos->current_threadid = halted_hart + 1; + target->rtos->current_thread = halted_hart + 1; + riscv_set_rtos_hartid(target, halted_hart); + + /* If we're here then at least one hart triggered. That means we want + * to go and halt _every_ hart (configured with -rtos riscv) in the + * system, as that's the invariant we hold here. Some harts might have + * already halted (as we're either in single-step mode or they also + * triggered a breakpoint), so don't attempt to halt those harts. + * riscv_halt() will do all that for us. */ + riscv_halt(target); } else if (target->smp) { bool halt_discovered = false; @@ -1481,6 +1601,7 @@ int riscv_openocd_poll(struct target *target) break; case RPH_DISCOVERED_RUNNING: t->state = TARGET_RUNNING; + t->debug_reason = DBG_REASON_NOTHALTED; break; case RPH_DISCOVERED_HALTED: halt_discovered = true; @@ -1495,24 +1616,6 @@ int riscv_openocd_poll(struct target *target) } if (halt_discovered) { - LOG_DEBUG("Halt other targets in this SMP group."); - i = 0; - for (struct target_list *list = target->head; list != NULL; - list = list->next, i++) { - struct target *t = list->target; - riscv_info_t *r = riscv_info(t); - if (t->state != TARGET_HALTED) { - if (riscv_halt_one_hart(t, r->current_hartid) != ERROR_OK) - return ERROR_FAIL; - t->state = TARGET_HALTED; - if (set_debug_reason(t, r->current_hartid) != ERROR_OK) - return ERROR_FAIL; - newly_halted[i] = true; - } - } - - /* Now that we have all our ducks in a row, tell the higher layers - * what just happened. */ i = 0; for (struct target_list *list = target->head; list != NULL; list = list->next, i++) { @@ -1520,6 +1623,9 @@ int riscv_openocd_poll(struct target *target) if (newly_halted[i]) target_call_event_callbacks(t, TARGET_EVENT_HALTED); } + + LOG_DEBUG("Halt other targets in this SMP group."); + riscv_halt(target); } return ERROR_OK; @@ -1533,20 +1639,12 @@ int riscv_openocd_poll(struct target *target) halted_hart = riscv_current_hartid(target); LOG_DEBUG(" hart %d halted", halted_hart); - } - - target->state = TARGET_HALTED; - if (set_debug_reason(target, halted_hart) != ERROR_OK) - return ERROR_FAIL; - if (riscv_rtos_enabled(target)) { - target->rtos->current_threadid = halted_hart + 1; - target->rtos->current_thread = halted_hart + 1; - riscv_set_rtos_hartid(target, halted_hart); + if (set_debug_reason(target, halted_hart) != ERROR_OK) + return ERROR_FAIL; + target->state = TARGET_HALTED; } - target->state = TARGET_HALTED; - if (target->debug_reason == DBG_REASON_BREAKPOINT) { int retval; if (riscv_semihosting(target, &retval) != 0) @@ -1557,44 +1655,6 @@ int riscv_openocd_poll(struct target *target) return ERROR_OK; } -int riscv_openocd_halt(struct target *target) -{ - RISCV_INFO(r); - int result; - - LOG_DEBUG("[%d] halting all harts", target->coreid); - - if (target->smp) { - LOG_DEBUG("Halt other targets in this SMP group."); - struct target_list *targets = target->head; - result = ERROR_OK; - while (targets) { - struct target *t = targets->target; - targets = targets->next; - if (t->state != TARGET_HALTED) { - if (riscv_halt_all_harts(t) != ERROR_OK) - result = ERROR_FAIL; - } - } - } else { - result = riscv_halt_all_harts(target); - } - - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid != -1) { - LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); - target->rtos->current_threadid = r->rtos_hartid + 1; - target->rtos->current_thread = r->rtos_hartid + 1; - } else - LOG_DEBUG("halt requested, but no known RTOS hartid"); - } - - target->state = TARGET_HALTED; - target->debug_reason = DBG_REASON_DBGRQ; - target_call_event_callbacks(target, TARGET_EVENT_HALTED); - return result; -} - int riscv_openocd_step( struct target *target, int current, @@ -2178,7 +2238,7 @@ struct target_type riscv_target = { /* poll current target status */ .poll = old_or_new_riscv_poll, - .halt = old_or_new_riscv_halt, + .halt = riscv_halt, .resume = riscv_resume, .step = old_or_new_riscv_step, @@ -2228,36 +2288,6 @@ void riscv_info_init(struct target *target, riscv_info_t *r) } } -int riscv_halt_all_harts(struct target *target) -{ - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - - riscv_halt_one_hart(target, i); - } - - riscv_invalidate_register_cache(target); - - return ERROR_OK; -} - -int riscv_halt_one_hart(struct target *target, int hartid) -{ - RISCV_INFO(r); - LOG_DEBUG("halting hart %d", hartid); - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) - return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG(" hart %d requested halt, but was already halted", hartid); - return ERROR_OK; - } - - int result = r->halt_current_hart(target); - register_cache_invalidate(target->reg_cache); - return result; -} - static int riscv_resume_go_all_harts(struct target *target) { RISCV_INFO(r); diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 0662efd..fe1c578 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -99,6 +99,8 @@ typedef struct { /* This target has been prepped and is ready to step/resume. */ bool prepped; + /* This target was selected using hasel. */ + bool selected; /* Helper functions that target the various RISC-V debug spec * implementations. */ @@ -108,7 +110,6 @@ typedef struct { uint64_t value); int (*select_current_hart)(struct target *); bool (*is_halted)(struct target *target); - int (*halt_current_hart)(struct target *); /* Resume this target, as well as every other prepped target that can be * resumed near-simultaneously. Clear the prepped flag on any target that * was resumed. */ @@ -118,6 +119,8 @@ typedef struct { /* Get this target as ready as possible to resume, without actually * resuming. */ int (*resume_prep)(struct target *target); + int (*halt_prep)(struct target *target); + int (*halt_go)(struct target *target); int (*on_step)(struct target *target); enum riscv_halt_reason (*halt_reason)(struct target *target); int (*write_debug_buffer)(struct target *target, unsigned index, @@ -179,7 +182,7 @@ void select_dmi_via_bscan(struct target *target); /*** OpenOCD Interface */ int riscv_openocd_poll(struct target *target); -int riscv_openocd_halt(struct target *target); +int riscv_halt(struct target *target); int riscv_resume( struct target *target, @@ -204,12 +207,6 @@ int riscv_openocd_deassert_reset(struct target *target); /* Initializes the shared RISC-V structure. */ void riscv_info_init(struct target *target, riscv_info_t *r); -/* Run control, possibly for multiple harts. The _all_harts versions resume - * all the enabled harts, which when running in RTOS mode is all the harts on - * the system. */ -int riscv_halt_all_harts(struct target *target); -int riscv_halt_one_hart(struct target *target, int hartid); - /* Steps the hart that's currently selected in the RTOS, or if there is no RTOS * then the only hart. */ int riscv_step_rtos_hart(struct target *target); -- cgit v1.1