From 628820ef3946c730149355e8ac5950104444f229 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 26 Jun 2023 17:33:18 -0700 Subject: [PATCH] Let debugger control hart availability This change lets me test OpenOCD's behavior when harts become available. It only affects how things look to the debugger. Harts that are "unavailable" still execute code as usual. Control is implemented through the 2 LSBs of the DMCUSTOM register in the Debug Module. --- riscv/debug_module.cc | 42 +++++++++++++++++++++++++++++++++--------- riscv/debug_module.h | 6 ++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/riscv/debug_module.cc b/riscv/debug_module.cc index f7163ef41b..b81e283679 100644 --- a/riscv/debug_module.cc +++ b/riscv/debug_module.cc @@ -43,7 +43,7 @@ debug_module_t::debug_module_t(simif_t *sim, const debug_module_config_t &config // them because I'm too lazy to add the code to just ignore accesses. hart_state(1 << field_width(sim->get_cfg().max_hartid() + 1)), hart_array_mask(sim->get_cfg().max_hartid() + 1), - rti_remaining(0) + rti_remaining(0), hart_available_state{true, true} { D(fprintf(stderr, "debug_data_start=0x%x\n", debug_data_start)); D(fprintf(stderr, "debug_progbuf_start=0x%x\n", debug_progbuf_start)); @@ -202,7 +202,8 @@ bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes) if (hart_state[id].haltgroup) { for (const auto& [hart_id, hart] : sim->get_harts()) { if (!hart_state[hart_id].halted && - hart_state[hart_id].haltgroup == hart_state[id].haltgroup) { + hart_state[hart_id].haltgroup == hart_state[id].haltgroup && + hart_available(hart_id)) { hart->halt_request = hart->HR_GROUP; // TODO: What if the debugger comes and writes dmcontrol before the // halt occurs? @@ -337,6 +338,13 @@ void debug_module_t::sb_write() } } +bool debug_module_t::hart_available(unsigned hart_id) const +{ + if (hart_id < sizeof(hart_available_state) / sizeof(*hart_available_state)) + return hart_available_state[hart_id]; + return true; +} + bool debug_module_t::dmi_read(unsigned address, uint32_t *value) { uint32_t result = 0; @@ -391,6 +399,8 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value) dmstatus.allnonexistant = true; dmstatus.allresumeack = true; dmstatus.anyresumeack = false; + dmstatus.allunavail = true; + dmstatus.anyunavail = false; for (const auto& [hart_id, hart] : sim->get_harts()) { if (hart_selected(hart_id)) { dmstatus.allnonexistant = false; @@ -399,12 +409,19 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value) } else { dmstatus.allresumeack = false; } + auto hart = sim->get_harts().at(hart_id); if (hart_state[hart_id].halted) { dmstatus.allrunning = false; dmstatus.anyhalted = true; + dmstatus.allunavail = false; + } else if (!hart_available(hart_id)) { + dmstatus.allrunning = false; + dmstatus.allhalted = false; + dmstatus.anyunavail = true; } else { dmstatus.allhalted = false; dmstatus.anyrunning = true; + dmstatus.allunavail = false; } } } @@ -414,9 +431,6 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value) // non-existant hartsel. dmstatus.anynonexistant = dmcontrol.hartsel >= sim->get_cfg().nprocs(); - dmstatus.allunavail = false; - dmstatus.anyunavail = false; - result = set_field(result, DM_DMSTATUS_IMPEBREAK, dmstatus.impebreak); result = set_field(result, DM_DMSTATUS_ALLHAVERESET, selected_hart_state().havereset); @@ -522,6 +536,10 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value) case DM_DMCS2: result = set_field(result, DM_DMCS2_GROUP, selected_hart_state().haltgroup); break; + case DM_CUSTOM: + result = set_field(result, 1, hart_available_state[0]); + result = set_field(result, 2, hart_available_state[1]); + break; default: result = 0; D(fprintf(stderr, "Unexpected. Returning Error.")); @@ -790,16 +808,18 @@ bool debug_module_t::dmi_write(unsigned address, uint32_t value) if (get_field(value, DM_DMCONTROL_ACKHAVERESET)) { hart_state[hart_id].havereset = false; } - hart->halt_request = dmcontrol.haltreq ? hart->HR_REGULAR : hart->HR_NONE; - if (dmcontrol.haltreq) { + if (dmcontrol.haltreq && hart_available(hart_id)) { + hart->halt_request = hart->HR_REGULAR; D(fprintf(stderr, "halt hart %d\n", hart_id)); + } else { + hart->halt_request = hart->HR_NONE; } - if (dmcontrol.resumereq) { + if (dmcontrol.resumereq && hart_available(hart_id)) { D(fprintf(stderr, "resume hart %d\n", hart_id)); debug_rom_flags[hart_id] |= (1 << DEBUG_ROM_FLAG_RESUME); hart_state[hart_id].resumeack = false; } - if (dmcontrol.hartreset) { + if (dmcontrol.hartreset && hart_available(hart_id)) { hart->reset(); } } @@ -903,6 +923,10 @@ bool debug_module_t::dmi_write(unsigned address, uint32_t value) selected_hart_state().haltgroup = get_field(value, DM_DMCS2_GROUP); } return true; + case DM_CUSTOM: + hart_available_state[0] = get_field(value, 1); + hart_available_state[1] = get_field(value, 2); + return true; } } return false; diff --git a/riscv/debug_module.h b/riscv/debug_module.h index 518f119df6..73bb7fa6a9 100644 --- a/riscv/debug_module.h +++ b/riscv/debug_module.h @@ -185,6 +185,12 @@ class debug_module_t : public abstract_device_t size_t selected_hart_id() const; hart_debug_state_t& selected_hart_state(); + + /* Whether the first 2 harts are available is controllable through DMCUSTOM, + * where bit 0 corresponds to hart 0, etc. When a bit is one the hart + * available. Otherwise it is unavailable. */ + bool hart_available_state[2]; + bool hart_available(unsigned hart_id) const; }; #endif