Change-Id: I0264a73b7f7d2ce89cc0b80692dbf81d9cdcc2fd
This commit is contained in:
Tim Newsome 2020-01-13 15:11:35 -08:00
parent 69e6891434
commit 9e80ab1f85
6 changed files with 1486 additions and 8 deletions

File diff suppressed because it is too large Load Diff

View File

@ -76,6 +76,12 @@ enum gdb_regno {
GDB_REGNO_FT11, GDB_REGNO_FT11,
GDB_REGNO_FPR31 = GDB_REGNO_FT11, GDB_REGNO_FPR31 = GDB_REGNO_FT11,
GDB_REGNO_CSR0 = 65, 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_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0, GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA2 = CSR_TDATA2 + 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_SATP = CSR_SATP + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095, GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161, 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 GDB_REGNO_COUNT
}; };

View File

@ -323,3 +323,27 @@ static uint32_t auipc(unsigned int dest)
{ {
return MATCH_AUIPC | (dest << 7); 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 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;
}

View File

@ -853,6 +853,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3); command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3);
break; break;
default: default:
LOG_ERROR("%d-bit register %s not supported.", size,
gdb_regno_name(number));
assert(0); assert(0);
} }
@ -872,6 +874,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
assert(reg_info); assert(reg_info);
command = set_field(command, AC_ACCESS_REGISTER_REGNO, command = set_field(command, AC_ACCESS_REGISTER_REGNO,
0xc000 + reg_info->custom_number); 0xc000 + reg_info->custom_number);
} else {
assert(0);
} }
command |= flags; command |= flags;
@ -890,6 +894,9 @@ static int register_read_abstract(struct target *target, uint64_t *value,
if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 &&
!info->abstract_read_csr_supported) !info->abstract_read_csr_supported)
return ERROR_FAIL; 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, uint32_t command = access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER); AC_ACCESS_REGISTER_TRANSFER);
@ -1279,6 +1286,15 @@ static int register_write_direct(struct target *target, unsigned number,
riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0)); riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
else else
riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); 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) { } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
riscv_program_csrw(&program, S0, number); riscv_program_csrw(&program, S0, number);
} else { } else {
@ -1329,6 +1345,17 @@ static int is_fpu_reg(uint32_t gdb_regno)
(gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR); (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;
}
/** Actually read registers from the target right now. */ /** Actually read registers from the target right now. */
static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
{ {
@ -1347,20 +1374,25 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
scratch_mem_t scratch; scratch_mem_t scratch;
bool use_scratch = false; bool use_scratch = false;
uint64_t s0; riscv_reg_t s0;
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
/* Write program to move data into s0. */ /* Write program to move data into s0. */
uint64_t mstatus; uint64_t mstatus;
if (is_fpu_reg(number)) { if (is_fpu_reg(number) || is_vector_reg(number)) {
if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
if ((mstatus & MSTATUS_FS) == 0) if (is_fpu_reg(number) && (mstatus & MSTATUS_FS) == 0) {
if (register_write_direct(target, GDB_REGNO_MSTATUS, if (register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK) set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
} else if (is_vector_reg(number) && (mstatus & MSTATUS_VS) == 0) {
if (register_write_direct(target, GDB_REGNO_MSTATUS,
set_field(mstatus, MSTATUS_VS, 1)) != ERROR_OK)
return ERROR_FAIL;
}
} }
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
@ -1388,8 +1420,80 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
} }
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
riscv_program_csrr(&program, S0, number); riscv_program_csrr(&program, S0, number);
} else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
/*
* The idea is that you read a vector register destructively: read
* element 0 using vmv.x.s into t0; send t0 to the debugger; then
* vslide1down with t0 as the scalar argument. This is effectively
* rotating the vector one element at a time, so after vl steps,
* the vector register is back to its original value."
*
* The two instructions vmv.x.s and vmv.s.x are described in
* section 17.1.
*
* The vmv.x.s instruction copies a single SEW-wide element from
* index 0 of the source vector register to a destination integer
* register.
*
* The vmv.s.x instruction copies the scalar integer register to
* element 0 of the destination vector register.
*
* Executing the two instructions in the PROGBUF and reading or
* writing the x register in a loop would not be that much slower
* than executing the instruction to move a vector reg to memory
* then reading memory sequentially.
*
* I recommend not using memory-based vector register read/writes -
* it would add user complication to have to allocate target memory
* for a vector register buffer just for debug - and require adding
* to the linker script to do this allocation and then informing
* the debugger where it is. */
/* TODO: this continuous save/restore is terrible for performance. */
uint64_t vtype, vl;
/* 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;
/* Restore 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;
}
if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 2) != ERROR_OK)
return ERROR_FAIL;
unsigned 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;
unsigned vnum = number - GDB_REGNO_V0;
riscv_program_insert(&program, vmv_x_s(S0, vnum));
riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, false));
for (unsigned i = 0; i < debug_vl; i++) {
if (riscv_program_exec(&program, target) != ERROR_OK)
return ERROR_FAIL;
if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
}
/* 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;
} else { } else {
LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); LOG_ERROR("Unsupported register: %s", gdb_regno_name(number));
return ERROR_FAIL; return ERROR_FAIL;
} }
@ -1408,7 +1512,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
return ERROR_FAIL; return ERROR_FAIL;
} }
if (is_fpu_reg(number) && (mstatus & MSTATUS_FS) == 0) if ((is_fpu_reg(number) && (mstatus & MSTATUS_FS) == 0) ||
(is_vector_reg(number) && (mstatus & MSTATUS_VS) == 0))
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK) if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;
@ -1471,6 +1576,20 @@ static int set_haltgroup(struct target *target, bool *supported)
return ERROR_OK; 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) static int examine(struct target *target)
{ {
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
@ -1650,6 +1769,11 @@ static int examine(struct target *target)
return ERROR_FAIL; 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. */ /* Now init registers based on what we discovered. */
if (riscv_init_registers(target) != ERROR_OK) if (riscv_init_registers(target) != ERROR_OK)
return ERROR_FAIL; return ERROR_FAIL;

View File

@ -3229,6 +3229,70 @@ const char *gdb_regno_name(enum gdb_regno regno)
return "priv"; return "priv";
case GDB_REGNO_SATP: case GDB_REGNO_SATP:
return "satp"; return "satp";
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: default:
if (regno <= GDB_REGNO_XPR31) if (regno <= GDB_REGNO_XPR31)
sprintf(buf, "x%d", regno - GDB_REGNO_ZERO); sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
@ -3342,6 +3406,9 @@ int riscv_init_registers(struct target *target)
static struct reg_feature feature_csr = { static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.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 = { static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual" .name = "org.gnu.gdb.riscv.virtual"
}; };
@ -3357,6 +3424,14 @@ int riscv_init_registers(struct target *target)
.type = REG_TYPE_IEEE_DOUBLE, .type = REG_TYPE_IEEE_DOUBLE,
.id = "ieee_double" .id = "ieee_double"
}; };
// static struct reg_data_type_vector reg_type_vector = {
// };
// static struct reg_data_type type_arch_defined = {
// .type = REG_TYPE_ARCH_DEFINED,
// .id = "arch_defined",
// .type_class = REG_TYPE_CLASS_VECTOR,
// .reg_type_vector = &reg_type_vector
// };
struct csr_info csr_info[] = { struct csr_info csr_info[] = {
#define DECLARE_CSR(name, number) { number, #name }, #define DECLARE_CSR(name, number) { number, #name },
#include "encoding.h" #include "encoding.h"
@ -3731,6 +3806,16 @@ int riscv_init_registers(struct target *target)
case CSR_MHPMCOUNTER31H: case CSR_MHPMCOUNTER31H:
r->exist = riscv_xlen(target) == 32; r->exist = riscv_xlen(target) == 32;
break; break;
case CSR_VSTART:
case CSR_VXSAT:
case CSR_VXRM:
case CSR_VL:
// TODO: write using vsetvli and vsetvl
case CSR_VTYPE:
case CSR_VLENB:
r->exist = riscv_supports_extension(target, hartid, 'V');
break;
} }
if (!r->exist && expose_csr) { if (!r->exist && expose_csr) {
@ -3749,6 +3834,15 @@ int riscv_init_registers(struct target *target)
r->feature = &feature_virtual; r->feature = &feature_virtual;
r->size = 8; r->size = 8;
} else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
r->caller_save = true;
r->exist = riscv_supports_extension(target, hartid, 'V');
//r->reg_data_type = &type_arch_defined;
r->size = info->vlenb[hartid] * 8;
sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
r->group = "vector";
r->feature = &feature_vector;
} else if (number >= GDB_REGNO_COUNT) { } else if (number >= GDB_REGNO_COUNT) {
/* Custom registers. */ /* Custom registers. */
assert(expose_custom); assert(expose_custom);

View File

@ -74,6 +74,7 @@ typedef struct {
/* It's possible that each core has a different supported ISA set. */ /* It's possible that each core has a different supported ISA set. */
int xlen[RISCV_MAX_HARTS]; int xlen[RISCV_MAX_HARTS];
riscv_reg_t misa[RISCV_MAX_HARTS]; riscv_reg_t misa[RISCV_MAX_HARTS];
unsigned vlenb[RISCV_MAX_HARTS];
/* The number of triggers per hart. */ /* The number of triggers per hart. */
unsigned trigger_count[RISCV_MAX_HARTS]; unsigned trigger_count[RISCV_MAX_HARTS];