`make all` debug tests now pass.

Also properly support (I think) D extension on RV32.

Change-Id: I2f0162d36e4c18c251f99b6943403cef30d17d29
This commit is contained in:
Tim Newsome 2017-11-13 13:14:07 -08:00
parent c421fefdcb
commit e648856a41
3 changed files with 132 additions and 99 deletions

View File

@ -128,12 +128,6 @@ struct trigger {
int unique_id;
};
struct memory_cache_line {
uint32_t data;
bool valid;
bool dirty;
};
typedef enum {
YNM_MAYBE,
YNM_YES,
@ -302,20 +296,6 @@ static int register_get(struct reg *reg);
/*** Utility functions. ***/
bool supports_extension(struct target *target, char letter)
{
RISCV_INFO(r);
unsigned num;
if (letter >= 'a' && letter <= 'z') {
num = letter - 'a';
} else if (letter >= 'A' && letter <= 'Z') {
num = letter - 'A';
} else {
return false;
}
return r->misa & (1 << num);
}
static void select_dmi(struct target *target)
{
static uint8_t ir_dmi[1] = {DTM_DMI};
@ -969,7 +949,7 @@ static int register_write_direct(struct target *target, unsigned number,
return ERROR_FAIL;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
supports_extension(target, 'D') &&
riscv_supports_extension(target, 'D') &&
riscv_xlen(target) < 64) {
/* There are no instructions to move all the bits from a register, so
* we need to use some scratch RAM. */
@ -991,7 +971,7 @@ static int register_write_direct(struct target *target, unsigned number,
return ERROR_FAIL;
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
if (supports_extension(target, 'D')) {
if (riscv_supports_extension(target, 'D')) {
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));
@ -1038,7 +1018,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
// TODO: Possibly set F in mstatus.
if (supports_extension(target, 'D') && riscv_xlen(target) < 64) {
if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) {
/* There are no instructions to move all the bits from a
* register, so we need to use some scratch RAM. */
riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
@ -1051,7 +1031,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
if (register_write_direct(target, GDB_REGNO_S0,
scratch.hart_address) != ERROR_OK)
return ERROR_FAIL;
} else if (supports_extension(target, 'D')) {
} else if (riscv_supports_extension(target, 'D')) {
riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
} else {
riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
@ -1094,7 +1074,7 @@ static int register_get(struct reg *reg)
{
struct target *target = (struct target *) reg->arch_info;
uint64_t value = riscv_get_register(target, reg->number);
buf_set_u64(reg->value, 0, 64, value);
buf_set_u64(reg->value, 0, reg->size, value);
return ERROR_OK;
}
@ -1109,7 +1089,7 @@ static int register_set(struct reg *reg, uint8_t *buf)
{
struct target *target = (struct target *) reg->arch_info;
uint64_t value = buf_get_u64(buf, 0, riscv_xlen(target));
uint64_t value = buf_get_u64(buf, 0, reg->size);
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
struct reg *r = &target->reg_cache->reg_list[reg->number];
@ -1124,6 +1104,99 @@ static struct reg_arch_type riscv_reg_arch_type = {
.set = register_set
};
static int init_registers(struct target *target)
{
riscv013_info_t *info = get_info(target);
if (target->reg_cache) {
if (target->reg_cache->reg_list)
free(target->reg_cache->reg_list);
free(target->reg_cache);
}
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
const unsigned int max_reg_name_len = 12;
if (info->reg_names)
free(info->reg_names);
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
char *reg_name = info->reg_names;
info->reg_values = NULL;
static struct reg_feature feature_cpu = {
.name = "org.gnu.gdb.riscv.cpu"
};
static struct reg_feature feature_fpu = {
.name = "org.gnu.gdb.riscv.fpu"
};
static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.csr"
};
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
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"
};
for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
struct reg *r = &target->reg_cache->reg_list[i];
r->number = i;
r->caller_save = true;
r->dirty = false;
r->valid = false;
r->exist = true;
r->type = &riscv_reg_arch_type;
r->arch_info = target;
// r->size is set in riscv_invalidate_register_cache, maybe because the
// target is in theory allowed to change XLEN on us. But I expect a lot
// of other things to break in that case as well.
if (i <= GDB_REGNO_XPR31) {
sprintf(reg_name, "x%d", i);
r->group = "general";
r->feature = &feature_cpu;
} else if (i == GDB_REGNO_PC) {
sprintf(reg_name, "pc");
r->group = "general";
r->feature = &feature_cpu;
} else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
r->group = "float";
r->feature = &feature_fpu;
if (riscv_supports_extension(target, 'D')) {
r->reg_data_type = &type_ieee_double;
} else if (riscv_supports_extension(target, 'F')) {
r->reg_data_type = &type_ieee_single;
}
} else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
r->group = "csr";
r->feature = &feature_csr;
} else if (i == GDB_REGNO_PRIV) {
sprintf(reg_name, "priv");
r->group = "general";
r->feature = &feature_virtual;
}
if (reg_name[0]) {
r->name = reg_name;
}
reg_name += strlen(reg_name) + 1;
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
}
return ERROR_OK;
}
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
@ -1168,74 +1241,6 @@ static int init_target(struct command_context *cmd_ctx,
info->abstract_read_fpr_supported = true;
info->abstract_write_fpr_supported = true;
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
const unsigned int max_reg_name_len = 12;
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
char *reg_name = info->reg_names;
info->reg_values = NULL;
static struct reg_feature feature_cpu = {
.name = "org.gnu.gdb.riscv.cpu"
};
static struct reg_feature feature_fpu = {
.name = "org.gnu.gdb.riscv.fpu"
};
static struct reg_feature feature_csr = {
.name = "org.gnu.gdb.riscv.csr"
};
static struct reg_feature feature_virtual = {
.name = "org.gnu.gdb.riscv.virtual"
};
static struct reg_data_type type_ieee_single = {
.type = REG_TYPE_IEEE_SINGLE,
.id = "ieee_single"
};
for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
struct reg *r = &target->reg_cache->reg_list[i];
r->number = i;
r->caller_save = true;
r->dirty = false;
r->valid = false;
r->exist = true;
r->type = &riscv_reg_arch_type;
r->arch_info = target;
if (i <= GDB_REGNO_XPR31) {
sprintf(reg_name, "x%d", i);
r->group = "general";
r->feature = &feature_cpu;
} else if (i == GDB_REGNO_PC) {
sprintf(reg_name, "pc");
r->group = "general";
r->feature = &feature_cpu;
} else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
r->group = "float";
r->feature = &feature_fpu;
// TODO: check D or F extension
r->reg_data_type = &type_ieee_single;
} else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
r->group = "csr";
r->feature = &feature_csr;
} else if (i == GDB_REGNO_PRIV) {
sprintf(reg_name, "priv");
r->group = "general";
r->feature = &feature_virtual;
}
if (reg_name[0]) {
r->name = reg_name;
}
reg_name += strlen(reg_name) + 1;
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
}
return ERROR_OK;
}
@ -1345,6 +1350,10 @@ static int examine(struct target *target)
LOG_DEBUG("Enumerated %d harts", r->hart_count);
// Get a functional register cache going.
if (init_registers(target) != ERROR_OK)
return ERROR_FAIL;
/* Halt every hart so we can probe them. */
riscv_halt_all_harts(target);
@ -1381,6 +1390,11 @@ static int examine(struct target *target)
/* Resumes all the harts, so the debugger can later pause them. */
riscv_resume_all_harts(target);
target->state = TARGET_RUNNING;
// Now reinit registers based on what we discovered.
if (init_registers(target) != ERROR_OK)
return ERROR_FAIL;
target_set_examined(target);
if (target->rtos) {

View File

@ -1352,6 +1352,20 @@ int riscv_step_rtos_hart(struct target *target)
return ERROR_OK;
}
bool riscv_supports_extension(struct target *target, char letter)
{
RISCV_INFO(r);
unsigned num;
if (letter >= 'a' && letter <= 'z') {
num = letter - 'a';
} else if (letter >= 'A' && letter <= 'Z') {
num = letter - 'A';
} else {
return false;
}
return r->misa & (1 << num);
}
int riscv_xlen(const struct target *target)
{
return riscv_xlen_of_hart(target, riscv_current_hartid(target));
@ -1410,13 +1424,16 @@ void riscv_invalidate_register_cache(struct target *target)
reg->value = &r->reg_cache_values[i];
reg->valid = false;
switch (i) {
case GDB_REGNO_PRIV:
if (i == GDB_REGNO_PRIV) {
reg->size = 8;
break;
default:
} else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
if (riscv_supports_extension(target, 'D')) {
reg->size = 64;
} else if (riscv_supports_extension(target, 'F')) {
reg->size = 32;
}
} else {
reg->size = riscv_xlen(target);
break;
}
}

View File

@ -168,6 +168,8 @@ int riscv_resume_one_hart(struct target *target, int hartid);
* then the only hart. */
int riscv_step_rtos_hart(struct target *target);
bool riscv_supports_extension(struct target *target, char letter);
/* Returns XLEN for the given (or current) hart. */
int riscv_xlen(const struct target *target);
int riscv_xlen_of_hart(const struct target *target, int hartid);