target/riscv: improve register caching (prep_*, cleanup_*)

Introduce riscv_write_register to prep_for_register/vector_access and
cleanup_after_register/vector_access.

Change-Id: I77a0a06ac6f12eceec309f0aff94aa77bd56ff55
Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
Evgeniy Naydanov 2023-04-24 15:11:11 +03:00
parent 8f3a617dc7
commit c822dc8194
1 changed files with 107 additions and 90 deletions

View File

@ -1065,35 +1065,54 @@ static int is_vector_reg(enum gdb_regno gdb_regno)
gdb_regno == GDB_REGNO_VLENB;
}
static int prep_for_register_access(struct target *target, riscv_reg_t *mstatus,
enum gdb_regno regno)
static int prep_for_register_access(struct target *target,
riscv_reg_t *orig_mstatus, enum gdb_regno regno)
{
if (is_fpu_reg(regno) || is_vector_reg(regno)) {
if (register_read_direct(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
assert(orig_mstatus);
if (!is_fpu_reg(regno) && !is_vector_reg(regno))
/* No special preparation needed */
return ERROR_OK;
LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s",
gdb_regno_name(regno));
/* FIXME: On a running target, there is no way to make sure mstatus won't
* change between reading and writing it, so
* if target->state != TARGET_HALTED an error should be returned here.
* However, this would not allow access to FPU registers on the running
* target.
* See https://github.com/riscv/riscv-openocd/pull/842#discussion_r1178500114
*/
if (riscv_get_register(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
return ERROR_FAIL;
if (is_fpu_reg(regno) && (*mstatus & MSTATUS_FS) == 0) {
if (register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(*mstatus, MSTATUS_FS, 1)) != ERROR_OK)
riscv_reg_t new_mstatus = *orig_mstatus;
riscv_reg_t field_mask = is_fpu_reg(regno) ? MSTATUS_FS : MSTATUS_VS;
if ((new_mstatus & field_mask) != 0)
return ERROR_OK;
new_mstatus = set_field(new_mstatus, field_mask, 1);
if (riscv_write_register(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
return ERROR_FAIL;
} else if (is_vector_reg(regno) && (*mstatus & MSTATUS_VS) == 0) {
if (register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(*mstatus, MSTATUS_VS, 1)) != ERROR_OK)
return ERROR_FAIL;
}
} else {
*mstatus = 0;
}
LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")",
gdb_regno_name(regno), new_mstatus);
return ERROR_OK;
}
static int cleanup_after_register_access(struct target *target,
riscv_reg_t mstatus, enum gdb_regno regno)
{
if ((is_fpu_reg(regno) && (mstatus & MSTATUS_FS) == 0) ||
(is_vector_reg(regno) && (mstatus & MSTATUS_VS) == 0))
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
return ERROR_FAIL;
if (!is_fpu_reg(regno) && !is_vector_reg(regno))
/* Mstatus was not changed for this register access. No need to restore it. */
return ERROR_OK;
LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus);
return riscv_write_register(target, GDB_REGNO_MSTATUS, mstatus);
}
typedef enum {
@ -1913,69 +1932,78 @@ static COMMAND_HELPER(riscv013_print_info, struct target *target)
return 0;
}
static int prep_for_vector_access(struct target *target, riscv_reg_t *saved_vtype,
riscv_reg_t *saved_vl, unsigned int *debug_vl, unsigned int *debug_vsew)
static int try_set_vsew(struct target *target, unsigned int *debug_vsew)
{
RISCV_INFO(r);
/* TODO: this continuous save/restore is terrible for performance. */
/* Write vtype and vl. */
/* Save vtype and vl. */
if (register_read_direct(target, saved_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
if (register_read_direct(target, saved_vl, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
while (true) {
unsigned int encoded_vsew;
if (riscv_xlen(target) == 64 && r->vsew64_supported != YNM_NO) {
encoded_vsew = 3;
*debug_vsew = 64;
} else {
encoded_vsew = 2;
*debug_vsew = 32;
}
unsigned int encoded_vsew =
(riscv_xlen(target) == 64 && r->vsew64_supported != YNM_NO) ? 3 : 2;
/* Set standard element width to match XLEN, for vmv instruction to move
* the least significant bits into a GPR. */
if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
* the least significant bits into a GPR.
*/
if (riscv_write_register(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
return ERROR_FAIL;
if (*debug_vsew == 64 && r->vsew64_supported == YNM_MAYBE) {
if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) {
/* Check that it's supported. */
uint64_t vtype;
if (register_read_direct(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
riscv_reg_t vtype;
if (riscv_get_register(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
if (vtype >> (riscv_xlen(target) - 1)) {
r->vsew64_supported = YNM_NO;
/* Try again. */
continue;
} else {
return try_set_vsew(target, debug_vsew);
}
r->vsew64_supported = YNM_YES;
}
}
break;
}
*debug_vsew = encoded_vsew == 3 ? 64 : 32;
return ERROR_OK;
}
static int prep_for_vector_access(struct target *target,
riscv_reg_t *orig_mstatus, riscv_reg_t *orig_vtype, riscv_reg_t *orig_vl,
unsigned int *debug_vl, unsigned int *debug_vsew)
{
assert(orig_mstatus);
assert(orig_vtype);
assert(orig_vl);
assert(debug_vl);
assert(debug_vsew);
RISCV_INFO(r);
if (target->state != TARGET_HALTED) {
LOG_TARGET_ERROR(target,
"Unable to access vector register: target not halted");
return ERROR_FAIL;
}
if (prep_for_register_access(target, orig_mstatus, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
/* Save vtype and vl. */
if (riscv_get_register(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
if (riscv_get_register(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
if (try_set_vsew(target, debug_vsew) != ERROR_OK)
return ERROR_FAIL;
/* Set the number of elements to be updated with results from a vector
* instruction, for the vslide1down instruction.
* Set it so the entire V register is updated. */
*debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew);
if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
return riscv_write_register(target, GDB_REGNO_VL, *debug_vl);
}
static int cleanup_after_vector_access(struct target *target, riscv_reg_t vtype,
riscv_reg_t vl)
static int cleanup_after_vector_access(struct target *target,
riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl)
{
/* Restore vtype and vl. */
if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
if (riscv_write_register(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
return ERROR_FAIL;
if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK)
if (riscv_write_register(target, GDB_REGNO_VL, vl) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL);
}
static int riscv013_get_register_buf(struct target *target,
@ -1989,13 +2017,10 @@ static int riscv013_get_register_buf(struct target *target,
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t mstatus;
if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t vtype, vl;
riscv_reg_t mstatus, vtype, vl;
unsigned int debug_vl, debug_vsew;
if (prep_for_vector_access(target, &vtype, &vl, &debug_vl, &debug_vsew) != ERROR_OK)
if (prep_for_vector_access(target, &mstatus, &vtype, &vl,
&debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
unsigned vnum = regno - GDB_REGNO_V0;
@ -2022,16 +2047,14 @@ static int riscv013_get_register_buf(struct target *target,
return ERROR_FAIL;
buf_set_u64(value, debug_vsew * i, debug_vsew, v);
} else {
LOG_TARGET_ERROR(target, "Failed to execute vmv/vslide1down while reading %s",
LOG_TARGET_ERROR(target,
"Failed to execute vmv/vslide1down while reading %s",
gdb_regno_name(regno));
break;
}
}
if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
return result;
@ -2048,13 +2071,10 @@ static int riscv013_set_register_buf(struct target *target,
if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t mstatus;
if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t vtype, vl;
riscv_reg_t mstatus, vtype, vl;
unsigned int debug_vl, debug_vsew;
if (prep_for_vector_access(target, &vtype, &vl, &debug_vl, &debug_vsew) != ERROR_OK)
if (prep_for_vector_access(target, &mstatus, &vtype, &vl,
&debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
unsigned vnum = regno - GDB_REGNO_V0;
@ -2072,10 +2092,7 @@ static int riscv013_set_register_buf(struct target *target,
break;
}
if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
return result;