Add support for vector register access (#448)
* WIP Change-Id: I0264a73b7f7d2ce89cc0b80692dbf81d9cdcc2fd * Reading v* registers appears to work. Can't really test it though, because gdb doesn't print them right. Change-Id: I8d66339371c564a493d32f15c3d114b738a455c5 * Total hack to communicate registers to gdb. Change-Id: Id06c819675f2a5bcaf751e322d95a7d71c633765 * Implement writing vector registers. Fixed reading vector registers. Change-Id: I8f06aa5ee5020b3213a4f68644c205c9d6b9d214 * Show gdb the actual size of the vector registers. This length may be different per hart. Change-Id: I92e95383da82ee7a5c995822a53d51b1ea933493 * Remove outdated todo comment. Change-Id: Ic9158b002858f0d15a6452773b095aa5f4501128 * Removed TODO comment. Filed #449 to track this. Change-Id: I5277b19e545df2024f34cda39158ddf7d0d89d47 * Nicely handle some errors reading/writing V regs. Change-Id: Ia7bb63a5f9433d9f7b46496b2c0994864cfc4a09
This commit is contained in:
parent
7cb8843794
commit
95462a8a35
File diff suppressed because it is too large
Load Diff
|
@ -76,6 +76,12 @@ enum gdb_regno {
|
|||
GDB_REGNO_FT11,
|
||||
GDB_REGNO_FPR31 = GDB_REGNO_FT11,
|
||||
GDB_REGNO_CSR0 = 65,
|
||||
GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
|
||||
|
@ -89,6 +95,18 @@ enum gdb_regno {
|
|||
GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0,
|
||||
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
|
||||
GDB_REGNO_PRIV = 4161,
|
||||
/* It's still undecided what register numbers GDB will actually use for
|
||||
* these. See
|
||||
* https://groups.google.com/a/groups.riscv.org/d/msg/sw-dev/7lQYiTUN9Ms/gTxGhzaYBQAJ
|
||||
*/
|
||||
GDB_REGNO_V0, GDB_REGNO_V1, GDB_REGNO_V2, GDB_REGNO_V3,
|
||||
GDB_REGNO_V4, GDB_REGNO_V5, GDB_REGNO_V6, GDB_REGNO_V7,
|
||||
GDB_REGNO_V8, GDB_REGNO_V9, GDB_REGNO_V10, GDB_REGNO_V11,
|
||||
GDB_REGNO_V12, GDB_REGNO_V13, GDB_REGNO_V14, GDB_REGNO_V15,
|
||||
GDB_REGNO_V16, GDB_REGNO_V17, GDB_REGNO_V18, GDB_REGNO_V19,
|
||||
GDB_REGNO_V20, GDB_REGNO_V21, GDB_REGNO_V22, GDB_REGNO_V23,
|
||||
GDB_REGNO_V24, GDB_REGNO_V25, GDB_REGNO_V26, GDB_REGNO_V27,
|
||||
GDB_REGNO_V28, GDB_REGNO_V29, GDB_REGNO_V30, GDB_REGNO_V31,
|
||||
GDB_REGNO_COUNT
|
||||
};
|
||||
|
||||
|
|
|
@ -323,3 +323,33 @@ static uint32_t auipc(unsigned int dest)
|
|||
{
|
||||
return MATCH_AUIPC | (dest << 7);
|
||||
}
|
||||
|
||||
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) __attribute__((unused));
|
||||
static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm)
|
||||
{
|
||||
return (bits(imm, 10, 0) << 20) |
|
||||
(src << 15) |
|
||||
(dest << 7) |
|
||||
MATCH_VSETVLI;
|
||||
}
|
||||
|
||||
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused));
|
||||
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2)
|
||||
{
|
||||
return (vs2 << 20) | (rd << 7) | MATCH_VMV_X_S;
|
||||
}
|
||||
|
||||
static uint32_t vmv_s_x(unsigned int vd, unsigned int vs2) __attribute__((unused));
|
||||
static uint32_t vmv_s_x(unsigned int vd, unsigned int rs1)
|
||||
{
|
||||
return (rs1 << 15) | (vd << 7) | MATCH_VMV_S_X;
|
||||
}
|
||||
|
||||
static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
|
||||
unsigned int rs1, unsigned int vm) __attribute__((unused));
|
||||
static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
|
||||
unsigned int rs1, unsigned int vm)
|
||||
{
|
||||
return (vm << 25) | (vs2 << 20) | (rs1 << 15) | (vd << 7) |
|
||||
MATCH_VSLIDE1DOWN_VX;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,8 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
|
|||
if (riscv_program_ebreak(p) != ERROR_OK) {
|
||||
LOG_ERROR("Unable to write ebreak");
|
||||
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
|
||||
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]);
|
||||
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i,
|
||||
(long)p->debug_buffer[i], (long)p->debug_buffer[i]);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
|
|
|
@ -858,6 +858,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
|
|||
command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("%d-bit register %s not supported.", size,
|
||||
gdb_regno_name(number));
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
@ -877,6 +879,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
|
|||
assert(reg_info);
|
||||
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
|
||||
0xc000 + reg_info->custom_number);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
command |= flags;
|
||||
|
@ -895,6 +899,9 @@ static int register_read_abstract(struct target *target, uint64_t *value,
|
|||
if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
|
||||
!info->abstract_read_csr_supported)
|
||||
return ERROR_FAIL;
|
||||
/* The spec doesn't define abstract register numbers for vector registers. */
|
||||
if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint32_t command = access_register_command(target, number, size,
|
||||
AC_ACCESS_REGISTER_TRANSFER);
|
||||
|
@ -1053,6 +1060,54 @@ static int examine_progbuf(struct target *target)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int is_fpu_reg(uint32_t gdb_regno)
|
||||
{
|
||||
return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) ||
|
||||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) ||
|
||||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) ||
|
||||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR);
|
||||
}
|
||||
|
||||
static int is_vector_reg(uint32_t gdb_regno)
|
||||
{
|
||||
return (gdb_regno >= GDB_REGNO_V0 && gdb_regno <= GDB_REGNO_V31) ||
|
||||
gdb_regno == GDB_REGNO_VSTART ||
|
||||
gdb_regno == GDB_REGNO_VXSAT ||
|
||||
gdb_regno == GDB_REGNO_VXRM ||
|
||||
gdb_regno == GDB_REGNO_VL ||
|
||||
gdb_regno == GDB_REGNO_VTYPE ||
|
||||
gdb_regno == GDB_REGNO_VLENB;
|
||||
}
|
||||
|
||||
static int prep_for_register_access(struct target *target, uint64_t *mstatus,
|
||||
int regno)
|
||||
{
|
||||
if (is_fpu_reg(regno) || is_vector_reg(regno)) {
|
||||
if (register_read(target, 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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cleanup_after_register_access(struct target *target,
|
||||
uint64_t mstatus, int 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;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
SPACE_DMI_DATA,
|
||||
SPACE_DMI_PROGBUF,
|
||||
|
@ -1251,6 +1306,10 @@ static int register_write_direct(struct target *target, unsigned number,
|
|||
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint64_t mstatus;
|
||||
if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
scratch_mem_t scratch;
|
||||
bool use_scratch = false;
|
||||
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
|
||||
|
@ -1275,6 +1334,10 @@ static int register_write_direct(struct target *target, unsigned number,
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
} else if (number == GDB_REGNO_VTYPE) {
|
||||
riscv_program_insert(&program, csrr(S0, CSR_VL));
|
||||
riscv_program_insert(&program, vsetvli(ZERO, S0, value));
|
||||
|
||||
} else {
|
||||
if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
@ -1284,6 +1347,15 @@ static int register_write_direct(struct target *target, unsigned number,
|
|||
riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
|
||||
else
|
||||
riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
|
||||
} else if (number == GDB_REGNO_VL) {
|
||||
/* "The XLEN-bit-wide read-only vl CSR can only be updated by the
|
||||
* vsetvli and vsetvl instructions, and the fault-only-rst vector
|
||||
* load instruction variants." */
|
||||
riscv_reg_t vtype;
|
||||
if (register_read(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (riscv_program_insert(&program, vsetvli(ZERO, S0, vtype)) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
||||
riscv_program_csrw(&program, S0, number);
|
||||
} else {
|
||||
|
@ -1302,6 +1374,9 @@ static int register_write_direct(struct target *target, unsigned number,
|
|||
if (use_scratch)
|
||||
scratch_release(target, &scratch);
|
||||
|
||||
if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Restore S0. */
|
||||
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
@ -1326,14 +1401,6 @@ static int register_read(struct target *target, uint64_t *value, uint32_t number
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int is_fpu_reg(uint32_t gdb_regno)
|
||||
{
|
||||
return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) ||
|
||||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) ||
|
||||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) ||
|
||||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR);
|
||||
}
|
||||
|
||||
/** Actually read registers from the target right now. */
|
||||
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
|
||||
{
|
||||
|
@ -1352,21 +1419,15 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
|
|||
scratch_mem_t scratch;
|
||||
bool use_scratch = false;
|
||||
|
||||
uint64_t s0;
|
||||
riscv_reg_t s0;
|
||||
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Write program to move data into s0. */
|
||||
|
||||
uint64_t mstatus;
|
||||
if (is_fpu_reg(number)) {
|
||||
if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if ((mstatus & MSTATUS_FS) == 0)
|
||||
if (register_write_direct(target, GDB_REGNO_MSTATUS,
|
||||
set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
|
||||
if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
|
||||
|
@ -1394,7 +1455,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
|
|||
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
||||
riscv_program_csrr(&program, S0, number);
|
||||
} else {
|
||||
LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
|
||||
LOG_ERROR("Unsupported register: %s", gdb_regno_name(number));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
|
@ -1413,9 +1474,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (is_fpu_reg(number) && (mstatus & MSTATUS_FS) == 0)
|
||||
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Restore S0. */
|
||||
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
|
||||
|
@ -1476,6 +1536,20 @@ static int set_haltgroup(struct target *target, bool *supported)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int discover_vlenb(struct target *target, int hartid)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
riscv_reg_t vlenb;
|
||||
|
||||
if (register_read(target, &vlenb, GDB_REGNO_VLENB) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
r->vlenb[hartid] = vlenb;
|
||||
|
||||
LOG_INFO("hart %d: Vector support with vlenb=%d", hartid, r->vlenb[hartid]);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int examine(struct target *target)
|
||||
{
|
||||
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
|
||||
|
@ -1654,6 +1728,11 @@ static int examine(struct target *target)
|
|||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (riscv_supports_extension(target, i, 'V')) {
|
||||
if (discover_vlenb(target, i) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* Now init registers based on what we discovered. */
|
||||
if (riscv_init_registers(target) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
@ -1761,6 +1840,153 @@ static unsigned riscv013_data_bits(struct target *target)
|
|||
return riscv_xlen(target);
|
||||
}
|
||||
|
||||
static int prep_for_vector_access(struct target *target, uint64_t *vtype,
|
||||
uint64_t *vl, unsigned *debug_vl)
|
||||
{
|
||||
RISCV_INFO(r);
|
||||
/* TODO: this continuous save/restore is terrible for performance. */
|
||||
/* Write vtype and vl. */
|
||||
unsigned encoded_vsew;
|
||||
switch (riscv_xlen(target)) {
|
||||
case 32:
|
||||
encoded_vsew = 2;
|
||||
break;
|
||||
case 64:
|
||||
encoded_vsew = 3;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* Save vtype and vl. */
|
||||
if (register_read(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (register_read(target, vl, GDB_REGNO_VL) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 2) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
*debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8,
|
||||
riscv_xlen(target));
|
||||
if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cleanup_after_vector_access(struct target *target, uint64_t vtype,
|
||||
uint64_t vl)
|
||||
{
|
||||
/* Restore vtype and vl. */
|
||||
if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int riscv013_get_register_buf(struct target *target,
|
||||
uint8_t *value, int regno)
|
||||
{
|
||||
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
|
||||
|
||||
riscv_reg_t s0;
|
||||
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint64_t mstatus;
|
||||
if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint64_t vtype, vl;
|
||||
unsigned debug_vl;
|
||||
if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
unsigned vnum = regno - GDB_REGNO_V0;
|
||||
unsigned xlen = riscv_xlen(target);
|
||||
|
||||
struct riscv_program program;
|
||||
riscv_program_init(&program, target);
|
||||
riscv_program_insert(&program, vmv_x_s(S0, vnum));
|
||||
riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
|
||||
|
||||
int result = ERROR_OK;
|
||||
for (unsigned i = 0; i < debug_vl; i++) {
|
||||
/* Executing the program might result in an exception if there is some
|
||||
* issue with the vector implementation/instructions we're using. If that
|
||||
* happens, attempt to restore as usual. We may have clobbered the
|
||||
* vector register we tried to read already.
|
||||
* For other failures, we just return error because things are probably
|
||||
* so messed up that attempting to restore isn't going to help. */
|
||||
result = riscv_program_exec(&program, target);
|
||||
if (result == ERROR_OK) {
|
||||
uint64_t v;
|
||||
if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
buf_set_u64(value, xlen * i, xlen, v);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int riscv013_set_register_buf(struct target *target,
|
||||
int regno, const uint8_t *value)
|
||||
{
|
||||
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
|
||||
|
||||
riscv_reg_t s0;
|
||||
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint64_t mstatus;
|
||||
if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint64_t vtype, vl;
|
||||
unsigned debug_vl;
|
||||
if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
unsigned vnum = regno - GDB_REGNO_V0;
|
||||
unsigned xlen = riscv_xlen(target);
|
||||
|
||||
struct riscv_program program;
|
||||
riscv_program_init(&program, target);
|
||||
riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
|
||||
int result = ERROR_OK;
|
||||
for (unsigned i = 0; i < debug_vl; i++) {
|
||||
if (register_write_direct(target, GDB_REGNO_S0,
|
||||
buf_get_u64(value, xlen * i, xlen)) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
result = riscv_program_exec(&program, target);
|
||||
if (result != ERROR_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int init_target(struct command_context *cmd_ctx,
|
||||
struct target *target)
|
||||
{
|
||||
|
@ -1769,6 +1995,8 @@ static int init_target(struct command_context *cmd_ctx,
|
|||
|
||||
generic_info->get_register = &riscv013_get_register;
|
||||
generic_info->set_register = &riscv013_set_register;
|
||||
generic_info->get_register_buf = &riscv013_get_register_buf;
|
||||
generic_info->set_register_buf = &riscv013_set_register_buf;
|
||||
generic_info->select_current_hart = &riscv013_select_current_hart;
|
||||
generic_info->is_halted = &riscv013_is_halted;
|
||||
generic_info->resume_go = &riscv013_resume_go;
|
||||
|
@ -3385,10 +3613,12 @@ static int riscv013_get_register(struct target *target,
|
|||
|
||||
int result = ERROR_OK;
|
||||
if (rid == GDB_REGNO_PC) {
|
||||
/* TODO: move this into riscv.c. */
|
||||
result = register_read(target, value, GDB_REGNO_DPC);
|
||||
LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value);
|
||||
} else if (rid == GDB_REGNO_PRIV) {
|
||||
uint64_t dcsr;
|
||||
/* TODO: move this into riscv.c. */
|
||||
result = register_read(target, &dcsr, GDB_REGNO_DCSR);
|
||||
*value = get_field(dcsr, CSR_DCSR_PRV);
|
||||
} else {
|
||||
|
|
|
@ -3229,6 +3229,74 @@ const char *gdb_regno_name(enum gdb_regno regno)
|
|||
return "priv";
|
||||
case GDB_REGNO_SATP:
|
||||
return "satp";
|
||||
case GDB_REGNO_VTYPE:
|
||||
return "vtype";
|
||||
case GDB_REGNO_VL:
|
||||
return "vl";
|
||||
case GDB_REGNO_V0:
|
||||
return "v0";
|
||||
case GDB_REGNO_V1:
|
||||
return "v1";
|
||||
case GDB_REGNO_V2:
|
||||
return "v2";
|
||||
case GDB_REGNO_V3:
|
||||
return "v3";
|
||||
case GDB_REGNO_V4:
|
||||
return "v4";
|
||||
case GDB_REGNO_V5:
|
||||
return "v5";
|
||||
case GDB_REGNO_V6:
|
||||
return "v6";
|
||||
case GDB_REGNO_V7:
|
||||
return "v7";
|
||||
case GDB_REGNO_V8:
|
||||
return "v8";
|
||||
case GDB_REGNO_V9:
|
||||
return "v9";
|
||||
case GDB_REGNO_V10:
|
||||
return "v10";
|
||||
case GDB_REGNO_V11:
|
||||
return "v11";
|
||||
case GDB_REGNO_V12:
|
||||
return "v12";
|
||||
case GDB_REGNO_V13:
|
||||
return "v13";
|
||||
case GDB_REGNO_V14:
|
||||
return "v14";
|
||||
case GDB_REGNO_V15:
|
||||
return "v15";
|
||||
case GDB_REGNO_V16:
|
||||
return "v16";
|
||||
case GDB_REGNO_V17:
|
||||
return "v17";
|
||||
case GDB_REGNO_V18:
|
||||
return "v18";
|
||||
case GDB_REGNO_V19:
|
||||
return "v19";
|
||||
case GDB_REGNO_V20:
|
||||
return "v20";
|
||||
case GDB_REGNO_V21:
|
||||
return "v21";
|
||||
case GDB_REGNO_V22:
|
||||
return "v22";
|
||||
case GDB_REGNO_V23:
|
||||
return "v23";
|
||||
case GDB_REGNO_V24:
|
||||
return "v24";
|
||||
case GDB_REGNO_V25:
|
||||
return "v25";
|
||||
case GDB_REGNO_V26:
|
||||
return "v26";
|
||||
case GDB_REGNO_V27:
|
||||
return "v27";
|
||||
case GDB_REGNO_V28:
|
||||
return "v28";
|
||||
case GDB_REGNO_V29:
|
||||
return "v29";
|
||||
case GDB_REGNO_V30:
|
||||
return "v30";
|
||||
case GDB_REGNO_V31:
|
||||
return "v31";
|
||||
default:
|
||||
if (regno <= GDB_REGNO_XPR31)
|
||||
sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
|
||||
|
@ -3246,20 +3314,35 @@ static int register_get(struct reg *reg)
|
|||
{
|
||||
riscv_reg_info_t *reg_info = reg->arch_info;
|
||||
struct target *target = reg_info->target;
|
||||
uint64_t value;
|
||||
int result = riscv_get_register(target, &value, reg->number);
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
buf_set_u64(reg->value, 0, reg->size, value);
|
||||
RISCV_INFO(r);
|
||||
|
||||
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
|
||||
if (!r->get_register_buf) {
|
||||
LOG_ERROR("Reading register %s not supported on this RISC-V target.",
|
||||
gdb_regno_name(reg->number));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
} else {
|
||||
uint64_t value;
|
||||
int result = riscv_get_register(target, &value, reg->number);
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
buf_set_u64(reg->value, 0, reg->size, value);
|
||||
}
|
||||
/* CSRs (and possibly other extension) registers may change value at any
|
||||
* time. */
|
||||
if (reg->number <= GDB_REGNO_XPR31 ||
|
||||
(reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) ||
|
||||
(reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) ||
|
||||
reg->number == GDB_REGNO_PC)
|
||||
reg->valid = true;
|
||||
LOG_DEBUG("[%d]{%d} read 0x%" PRIx64 " from %s (valid=%d)",
|
||||
target->coreid, riscv_current_hartid(target), value, reg->name,
|
||||
reg->valid);
|
||||
char *str = buf_to_str(reg->value, reg->size, 16);
|
||||
LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid,
|
||||
riscv_current_hartid(target), str, reg->name, reg->valid);
|
||||
free(str);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -3267,22 +3350,37 @@ static int register_set(struct reg *reg, uint8_t *buf)
|
|||
{
|
||||
riscv_reg_info_t *reg_info = reg->arch_info;
|
||||
struct target *target = reg_info->target;
|
||||
RISCV_INFO(r);
|
||||
|
||||
uint64_t value = buf_get_u64(buf, 0, reg->size);
|
||||
char *str = buf_to_str(buf, reg->size, 16);
|
||||
LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid,
|
||||
riscv_current_hartid(target), str, reg->name, reg->valid);
|
||||
free(str);
|
||||
|
||||
LOG_DEBUG("[%d]{%d} write 0x%" PRIx64 " to %s (valid=%d)",
|
||||
target->coreid, riscv_current_hartid(target), value, reg->name,
|
||||
reg->valid);
|
||||
struct reg *r = &target->reg_cache->reg_list[reg->number];
|
||||
memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
|
||||
/* CSRs (and possibly other extension) registers may change value at any
|
||||
* time. */
|
||||
if (reg->number <= GDB_REGNO_XPR31 ||
|
||||
(reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) ||
|
||||
(reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) ||
|
||||
reg->number == GDB_REGNO_PC)
|
||||
r->valid = true;
|
||||
memcpy(r->value, buf, (r->size + 7) / 8);
|
||||
reg->valid = true;
|
||||
|
||||
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
|
||||
if (!r->set_register_buf) {
|
||||
LOG_ERROR("Writing register %s not supported on this RISC-V target.",
|
||||
gdb_regno_name(reg->number));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
} else {
|
||||
uint64_t value = buf_get_u64(buf, 0, reg->size);
|
||||
if (riscv_set_register(target, reg->number, value) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
riscv_set_register(target, reg->number, value);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -3333,6 +3431,8 @@ int riscv_init_registers(struct target *target)
|
|||
calloc(target->reg_cache->num_regs, max_reg_name_len);
|
||||
char *reg_name = info->reg_names;
|
||||
|
||||
int hartid = riscv_current_hartid(target);
|
||||
|
||||
static struct reg_feature feature_cpu = {
|
||||
.name = "org.gnu.gdb.riscv.cpu"
|
||||
};
|
||||
|
@ -3342,6 +3442,9 @@ int riscv_init_registers(struct target *target)
|
|||
static struct reg_feature feature_csr = {
|
||||
.name = "org.gnu.gdb.riscv.csr"
|
||||
};
|
||||
static struct reg_feature feature_vector = {
|
||||
.name = "org.gnu.gdb.riscv.vector"
|
||||
};
|
||||
static struct reg_feature feature_virtual = {
|
||||
.name = "org.gnu.gdb.riscv.virtual"
|
||||
};
|
||||
|
@ -3349,14 +3452,104 @@ int riscv_init_registers(struct target *target)
|
|||
.name = "org.gnu.gdb.riscv.custom"
|
||||
};
|
||||
|
||||
static struct reg_data_type type_ieee_single = {
|
||||
.type = REG_TYPE_IEEE_SINGLE,
|
||||
.id = "ieee_single"
|
||||
};
|
||||
static struct reg_data_type type_ieee_double = {
|
||||
.type = REG_TYPE_IEEE_DOUBLE,
|
||||
.id = "ieee_double"
|
||||
};
|
||||
/* These types are built into gdb. */
|
||||
static struct reg_data_type type_ieee_single = { .type = REG_TYPE_IEEE_SINGLE, .id = "ieee_single" };
|
||||
static struct reg_data_type type_ieee_double = { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" };
|
||||
static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
|
||||
static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
|
||||
static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
|
||||
static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" };
|
||||
static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" };
|
||||
|
||||
/* This is roughly the XML we want:
|
||||
* <vector id="bytes" type="uint8" count="16"/>
|
||||
* <vector id="shorts" type="uint16" count="8"/>
|
||||
* <vector id="words" type="uint32" count="4"/>
|
||||
* <vector id="longs" type="uint64" count="2"/>
|
||||
* <vector id="quads" type="uint128" count="1"/>
|
||||
* <union id="riscv_vector_type">
|
||||
* <field name="b" type="bytes"/>
|
||||
* <field name="s" type="shorts"/>
|
||||
* <field name="w" type="words"/>
|
||||
* <field name="l" type="longs"/>
|
||||
* <field name="q" type="quads"/>
|
||||
* </union>
|
||||
*/
|
||||
|
||||
info->vector_uint8.type = &type_uint8;
|
||||
info->vector_uint8.count = info->vlenb[hartid];
|
||||
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||
info->type_uint8_vector.id = "bytes";
|
||||
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
|
||||
|
||||
info->vector_uint16.type = &type_uint16;
|
||||
info->vector_uint16.count = info->vlenb[hartid] / 2;
|
||||
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||
info->type_uint16_vector.id = "shorts";
|
||||
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
|
||||
|
||||
info->vector_uint32.type = &type_uint32;
|
||||
info->vector_uint32.count = info->vlenb[hartid] / 4;
|
||||
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||
info->type_uint32_vector.id = "words";
|
||||
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
|
||||
|
||||
info->vector_uint64.type = &type_uint64;
|
||||
info->vector_uint64.count = info->vlenb[hartid] / 8;
|
||||
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||
info->type_uint64_vector.id = "longs";
|
||||
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
|
||||
|
||||
info->vector_uint128.type = &type_uint128;
|
||||
info->vector_uint128.count = info->vlenb[hartid] / 16;
|
||||
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||
info->type_uint128_vector.id = "quads";
|
||||
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
|
||||
info->type_uint128_vector.reg_type_vector = &info->vector_uint128;
|
||||
|
||||
info->vector_fields[0].name = "b";
|
||||
info->vector_fields[0].type = &info->type_uint8_vector;
|
||||
if (info->vlenb[hartid] >= 2) {
|
||||
info->vector_fields[0].next = info->vector_fields + 1;
|
||||
info->vector_fields[1].name = "s";
|
||||
info->vector_fields[1].type = &info->type_uint16_vector;
|
||||
} else {
|
||||
info->vector_fields[0].next = NULL;
|
||||
}
|
||||
if (info->vlenb[hartid] >= 4) {
|
||||
info->vector_fields[1].next = info->vector_fields + 2;
|
||||
info->vector_fields[2].name = "w";
|
||||
info->vector_fields[2].type = &info->type_uint32_vector;
|
||||
} else {
|
||||
info->vector_fields[1].next = NULL;
|
||||
}
|
||||
if (info->vlenb[hartid] >= 8) {
|
||||
info->vector_fields[2].next = info->vector_fields + 3;
|
||||
info->vector_fields[3].name = "l";
|
||||
info->vector_fields[3].type = &info->type_uint64_vector;
|
||||
} else {
|
||||
info->vector_fields[2].next = NULL;
|
||||
}
|
||||
if (info->vlenb[hartid] >= 16) {
|
||||
info->vector_fields[3].next = info->vector_fields + 4;
|
||||
info->vector_fields[4].name = "q";
|
||||
info->vector_fields[4].type = &info->type_uint128_vector;
|
||||
} else {
|
||||
info->vector_fields[3].next = NULL;
|
||||
}
|
||||
info->vector_fields[4].next = NULL;
|
||||
|
||||
info->vector_union.fields = info->vector_fields;
|
||||
|
||||
info->type_vector.type = REG_TYPE_ARCH_DEFINED;
|
||||
info->type_vector.id = "riscv_vector";
|
||||
info->type_vector.type_class = REG_TYPE_CLASS_UNION;
|
||||
info->type_vector.reg_type_union = &info->vector_union;
|
||||
|
||||
struct csr_info csr_info[] = {
|
||||
#define DECLARE_CSR(name, number) { number, #name },
|
||||
#include "encoding.h"
|
||||
|
@ -3372,8 +3565,6 @@ int riscv_init_registers(struct target *target)
|
|||
riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
|
||||
shared_reg_info->target = target;
|
||||
|
||||
int hartid = riscv_current_hartid(target);
|
||||
|
||||
/* When gdb requests register N, gdb_get_register_packet() assumes that this
|
||||
* is register at index N in reg_list. So if there are certain registers
|
||||
* that don't exist, we need to leave holes in the list (or renumber, but
|
||||
|
@ -3731,6 +3922,15 @@ int riscv_init_registers(struct target *target)
|
|||
case CSR_MHPMCOUNTER31H:
|
||||
r->exist = riscv_xlen(target) == 32;
|
||||
break;
|
||||
|
||||
case CSR_VSTART:
|
||||
case CSR_VXSAT:
|
||||
case CSR_VXRM:
|
||||
case CSR_VL:
|
||||
case CSR_VTYPE:
|
||||
case CSR_VLENB:
|
||||
r->exist = riscv_supports_extension(target, hartid, 'V');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!r->exist && expose_csr) {
|
||||
|
@ -3749,6 +3949,15 @@ int riscv_init_registers(struct target *target)
|
|||
r->feature = &feature_virtual;
|
||||
r->size = 8;
|
||||
|
||||
} else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
|
||||
r->caller_save = false;
|
||||
r->exist = riscv_supports_extension(target, hartid, 'V');
|
||||
r->size = info->vlenb[hartid] * 8;
|
||||
sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
|
||||
r->group = "vector";
|
||||
r->feature = &feature_vector;
|
||||
r->reg_data_type = &info->type_vector;
|
||||
|
||||
} else if (number >= GDB_REGNO_COUNT) {
|
||||
/* Custom registers. */
|
||||
assert(expose_custom);
|
||||
|
|
|
@ -7,6 +7,7 @@ struct riscv_program;
|
|||
#include "opcodes.h"
|
||||
#include "gdb_regs.h"
|
||||
#include "jtag/jtag.h"
|
||||
#include "target/register.h"
|
||||
|
||||
/* The register cache is statically allocated. */
|
||||
#define RISCV_MAX_HARTS 1024
|
||||
|
@ -74,6 +75,7 @@ typedef struct {
|
|||
/* It's possible that each core has a different supported ISA set. */
|
||||
int xlen[RISCV_MAX_HARTS];
|
||||
riscv_reg_t misa[RISCV_MAX_HARTS];
|
||||
unsigned vlenb[RISCV_MAX_HARTS];
|
||||
|
||||
/* The number of triggers per hart. */
|
||||
unsigned trigger_count[RISCV_MAX_HARTS];
|
||||
|
@ -110,6 +112,9 @@ typedef struct {
|
|||
riscv_reg_t *value, int hid, int rid);
|
||||
int (*set_register)(struct target *, int hartid, int regid,
|
||||
uint64_t value);
|
||||
int (*get_register_buf)(struct target *target, uint8_t *buf, int regno);
|
||||
int (*set_register_buf)(struct target *target, int regno,
|
||||
const uint8_t *buf);
|
||||
int (*select_current_hart)(struct target *);
|
||||
bool (*is_halted)(struct target *target);
|
||||
/* Resume this target, as well as every other prepped target that can be
|
||||
|
@ -148,6 +153,21 @@ typedef struct {
|
|||
/* How many harts are attached to the DM that this target is attached to? */
|
||||
int (*hart_count)(struct target *target);
|
||||
unsigned (*data_bits)(struct target *target);
|
||||
|
||||
/* Storage for vector register types. */
|
||||
struct reg_data_type_vector vector_uint8;
|
||||
struct reg_data_type_vector vector_uint16;
|
||||
struct reg_data_type_vector vector_uint32;
|
||||
struct reg_data_type_vector vector_uint64;
|
||||
struct reg_data_type_vector vector_uint128;
|
||||
struct reg_data_type type_uint8_vector;
|
||||
struct reg_data_type type_uint16_vector;
|
||||
struct reg_data_type type_uint32_vector;
|
||||
struct reg_data_type type_uint64_vector;
|
||||
struct reg_data_type type_uint128_vector;
|
||||
struct reg_data_type_union_field vector_fields[5];
|
||||
struct reg_data_type_union vector_union;
|
||||
struct reg_data_type type_vector;
|
||||
} riscv_info_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
Loading…
Reference in New Issue