target/riscv: fix register cache flushing

Since writing a register can make some GPRs dirty (e.g. S0, S1), registers
should be flushed in reverse order, so GPRs are flushed last.

Change-Id: Ice352a4df4ae064619c0f9905db634a7b57e4711
Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
Evgeniy Naydanov 2023-04-24 13:59:54 +03:00
parent 431deec8c9
commit 919a98a05b
1 changed files with 22 additions and 9 deletions

View File

@ -1495,6 +1495,15 @@ static int old_or_new_riscv_poll(struct target *target)
return riscv_openocd_poll(target); return riscv_openocd_poll(target);
} }
static struct reg *get_reg_cache_entry(struct target *target,
unsigned int number)
{
assert(target->reg_cache);
assert(target->reg_cache->reg_list);
assert(number < target->reg_cache->num_regs);
return &target->reg_cache->reg_list[number];
}
int riscv_flush_registers(struct target *target) int riscv_flush_registers(struct target *target)
{ {
RISCV_INFO(r); RISCV_INFO(r);
@ -1502,21 +1511,25 @@ int riscv_flush_registers(struct target *target)
if (!target->reg_cache) if (!target->reg_cache)
return ERROR_OK; return ERROR_OK;
LOG_DEBUG("[%s]", target_name(target)); LOG_TARGET_DEBUG(target, "");
for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) { /* Writing non-GPR registers may require progbuf execution, and some GPRs
struct reg *reg = &target->reg_cache->reg_list[number]; * may become dirty in the process (e.g. S0, S1). For that reason, flush
* registers in reverse order, so that GPRs are flushed last.
*/
for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
struct reg *reg = get_reg_cache_entry(target, number);
if (reg->valid && reg->dirty) { if (reg->valid && reg->dirty) {
uint64_t value = buf_get_u64(reg->value, 0, reg->size); riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
LOG_DEBUG("[%s] %s is dirty; write back 0x%" PRIx64,
target_name(target), reg->name, value); LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
int result = r->set_register(target, number, value); reg->name, value);
if (result != ERROR_OK) if (r->set_register(target, number, value) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
reg->dirty = false; reg->dirty = false;
} }
} }
LOG_TARGET_DEBUG(target, "Flush of register cache completed");
return ERROR_OK; return ERROR_OK;
} }