WIP xml register for 0.11.
On HiFive1, FPRs show up with no name, and misa is 0x1105 instead of 0x40001105. Change-Id: I4ee223c905ad7d860147014e7b6394668658c6ea
This commit is contained in:
parent
8926e66d3a
commit
f341db9f72
|
@ -189,12 +189,6 @@ typedef struct {
|
||||||
|
|
||||||
struct memory_cache_line dram_cache[DRAM_CACHE_SIZE];
|
struct memory_cache_line dram_cache[DRAM_CACHE_SIZE];
|
||||||
|
|
||||||
/* Single buffer that contains all register names, instead of calling
|
|
||||||
* malloc for each register. Needs to be freed when reg_list is freed. */
|
|
||||||
char *reg_names;
|
|
||||||
/* Single buffer that contains all register values. */
|
|
||||||
void *reg_values;
|
|
||||||
|
|
||||||
// Number of run-test/idle cycles the target requests we do after each dbus
|
// Number of run-test/idle cycles the target requests we do after each dbus
|
||||||
// access.
|
// access.
|
||||||
unsigned int dtmcontrol_idle;
|
unsigned int dtmcontrol_idle;
|
||||||
|
@ -223,7 +217,7 @@ typedef struct {
|
||||||
|
|
||||||
static int poll_target(struct target *target, bool announce);
|
static int poll_target(struct target *target, bool announce);
|
||||||
static int riscv011_poll(struct target *target);
|
static int riscv011_poll(struct target *target);
|
||||||
static int register_get(struct reg *reg);
|
static riscv_reg_t get_register(struct target *target, int hartid, int regid);
|
||||||
|
|
||||||
/*** Utility functions. ***/
|
/*** Utility functions. ***/
|
||||||
|
|
||||||
|
@ -1181,30 +1175,6 @@ static int resume(struct target *target, int debug_execution, bool step)
|
||||||
return execute_resume(target, step);
|
return execute_resume(target, step);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update register sizes based on xlen. */
|
|
||||||
static void update_reg_list(struct target *target)
|
|
||||||
{
|
|
||||||
riscv011_info_t *info = get_info(target);
|
|
||||||
if (info->reg_values) {
|
|
||||||
free(info->reg_values);
|
|
||||||
}
|
|
||||||
info->reg_values = malloc(GDB_REGNO_COUNT * riscv_xlen(target) / 4);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < GDB_REGNO_COUNT; i++) {
|
|
||||||
struct reg *r = &target->reg_cache->reg_list[i];
|
|
||||||
r->value = info->reg_values + i * riscv_xlen(target) / 4;
|
|
||||||
if (r->dirty) {
|
|
||||||
LOG_ERROR("Register %d was dirty. Its value is lost.", i);
|
|
||||||
}
|
|
||||||
if (i == GDB_REGNO_PRIV) {
|
|
||||||
r->size = 8;
|
|
||||||
} else {
|
|
||||||
r->size = riscv_xlen(target);
|
|
||||||
}
|
|
||||||
r->valid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t reg_cache_get(struct target *target, unsigned int number)
|
static uint64_t reg_cache_get(struct target *target, unsigned int number)
|
||||||
{
|
{
|
||||||
struct reg *r = &target->reg_cache->reg_list[number];
|
struct reg *r = &target->reg_cache->reg_list[number];
|
||||||
|
@ -1236,7 +1206,8 @@ static int update_mstatus_actual(struct target *target)
|
||||||
|
|
||||||
// Force reading the register. In that process mstatus_actual will be
|
// Force reading the register. In that process mstatus_actual will be
|
||||||
// updated.
|
// updated.
|
||||||
return register_get(&target->reg_cache->reg_list[GDB_REGNO_MSTATUS]);
|
get_register(target, 0, GDB_REGNO_MSTATUS);
|
||||||
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** OpenOCD target functions. ***/
|
/*** OpenOCD target functions. ***/
|
||||||
|
@ -1275,58 +1246,6 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int register_get(struct reg *reg)
|
|
||||||
{
|
|
||||||
struct target *target = (struct target *) reg->arch_info;
|
|
||||||
riscv011_info_t *info = get_info(target);
|
|
||||||
|
|
||||||
maybe_write_tselect(target);
|
|
||||||
riscv_reg_t value = ~0;
|
|
||||||
|
|
||||||
if (reg->number <= GDB_REGNO_XPR31) {
|
|
||||||
value = reg_cache_get(target, reg->number);
|
|
||||||
LOG_DEBUG("%s=0x%" PRIx64, reg->name, reg_cache_get(target, reg->number));
|
|
||||||
} else if (reg->number == GDB_REGNO_PC) {
|
|
||||||
value = info->dpc;
|
|
||||||
LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc);
|
|
||||||
} else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) {
|
|
||||||
int result = update_mstatus_actual(target);
|
|
||||||
if (result != ERROR_OK) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
unsigned i = 0;
|
|
||||||
if ((info->mstatus_actual & MSTATUS_FS) == 0) {
|
|
||||||
info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
|
|
||||||
cache_set_load(target, i++, S0, SLOT1);
|
|
||||||
cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
|
|
||||||
cache_set(target, SLOT1, info->mstatus_actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (riscv_xlen(target) == 32) {
|
|
||||||
cache_set32(target, i++, fsw(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
|
|
||||||
} else {
|
|
||||||
cache_set32(target, i++, fsd(reg->number - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
|
|
||||||
}
|
|
||||||
cache_set_jump(target, i++);
|
|
||||||
|
|
||||||
if (cache_write(target, 4, true) != ERROR_OK) {
|
|
||||||
return ERROR_FAIL;
|
|
||||||
}
|
|
||||||
} else if (reg->number == GDB_REGNO_PRIV) {
|
|
||||||
value = get_field(info->dcsr, DCSR_PRV);
|
|
||||||
} else {
|
|
||||||
if (register_read(target, &value, reg->number) != ERROR_OK)
|
|
||||||
return ERROR_FAIL;
|
|
||||||
}
|
|
||||||
buf_set_u64(reg->value, 0, riscv_xlen(target), value);
|
|
||||||
|
|
||||||
if (reg->number == GDB_REGNO_MSTATUS) {
|
|
||||||
reg->valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERROR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the register. No caching or games.
|
// Write the register. No caching or games.
|
||||||
static int register_write(struct target *target, unsigned int number,
|
static int register_write(struct target *target, unsigned int number,
|
||||||
uint64_t value)
|
uint64_t value)
|
||||||
|
@ -1399,33 +1318,53 @@ static int register_write(struct target *target, unsigned int number,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
|
|
||||||
struct reg *r = &target->reg_cache->reg_list[reg->number];
|
|
||||||
r->valid = true;
|
|
||||||
memcpy(r->value, buf, (r->size + 7) / 8);
|
|
||||||
|
|
||||||
return register_write(target, reg->number, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct reg_arch_type riscv_reg_arch_type = {
|
|
||||||
.get = register_get,
|
|
||||||
.set = register_set
|
|
||||||
};
|
|
||||||
|
|
||||||
static riscv_reg_t get_register(struct target *target, int hartid, int regid)
|
static riscv_reg_t get_register(struct target *target, int hartid, int regid)
|
||||||
{
|
{
|
||||||
assert(hartid == 0);
|
assert(hartid == 0);
|
||||||
riscv_reg_t value;
|
riscv011_info_t *info = get_info(target);
|
||||||
if (register_read(target, &value, regid) != ERROR_OK) {
|
|
||||||
// TODO: propagate errors
|
maybe_write_tselect(target);
|
||||||
value = ~0;
|
riscv_reg_t value = ~0;
|
||||||
|
|
||||||
|
if (regid <= GDB_REGNO_XPR31) {
|
||||||
|
value = reg_cache_get(target, regid);
|
||||||
|
} else if (regid == GDB_REGNO_PC) {
|
||||||
|
value = info->dpc;
|
||||||
|
} else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) {
|
||||||
|
int result = update_mstatus_actual(target);
|
||||||
|
if (result != ERROR_OK) {
|
||||||
|
return ~0;
|
||||||
|
}
|
||||||
|
unsigned i = 0;
|
||||||
|
if ((info->mstatus_actual & MSTATUS_FS) == 0) {
|
||||||
|
info->mstatus_actual = set_field(info->mstatus_actual, MSTATUS_FS, 1);
|
||||||
|
cache_set_load(target, i++, S0, SLOT1);
|
||||||
|
cache_set32(target, i++, csrw(S0, CSR_MSTATUS));
|
||||||
|
cache_set(target, SLOT1, info->mstatus_actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (riscv_xlen(target) == 32) {
|
||||||
|
cache_set32(target, i++, fsw(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
|
||||||
|
} else {
|
||||||
|
cache_set32(target, i++, fsd(regid - GDB_REGNO_FPR0, 0, DEBUG_RAM_START + 16));
|
||||||
|
}
|
||||||
|
cache_set_jump(target, i++);
|
||||||
|
|
||||||
|
if (cache_write(target, 4, true) != ERROR_OK) {
|
||||||
|
return ~0;
|
||||||
|
}
|
||||||
|
} else if (regid == GDB_REGNO_PRIV) {
|
||||||
|
value = get_field(info->dcsr, DCSR_PRV);
|
||||||
|
} else {
|
||||||
|
if (register_read(target, &value, regid) != ERROR_OK) {
|
||||||
|
value = ~0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (regid == GDB_REGNO_MSTATUS) {
|
||||||
|
target->reg_cache->reg_list[regid].valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1466,45 +1405,10 @@ static int init_target(struct command_context *cmd_ctx,
|
||||||
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
|
generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
|
||||||
if (!generic_info->version_specific)
|
if (!generic_info->version_specific)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
riscv011_info_t *info = get_info(target);
|
|
||||||
|
|
||||||
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
|
// Assume 32-bit until we discover the real value in examine().
|
||||||
target->reg_cache->name = "RISC-V registers";
|
generic_info->xlen[0] = 32;
|
||||||
target->reg_cache->num_regs = GDB_REGNO_COUNT;
|
riscv_init_registers(target);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
} else if (i == GDB_REGNO_PC) {
|
|
||||||
sprintf(reg_name, "pc");
|
|
||||||
} else if (i >= GDB_REGNO_FPR0 && i <= GDB_REGNO_FPR31) {
|
|
||||||
sprintf(reg_name, "f%d", i - GDB_REGNO_FPR0);
|
|
||||||
} else if (i >= GDB_REGNO_CSR0 && i <= GDB_REGNO_CSR4095) {
|
|
||||||
sprintf(reg_name, "csr%d", i - GDB_REGNO_CSR0);
|
|
||||||
} else if (i == GDB_REGNO_PRIV) {
|
|
||||||
sprintf(reg_name, "priv");
|
|
||||||
}
|
|
||||||
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;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -1690,9 +1594,6 @@ static int examine(struct target *target)
|
||||||
}
|
}
|
||||||
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
|
LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
|
||||||
|
|
||||||
// Update register list to match discovered XLEN.
|
|
||||||
update_reg_list(target);
|
|
||||||
|
|
||||||
if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
|
if (read_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
|
||||||
const unsigned old_csr_misa = 0xf10;
|
const unsigned old_csr_misa = 0xf10;
|
||||||
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
|
LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
|
||||||
|
@ -1705,6 +1606,9 @@ static int examine(struct target *target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update register list to match discovered XLEN/supported extensions.
|
||||||
|
riscv_init_registers(target);
|
||||||
|
|
||||||
info->never_halted = true;
|
info->never_halted = true;
|
||||||
|
|
||||||
int result = riscv011_poll(target);
|
int result = riscv011_poll(target);
|
||||||
|
|
|
@ -150,12 +150,6 @@ typedef struct {
|
||||||
/* We only need the address so that we know the alignment of the buffer. */
|
/* We only need the address so that we know the alignment of the buffer. */
|
||||||
riscv_addr_t progbuf_address;
|
riscv_addr_t progbuf_address;
|
||||||
|
|
||||||
/* Single buffer that contains all register names, instead of calling
|
|
||||||
* malloc for each register. Needs to be freed when reg_list is freed. */
|
|
||||||
char *reg_names;
|
|
||||||
/* Single buffer that contains all register values. */
|
|
||||||
void *reg_values;
|
|
||||||
|
|
||||||
// Number of run-test/idle cycles the target requests we do after each dbus
|
// Number of run-test/idle cycles the target requests we do after each dbus
|
||||||
// access.
|
// access.
|
||||||
unsigned int dtmcontrol_idle;
|
unsigned int dtmcontrol_idle;
|
||||||
|
@ -290,10 +284,6 @@ static riscv013_info_t *get_info(const struct target *target)
|
||||||
return (riscv013_info_t *) info->version_specific;
|
return (riscv013_info_t *) info->version_specific;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Necessary prototypes. ***/
|
|
||||||
|
|
||||||
static int register_get(struct reg *reg);
|
|
||||||
|
|
||||||
/*** Utility functions. ***/
|
/*** Utility functions. ***/
|
||||||
|
|
||||||
static void select_dmi(struct target *target)
|
static void select_dmi(struct target *target)
|
||||||
|
@ -1070,225 +1060,6 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
|
||||||
|
|
||||||
/*** OpenOCD target functions. ***/
|
/*** OpenOCD target functions. ***/
|
||||||
|
|
||||||
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, reg->size, value);
|
|
||||||
return ERROR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_write(struct target *target, unsigned int number,
|
|
||||||
uint64_t value)
|
|
||||||
{
|
|
||||||
riscv_set_register(target, number, value);
|
|
||||||
return ERROR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, reg->size);
|
|
||||||
|
|
||||||
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
|
|
||||||
struct reg *r = &target->reg_cache->reg_list[reg->number];
|
|
||||||
r->valid = true;
|
|
||||||
memcpy(r->value, buf, (r->size + 7) / 8);
|
|
||||||
|
|
||||||
return register_write(target, reg->number, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct reg_arch_type riscv_reg_arch_type = {
|
|
||||||
.get = register_get,
|
|
||||||
.set = register_set
|
|
||||||
};
|
|
||||||
|
|
||||||
struct csr_info {
|
|
||||||
unsigned number;
|
|
||||||
const char *name;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int cmp_csr_info(const void *p1, const void *p2)
|
|
||||||
{
|
|
||||||
return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number);
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
};
|
|
||||||
struct csr_info csr_info[] = {
|
|
||||||
#define DECLARE_CSR(name, number) { number, #name },
|
|
||||||
#include "encoding.h"
|
|
||||||
#undef DECLARE_CSR
|
|
||||||
};
|
|
||||||
// encoding.h does not contain the registers in sorted order.
|
|
||||||
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
|
|
||||||
unsigned csr_info_index = 0;
|
|
||||||
|
|
||||||
// When gdb request 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
|
|
||||||
// it would be nice not to have yet another set of numbers to translate
|
|
||||||
// between).
|
|
||||||
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
|
|
||||||
struct reg *r = &target->reg_cache->reg_list[number];
|
|
||||||
r->caller_save = true;
|
|
||||||
r->dirty = false;
|
|
||||||
r->valid = false;
|
|
||||||
r->exist = true;
|
|
||||||
r->type = &riscv_reg_arch_type;
|
|
||||||
r->arch_info = target;
|
|
||||||
r->number = number;
|
|
||||||
// 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 (number <= GDB_REGNO_XPR31) {
|
|
||||||
switch (number) {
|
|
||||||
case GDB_REGNO_ZERO: r->name = "zero"; break;
|
|
||||||
case GDB_REGNO_RA: r->name = "ra"; break;
|
|
||||||
case GDB_REGNO_SP: r->name = "sp"; break;
|
|
||||||
case GDB_REGNO_GP: r->name = "gp"; break;
|
|
||||||
case GDB_REGNO_TP: r->name = "tp"; break;
|
|
||||||
case GDB_REGNO_T0: r->name = "t0"; break;
|
|
||||||
case GDB_REGNO_T1: r->name = "t1"; break;
|
|
||||||
case GDB_REGNO_T2: r->name = "t2"; break;
|
|
||||||
case GDB_REGNO_FP: r->name = "fp"; break;
|
|
||||||
case GDB_REGNO_S1: r->name = "s1"; break;
|
|
||||||
case GDB_REGNO_A0: r->name = "a0"; break;
|
|
||||||
case GDB_REGNO_A1: r->name = "a1"; break;
|
|
||||||
case GDB_REGNO_A2: r->name = "a2"; break;
|
|
||||||
case GDB_REGNO_A3: r->name = "a3"; break;
|
|
||||||
case GDB_REGNO_A4: r->name = "a4"; break;
|
|
||||||
case GDB_REGNO_A5: r->name = "a5"; break;
|
|
||||||
case GDB_REGNO_A6: r->name = "a6"; break;
|
|
||||||
case GDB_REGNO_A7: r->name = "a7"; break;
|
|
||||||
case GDB_REGNO_S2: r->name = "s2"; break;
|
|
||||||
case GDB_REGNO_S3: r->name = "s3"; break;
|
|
||||||
case GDB_REGNO_S4: r->name = "s4"; break;
|
|
||||||
case GDB_REGNO_S5: r->name = "s5"; break;
|
|
||||||
case GDB_REGNO_S6: r->name = "s6"; break;
|
|
||||||
case GDB_REGNO_S7: r->name = "s7"; break;
|
|
||||||
case GDB_REGNO_S8: r->name = "s8"; break;
|
|
||||||
case GDB_REGNO_S9: r->name = "s9"; break;
|
|
||||||
case GDB_REGNO_S10: r->name = "s10"; break;
|
|
||||||
case GDB_REGNO_S11: r->name = "s11"; break;
|
|
||||||
case GDB_REGNO_T3: r->name = "t3"; break;
|
|
||||||
case GDB_REGNO_T4: r->name = "t4"; break;
|
|
||||||
case GDB_REGNO_T5: r->name = "t5"; break;
|
|
||||||
case GDB_REGNO_T6: r->name = "t6"; break;
|
|
||||||
}
|
|
||||||
r->group = "general";
|
|
||||||
r->feature = &feature_cpu;
|
|
||||||
} else if (number == GDB_REGNO_PC) {
|
|
||||||
sprintf(reg_name, "pc");
|
|
||||||
r->group = "general";
|
|
||||||
r->feature = &feature_cpu;
|
|
||||||
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
|
|
||||||
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 {
|
|
||||||
r->exist = false;
|
|
||||||
}
|
|
||||||
sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0);
|
|
||||||
r->group = "float";
|
|
||||||
r->feature = &feature_fpu;
|
|
||||||
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
|
||||||
r->group = "csr";
|
|
||||||
r->feature = &feature_csr;
|
|
||||||
r->exist = true;
|
|
||||||
unsigned csr_number = number - GDB_REGNO_CSR0;
|
|
||||||
|
|
||||||
while (csr_info[csr_info_index].number < csr_number) {
|
|
||||||
csr_info_index++;
|
|
||||||
}
|
|
||||||
if (csr_info[csr_info_index].number == csr_number) {
|
|
||||||
r->name = csr_info[csr_info_index].name;
|
|
||||||
} else {
|
|
||||||
sprintf(reg_name, "csr%d", csr_number);
|
|
||||||
// Assume unnamed registers don't exist, unless we have some
|
|
||||||
// configuration that tells us otherwise. That's important
|
|
||||||
// because eg. Eclipse crashes if a target has too many
|
|
||||||
// registers, and apparently has no way of only showing a
|
|
||||||
// subset of registers in any case.
|
|
||||||
r->exist = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (csr_number) {
|
|
||||||
case CSR_FFLAGS:
|
|
||||||
r->exist = riscv_supports_extension(target, 'F');
|
|
||||||
r->group = "float";
|
|
||||||
r->feature = &feature_fpu;
|
|
||||||
break;
|
|
||||||
case CSR_FRM:
|
|
||||||
r->exist = riscv_supports_extension(target, 'F');
|
|
||||||
r->group = "float";
|
|
||||||
r->feature = &feature_fpu;
|
|
||||||
break;
|
|
||||||
case CSR_FCSR:
|
|
||||||
r->exist = riscv_supports_extension(target, 'F');
|
|
||||||
r->group = "float";
|
|
||||||
r->feature = &feature_fpu;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (number == 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,
|
static int init_target(struct command_context *cmd_ctx,
|
||||||
struct target *target)
|
struct target *target)
|
||||||
{
|
{
|
||||||
|
@ -1438,12 +1209,24 @@ static int examine(struct target *target)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Reset hartid to one that exists. Set coreid to avoid assert in
|
||||||
|
// riscv_set_current_hartid(). (TODO)
|
||||||
|
target->coreid = 0;
|
||||||
|
riscv_set_current_hartid(target, 0);
|
||||||
|
|
||||||
target->coreid = original_coreid;
|
target->coreid = original_coreid;
|
||||||
|
|
||||||
LOG_DEBUG("Enumerated %d harts", r->hart_count);
|
LOG_DEBUG("Enumerated %d harts", r->hart_count);
|
||||||
|
|
||||||
|
// Assume 32-bit until we discover the real value in examine().
|
||||||
|
for (int i = 0; i < riscv_count_harts(target); ++i) {
|
||||||
|
if (riscv_hart_enabled(target, i)) {
|
||||||
|
r->xlen[i] = 32;
|
||||||
|
}
|
||||||
|
LOG_DEBUG(">>> temporary XLEN for hart %d is %d", i, r->xlen[i]);
|
||||||
|
}
|
||||||
// Get a functional register cache going.
|
// Get a functional register cache going.
|
||||||
if (init_registers(target) != ERROR_OK)
|
if (riscv_init_registers(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
/* Halt every hart so we can probe them. */
|
/* Halt every hart so we can probe them. */
|
||||||
|
@ -1484,7 +1267,7 @@ static int examine(struct target *target)
|
||||||
target->state = TARGET_RUNNING;
|
target->state = TARGET_RUNNING;
|
||||||
|
|
||||||
// Now reinit registers based on what we discovered.
|
// Now reinit registers based on what we discovered.
|
||||||
if (init_registers(target) != ERROR_OK)
|
if (riscv_init_registers(target) != ERROR_OK)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
target_set_examined(target);
|
target_set_examined(target);
|
||||||
|
|
|
@ -415,7 +415,6 @@ static int add_trigger(struct target *target, struct trigger *trigger)
|
||||||
|
|
||||||
int result = ERROR_OK;
|
int result = ERROR_OK;
|
||||||
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
|
for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
|
||||||
LOG_DEBUG(">>> hartid=%d", hartid);
|
|
||||||
if (!riscv_hart_enabled(target, hartid))
|
if (!riscv_hart_enabled(target, hartid))
|
||||||
continue;
|
continue;
|
||||||
if (hartid > first_hart) {
|
if (hartid > first_hart) {
|
||||||
|
@ -1248,6 +1247,7 @@ void riscv_info_init(struct target *target, riscv_info_t *r)
|
||||||
memset(r, 0, sizeof(*r));
|
memset(r, 0, sizeof(*r));
|
||||||
r->dtm_version = 1;
|
r->dtm_version = 1;
|
||||||
r->registers_initialized = false;
|
r->registers_initialized = false;
|
||||||
|
LOG_DEBUG(">>> current_hartid=%d", target->coreid);
|
||||||
r->current_hartid = target->coreid;
|
r->current_hartid = target->coreid;
|
||||||
|
|
||||||
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
|
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
|
||||||
|
@ -1360,6 +1360,7 @@ int riscv_xlen(const struct target *target)
|
||||||
int riscv_xlen_of_hart(const struct target *target, int hartid)
|
int riscv_xlen_of_hart(const struct target *target, int hartid)
|
||||||
{
|
{
|
||||||
RISCV_INFO(r);
|
RISCV_INFO(r);
|
||||||
|
LOG_DEBUG(">>> xlen[%d] = %d", hartid, r->xlen[hartid]);
|
||||||
assert(r->xlen[hartid] != -1);
|
assert(r->xlen[hartid] != -1);
|
||||||
return r->xlen[hartid];
|
return r->xlen[hartid];
|
||||||
}
|
}
|
||||||
|
@ -1406,21 +1407,7 @@ void riscv_invalidate_register_cache(struct target *target)
|
||||||
register_cache_invalidate(target->reg_cache);
|
register_cache_invalidate(target->reg_cache);
|
||||||
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
|
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
|
||||||
struct reg *reg = &target->reg_cache->reg_list[i];
|
struct reg *reg = &target->reg_cache->reg_list[i];
|
||||||
|
|
||||||
reg->value = &r->reg_cache_values[i];
|
|
||||||
reg->valid = false;
|
reg->valid = false;
|
||||||
|
|
||||||
if (reg->number == GDB_REGNO_PRIV) {
|
|
||||||
reg->size = 8;
|
|
||||||
} else if (reg->number >= GDB_REGNO_FPR0 && reg->number <= 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r->registers_initialized = true;
|
r->registers_initialized = true;
|
||||||
|
@ -1460,6 +1447,7 @@ bool riscv_has_register(struct target *target, int hartid, int regid)
|
||||||
|
|
||||||
void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
|
void riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
|
||||||
{
|
{
|
||||||
|
// TODO: propagate errors
|
||||||
return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
|
return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1690,3 +1678,220 @@ const char *gdb_regno_name(enum gdb_regno regno)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, reg->size, value);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, reg->size);
|
||||||
|
|
||||||
|
LOG_DEBUG("write 0x%" PRIx64 " to %s", value, reg->name);
|
||||||
|
struct reg *r = &target->reg_cache->reg_list[reg->number];
|
||||||
|
r->valid = true;
|
||||||
|
memcpy(r->value, buf, (r->size + 7) / 8);
|
||||||
|
|
||||||
|
riscv_set_register(target, reg->number, value);
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct reg_arch_type riscv_reg_arch_type = {
|
||||||
|
.get = register_get,
|
||||||
|
.set = register_set
|
||||||
|
};
|
||||||
|
|
||||||
|
struct csr_info {
|
||||||
|
unsigned number;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cmp_csr_info(const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number);
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_init_registers(struct target *target)
|
||||||
|
{
|
||||||
|
RISCV_INFO(info);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
struct csr_info csr_info[] = {
|
||||||
|
#define DECLARE_CSR(name, number) { number, #name },
|
||||||
|
#include "encoding.h"
|
||||||
|
#undef DECLARE_CSR
|
||||||
|
};
|
||||||
|
// encoding.h does not contain the registers in sorted order.
|
||||||
|
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
|
||||||
|
unsigned csr_info_index = 0;
|
||||||
|
|
||||||
|
// When gdb request 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
|
||||||
|
// it would be nice not to have yet another set of numbers to translate
|
||||||
|
// between).
|
||||||
|
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
|
||||||
|
struct reg *r = &target->reg_cache->reg_list[number];
|
||||||
|
r->caller_save = true;
|
||||||
|
r->dirty = false;
|
||||||
|
r->valid = false;
|
||||||
|
r->exist = true;
|
||||||
|
r->type = &riscv_reg_arch_type;
|
||||||
|
r->arch_info = target;
|
||||||
|
r->number = number;
|
||||||
|
r->size = riscv_xlen(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 (number <= GDB_REGNO_XPR31) {
|
||||||
|
switch (number) {
|
||||||
|
case GDB_REGNO_ZERO: r->name = "zero"; break;
|
||||||
|
case GDB_REGNO_RA: r->name = "ra"; break;
|
||||||
|
case GDB_REGNO_SP: r->name = "sp"; break;
|
||||||
|
case GDB_REGNO_GP: r->name = "gp"; break;
|
||||||
|
case GDB_REGNO_TP: r->name = "tp"; break;
|
||||||
|
case GDB_REGNO_T0: r->name = "t0"; break;
|
||||||
|
case GDB_REGNO_T1: r->name = "t1"; break;
|
||||||
|
case GDB_REGNO_T2: r->name = "t2"; break;
|
||||||
|
case GDB_REGNO_FP: r->name = "fp"; break;
|
||||||
|
case GDB_REGNO_S1: r->name = "s1"; break;
|
||||||
|
case GDB_REGNO_A0: r->name = "a0"; break;
|
||||||
|
case GDB_REGNO_A1: r->name = "a1"; break;
|
||||||
|
case GDB_REGNO_A2: r->name = "a2"; break;
|
||||||
|
case GDB_REGNO_A3: r->name = "a3"; break;
|
||||||
|
case GDB_REGNO_A4: r->name = "a4"; break;
|
||||||
|
case GDB_REGNO_A5: r->name = "a5"; break;
|
||||||
|
case GDB_REGNO_A6: r->name = "a6"; break;
|
||||||
|
case GDB_REGNO_A7: r->name = "a7"; break;
|
||||||
|
case GDB_REGNO_S2: r->name = "s2"; break;
|
||||||
|
case GDB_REGNO_S3: r->name = "s3"; break;
|
||||||
|
case GDB_REGNO_S4: r->name = "s4"; break;
|
||||||
|
case GDB_REGNO_S5: r->name = "s5"; break;
|
||||||
|
case GDB_REGNO_S6: r->name = "s6"; break;
|
||||||
|
case GDB_REGNO_S7: r->name = "s7"; break;
|
||||||
|
case GDB_REGNO_S8: r->name = "s8"; break;
|
||||||
|
case GDB_REGNO_S9: r->name = "s9"; break;
|
||||||
|
case GDB_REGNO_S10: r->name = "s10"; break;
|
||||||
|
case GDB_REGNO_S11: r->name = "s11"; break;
|
||||||
|
case GDB_REGNO_T3: r->name = "t3"; break;
|
||||||
|
case GDB_REGNO_T4: r->name = "t4"; break;
|
||||||
|
case GDB_REGNO_T5: r->name = "t5"; break;
|
||||||
|
case GDB_REGNO_T6: r->name = "t6"; break;
|
||||||
|
}
|
||||||
|
r->group = "general";
|
||||||
|
r->feature = &feature_cpu;
|
||||||
|
} else if (number == GDB_REGNO_PC) {
|
||||||
|
sprintf(reg_name, "pc");
|
||||||
|
r->group = "general";
|
||||||
|
r->feature = &feature_cpu;
|
||||||
|
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
|
||||||
|
if (riscv_supports_extension(target, 'D')) {
|
||||||
|
r->reg_data_type = &type_ieee_double;
|
||||||
|
r->size = 64;
|
||||||
|
} else if (riscv_supports_extension(target, 'F')) {
|
||||||
|
r->reg_data_type = &type_ieee_single;
|
||||||
|
r->size = 32;
|
||||||
|
} else {
|
||||||
|
r->exist = false;
|
||||||
|
}
|
||||||
|
sprintf(reg_name, "f%d", number - GDB_REGNO_FPR0);
|
||||||
|
r->group = "float";
|
||||||
|
r->feature = &feature_fpu;
|
||||||
|
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
||||||
|
r->group = "csr";
|
||||||
|
r->feature = &feature_csr;
|
||||||
|
unsigned csr_number = number - GDB_REGNO_CSR0;
|
||||||
|
|
||||||
|
while (csr_info[csr_info_index].number < csr_number &&
|
||||||
|
csr_info_index < DIM(csr_info) - 1) {
|
||||||
|
csr_info_index++;
|
||||||
|
}
|
||||||
|
if (csr_info[csr_info_index].number == csr_number) {
|
||||||
|
r->name = csr_info[csr_info_index].name;
|
||||||
|
} else {
|
||||||
|
sprintf(reg_name, "csr%d", csr_number);
|
||||||
|
// Assume unnamed registers don't exist, unless we have some
|
||||||
|
// configuration that tells us otherwise. That's important
|
||||||
|
// because eg. Eclipse crashes if a target has too many
|
||||||
|
// registers, and apparently has no way of only showing a
|
||||||
|
// subset of registers in any case.
|
||||||
|
r->exist = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (csr_number) {
|
||||||
|
case CSR_FFLAGS:
|
||||||
|
r->exist = riscv_supports_extension(target, 'F');
|
||||||
|
r->group = "float";
|
||||||
|
r->feature = &feature_fpu;
|
||||||
|
break;
|
||||||
|
case CSR_FRM:
|
||||||
|
r->exist = riscv_supports_extension(target, 'F');
|
||||||
|
r->group = "float";
|
||||||
|
r->feature = &feature_fpu;
|
||||||
|
break;
|
||||||
|
case CSR_FCSR:
|
||||||
|
r->exist = riscv_supports_extension(target, 'F');
|
||||||
|
r->group = "float";
|
||||||
|
r->feature = &feature_fpu;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (number == 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);
|
||||||
|
r->value = &info->reg_cache_values[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,10 @@ typedef struct {
|
||||||
/* The register cache points into here. */
|
/* The register cache points into here. */
|
||||||
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
|
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
|
||||||
|
|
||||||
|
/* Single buffer that contains all register names, instead of calling
|
||||||
|
* malloc for each register. Needs to be freed when reg_list is freed. */
|
||||||
|
char *reg_names;
|
||||||
|
|
||||||
/* 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];
|
||||||
|
|
||||||
|
@ -243,4 +247,6 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
|
||||||
int riscv_remove_watchpoint(struct target *target,
|
int riscv_remove_watchpoint(struct target *target,
|
||||||
struct watchpoint *watchpoint);
|
struct watchpoint *watchpoint);
|
||||||
|
|
||||||
|
int riscv_init_registers(struct target *target);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue