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
This commit is contained in:
Tim Newsome 2019-05-09 11:32:04 -07:00 committed by GitHub
parent 44a4deb373
commit 5cb2f200f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 222 additions and 171 deletions

View File

@ -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) {

View File

@ -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);
if (set_debug_reason(target, halted_hart) != ERROR_OK)
return ERROR_FAIL;
target->state = TARGET_HALTED;
}
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);
}
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);

View File

@ -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);