Add command to expose custom registers (#293)
* Added `riscv expose_custom` command. Seems to work for reading. I need to do some more testing for writes, as well as minor cleanup. Change-Id: I85d5d00897d5da4add4a6643b538be37d31a016f * Conform to OpenOCD style. Change-Id: I40a316f06f418d2b63d9e11aea03ef51da8d8faf * Free all the memory allocated by register init. Change-Id: I04e35ab54613f99708cee85e41fef989079adefc * Properly document `riscv expose_custom`. Change-Id: Id78a02b7a00c161df80f11b521a306e0cf3d7478
This commit is contained in:
parent
074b4fabed
commit
b4b2ec7d2d
|
@ -8998,6 +8998,14 @@ command can be used if OpenOCD gets this wrong, or a target implements custom
|
||||||
CSRs.
|
CSRs.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv expose_csrs} n0[-m0][,n1[-m1]]...
|
||||||
|
The RISC-V Debug Specification allows targets to expose custom registers
|
||||||
|
through abstract commands. (See Section 3.5.1.1 in that document.) This command
|
||||||
|
configures a list of inclusive ranges of those registers to expose. Number 0
|
||||||
|
indicates the first custom register, whose abstract command number is 0xc000.
|
||||||
|
This command must be executed before `init`.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@deffn Command {riscv set_command_timeout_sec} [seconds]
|
@deffn Command {riscv set_command_timeout_sec} [seconds]
|
||||||
Set the wall-clock timeout (in seconds) for individual commands. The default
|
Set the wall-clock timeout (in seconds) for individual commands. The default
|
||||||
should work fine for all but the slowest targets (eg. simulators).
|
should work fine for all but the slowest targets (eg. simulators).
|
||||||
|
|
|
@ -745,8 +745,8 @@ static int write_abstract_arg(struct target *target, unsigned index,
|
||||||
/**
|
/**
|
||||||
* @size in bits
|
* @size in bits
|
||||||
*/
|
*/
|
||||||
static uint32_t access_register_command(uint32_t number, unsigned size,
|
static uint32_t access_register_command(struct target *target, uint32_t number,
|
||||||
uint32_t flags)
|
unsigned size, uint32_t flags)
|
||||||
{
|
{
|
||||||
uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
|
uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
|
||||||
switch (size) {
|
switch (size) {
|
||||||
|
@ -769,8 +769,13 @@ static uint32_t access_register_command(uint32_t number, unsigned size,
|
||||||
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
||||||
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
|
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
|
||||||
number - GDB_REGNO_CSR0);
|
number - GDB_REGNO_CSR0);
|
||||||
} else {
|
} else if (number >= GDB_REGNO_COUNT) {
|
||||||
assert(0);
|
/* Custom register. */
|
||||||
|
assert(target->reg_cache->reg_list[number].arch_info);
|
||||||
|
riscv_reg_info_t *reg_info = target->reg_cache->reg_list[number].arch_info;
|
||||||
|
assert(reg_info);
|
||||||
|
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
|
||||||
|
0xc000 + reg_info->custom_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
command |= flags;
|
command |= flags;
|
||||||
|
@ -790,7 +795,7 @@ static int register_read_abstract(struct target *target, uint64_t *value,
|
||||||
!info->abstract_read_csr_supported)
|
!info->abstract_read_csr_supported)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
uint32_t command = access_register_command(number, size,
|
uint32_t command = access_register_command(target, number, size,
|
||||||
AC_ACCESS_REGISTER_TRANSFER);
|
AC_ACCESS_REGISTER_TRANSFER);
|
||||||
|
|
||||||
int result = execute_abstract_command(target, command);
|
int result = execute_abstract_command(target, command);
|
||||||
|
@ -825,7 +830,7 @@ static int register_write_abstract(struct target *target, uint32_t number,
|
||||||
!info->abstract_write_csr_supported)
|
!info->abstract_write_csr_supported)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
uint32_t command = access_register_command(number, size,
|
uint32_t command = access_register_command(target, number, size,
|
||||||
AC_ACCESS_REGISTER_TRANSFER |
|
AC_ACCESS_REGISTER_TRANSFER |
|
||||||
AC_ACCESS_REGISTER_WRITE);
|
AC_ACCESS_REGISTER_WRITE);
|
||||||
|
|
||||||
|
@ -1320,6 +1325,7 @@ static void deinit_target(struct target *target)
|
||||||
LOG_DEBUG("riscv_deinit_target()");
|
LOG_DEBUG("riscv_deinit_target()");
|
||||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||||
free(info->version_specific);
|
free(info->version_specific);
|
||||||
|
/* TODO: free register arch_info */
|
||||||
info->version_specific = NULL;
|
info->version_specific = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2072,9 +2078,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
|
||||||
result = register_write_direct(target, GDB_REGNO_S0, address);
|
result = register_write_direct(target, GDB_REGNO_S0, address);
|
||||||
if (result != ERROR_OK)
|
if (result != ERROR_OK)
|
||||||
goto error;
|
goto error;
|
||||||
uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target),
|
uint32_t command = access_register_command(target, GDB_REGNO_S1,
|
||||||
AC_ACCESS_REGISTER_TRANSFER |
|
riscv_xlen(target),
|
||||||
AC_ACCESS_REGISTER_POSTEXEC);
|
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
|
||||||
result = execute_abstract_command(target, command);
|
result = execute_abstract_command(target, command);
|
||||||
if (result != ERROR_OK)
|
if (result != ERROR_OK)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -2560,7 +2566,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
|
||||||
|
|
||||||
/* Write and execute command that moves value into S1 and
|
/* Write and execute command that moves value into S1 and
|
||||||
* executes program buffer. */
|
* executes program buffer. */
|
||||||
uint32_t command = access_register_command(GDB_REGNO_S1, 32,
|
uint32_t command = access_register_command(target,
|
||||||
|
GDB_REGNO_S1, 32,
|
||||||
AC_ACCESS_REGISTER_POSTEXEC |
|
AC_ACCESS_REGISTER_POSTEXEC |
|
||||||
AC_ACCESS_REGISTER_TRANSFER |
|
AC_ACCESS_REGISTER_TRANSFER |
|
||||||
AC_ACCESS_REGISTER_WRITE);
|
AC_ACCESS_REGISTER_WRITE);
|
||||||
|
|
|
@ -187,13 +187,17 @@ int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
|
||||||
|
|
||||||
bool riscv_prefer_sba;
|
bool riscv_prefer_sba;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t low, high;
|
||||||
|
} range_t;
|
||||||
|
|
||||||
/* In addition to the ones in the standard spec, we'll also expose additional
|
/* In addition to the ones in the standard spec, we'll also expose additional
|
||||||
* CSRs in this list.
|
* CSRs in this list.
|
||||||
* The list is either NULL, or a series of ranges (inclusive), terminated with
|
* The list is either NULL, or a series of ranges (inclusive), terminated with
|
||||||
* 1,0. */
|
* 1,0. */
|
||||||
struct {
|
range_t *expose_csr;
|
||||||
uint16_t low, high;
|
/* Same, but for custom registers. */
|
||||||
} *expose_csr;
|
range_t *expose_custom;
|
||||||
|
|
||||||
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
|
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
|
||||||
{
|
{
|
||||||
|
@ -272,8 +276,16 @@ static void riscv_deinit_target(struct target *target)
|
||||||
if (tt) {
|
if (tt) {
|
||||||
tt->deinit_target(target);
|
tt->deinit_target(target);
|
||||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||||
|
free(info->reg_names);
|
||||||
free(info);
|
free(info);
|
||||||
}
|
}
|
||||||
|
/* Free the shared structure use for most registers. */
|
||||||
|
free(target->reg_cache->reg_list[0].arch_info);
|
||||||
|
/* Free the ones we allocated separately. */
|
||||||
|
for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
|
||||||
|
free(target->reg_cache->reg_list[i].arch_info);
|
||||||
|
free(target->reg_cache->reg_list);
|
||||||
|
free(target->reg_cache);
|
||||||
target->arch_info = NULL;
|
target->arch_info = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +896,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
|
||||||
*reg_list_size = 32;
|
*reg_list_size = 32;
|
||||||
break;
|
break;
|
||||||
case REG_CLASS_ALL:
|
case REG_CLASS_ALL:
|
||||||
*reg_list_size = GDB_REGNO_COUNT;
|
*reg_list_size = target->reg_cache->num_regs;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR("Unsupported reg_class: %d", reg_class);
|
LOG_ERROR("Unsupported reg_class: %d", reg_class);
|
||||||
|
@ -1334,20 +1346,15 @@ void parse_error(const char *string, char c, unsigned position)
|
||||||
LOG_ERROR("%s", buf);
|
LOG_ERROR("%s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
COMMAND_HANDLER(riscv_set_expose_csrs)
|
int parse_ranges(range_t **ranges, const char **argv)
|
||||||
{
|
{
|
||||||
if (CMD_ARGC != 1) {
|
|
||||||
LOG_ERROR("Command takes exactly 1 parameter");
|
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned pass = 0; pass < 2; pass++) {
|
for (unsigned pass = 0; pass < 2; pass++) {
|
||||||
unsigned range = 0;
|
unsigned range = 0;
|
||||||
unsigned low = 0;
|
unsigned low = 0;
|
||||||
bool parse_low = true;
|
bool parse_low = true;
|
||||||
unsigned high = 0;
|
unsigned high = 0;
|
||||||
for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) {
|
for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
|
||||||
char c = CMD_ARGV[0][i];
|
char c = argv[0][i];
|
||||||
if (isspace(c)) {
|
if (isspace(c)) {
|
||||||
/* Ignore whitespace. */
|
/* Ignore whitespace. */
|
||||||
continue;
|
continue;
|
||||||
|
@ -1361,13 +1368,13 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||||
parse_low = false;
|
parse_low = false;
|
||||||
} else if (c == ',' || c == 0) {
|
} else if (c == ',' || c == 0) {
|
||||||
if (pass == 1) {
|
if (pass == 1) {
|
||||||
expose_csr[range].low = low;
|
(*ranges)[range].low = low;
|
||||||
expose_csr[range].high = low;
|
(*ranges)[range].high = low;
|
||||||
}
|
}
|
||||||
low = 0;
|
low = 0;
|
||||||
range++;
|
range++;
|
||||||
} else {
|
} else {
|
||||||
parse_error(CMD_ARGV[0], c, i);
|
parse_error(argv[0], c, i);
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1378,31 +1385,52 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||||
} else if (c == ',' || c == 0) {
|
} else if (c == ',' || c == 0) {
|
||||||
parse_low = true;
|
parse_low = true;
|
||||||
if (pass == 1) {
|
if (pass == 1) {
|
||||||
expose_csr[range].low = low;
|
(*ranges)[range].low = low;
|
||||||
expose_csr[range].high = high;
|
(*ranges)[range].high = high;
|
||||||
}
|
}
|
||||||
low = 0;
|
low = 0;
|
||||||
high = 0;
|
high = 0;
|
||||||
range++;
|
range++;
|
||||||
} else {
|
} else {
|
||||||
parse_error(CMD_ARGV[0], c, i);
|
parse_error(argv[0], c, i);
|
||||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pass == 0) {
|
if (pass == 0) {
|
||||||
if (expose_csr)
|
if (*ranges)
|
||||||
free(expose_csr);
|
free(*ranges);
|
||||||
expose_csr = calloc(range + 2, sizeof(*expose_csr));
|
*ranges = calloc(range + 2, sizeof(range_t));
|
||||||
} else {
|
} else {
|
||||||
expose_csr[range].low = 1;
|
(*ranges)[range].low = 1;
|
||||||
expose_csr[range].high = 0;
|
(*ranges)[range].high = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||||
|
{
|
||||||
|
if (CMD_ARGC != 1) {
|
||||||
|
LOG_ERROR("Command takes exactly 1 parameter");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_ranges(&expose_csr, CMD_ARGV);
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(riscv_set_expose_custom)
|
||||||
|
{
|
||||||
|
if (CMD_ARGC != 1) {
|
||||||
|
LOG_ERROR("Command takes exactly 1 parameter");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_ranges(&expose_custom, CMD_ARGV);
|
||||||
|
}
|
||||||
|
|
||||||
COMMAND_HANDLER(riscv_authdata_read)
|
COMMAND_HANDLER(riscv_authdata_read)
|
||||||
{
|
{
|
||||||
if (CMD_ARGC != 0) {
|
if (CMD_ARGC != 0) {
|
||||||
|
@ -1542,6 +1570,15 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||||
"addition to the standard ones. This must be executed before "
|
"addition to the standard ones. This must be executed before "
|
||||||
"`init`."
|
"`init`."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "expose_custom",
|
||||||
|
.handler = riscv_set_expose_custom,
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.usage = "riscv expose_custom n0[-m0][,n1[-m1]]...",
|
||||||
|
.help = "Configure a list of inclusive ranges for custom registers to "
|
||||||
|
"expose. custom0 is accessed as abstract register number 0xc000, "
|
||||||
|
"etc. This must be executed before `init`."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "authdata_read",
|
.name = "authdata_read",
|
||||||
.handler = riscv_authdata_read,
|
.handler = riscv_authdata_read,
|
||||||
|
@ -1850,7 +1887,7 @@ void riscv_invalidate_register_cache(struct target *target)
|
||||||
RISCV_INFO(r);
|
RISCV_INFO(r);
|
||||||
|
|
||||||
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 < target->reg_cache->num_regs; ++i) {
|
||||||
struct reg *reg = &target->reg_cache->reg_list[i];
|
struct reg *reg = &target->reg_cache->reg_list[i];
|
||||||
reg->valid = false;
|
reg->valid = false;
|
||||||
}
|
}
|
||||||
|
@ -2123,7 +2160,8 @@ const char *gdb_regno_name(enum gdb_regno regno)
|
||||||
|
|
||||||
static int register_get(struct reg *reg)
|
static int register_get(struct reg *reg)
|
||||||
{
|
{
|
||||||
struct target *target = (struct target *) reg->arch_info;
|
riscv_reg_info_t *reg_info = reg->arch_info;
|
||||||
|
struct target *target = reg_info->target;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
int result = riscv_get_register(target, &value, reg->number);
|
int result = riscv_get_register(target, &value, reg->number);
|
||||||
if (result != ERROR_OK)
|
if (result != ERROR_OK)
|
||||||
|
@ -2134,7 +2172,8 @@ static int register_get(struct reg *reg)
|
||||||
|
|
||||||
static int register_set(struct reg *reg, uint8_t *buf)
|
static int register_set(struct reg *reg, uint8_t *buf)
|
||||||
{
|
{
|
||||||
struct target *target = (struct target *) reg->arch_info;
|
riscv_reg_info_t *reg_info = reg->arch_info;
|
||||||
|
struct target *target = reg_info->target;
|
||||||
|
|
||||||
uint64_t value = buf_get_u64(buf, 0, reg->size);
|
uint64_t value = buf_get_u64(buf, 0, reg->size);
|
||||||
|
|
||||||
|
@ -2176,12 +2215,26 @@ int riscv_init_registers(struct target *target)
|
||||||
target->reg_cache->name = "RISC-V Registers";
|
target->reg_cache->name = "RISC-V Registers";
|
||||||
target->reg_cache->num_regs = GDB_REGNO_COUNT;
|
target->reg_cache->num_regs = GDB_REGNO_COUNT;
|
||||||
|
|
||||||
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
|
if (expose_custom) {
|
||||||
|
for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
|
||||||
|
for (unsigned number = expose_custom[i].low;
|
||||||
|
number <= expose_custom[i].high;
|
||||||
|
number++)
|
||||||
|
target->reg_cache->num_regs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("create register cache for %d registers",
|
||||||
|
target->reg_cache->num_regs);
|
||||||
|
|
||||||
|
target->reg_cache->reg_list =
|
||||||
|
calloc(target->reg_cache->num_regs, sizeof(struct reg));
|
||||||
|
|
||||||
const unsigned int max_reg_name_len = 12;
|
const unsigned int max_reg_name_len = 12;
|
||||||
if (info->reg_names)
|
if (info->reg_names)
|
||||||
free(info->reg_names);
|
free(info->reg_names);
|
||||||
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
|
info->reg_names =
|
||||||
|
calloc(target->reg_cache->num_regs, max_reg_name_len);
|
||||||
char *reg_name = info->reg_names;
|
char *reg_name = info->reg_names;
|
||||||
|
|
||||||
static struct reg_feature feature_cpu = {
|
static struct reg_feature feature_cpu = {
|
||||||
|
@ -2196,6 +2249,9 @@ int riscv_init_registers(struct target *target)
|
||||||
static struct reg_feature feature_virtual = {
|
static struct reg_feature feature_virtual = {
|
||||||
.name = "org.gnu.gdb.riscv.virtual"
|
.name = "org.gnu.gdb.riscv.virtual"
|
||||||
};
|
};
|
||||||
|
static struct reg_feature feature_custom = {
|
||||||
|
.name = "org.gnu.gdb.riscv.custom"
|
||||||
|
};
|
||||||
|
|
||||||
static struct reg_data_type type_ieee_single = {
|
static struct reg_data_type type_ieee_single = {
|
||||||
.type = REG_TYPE_IEEE_SINGLE,
|
.type = REG_TYPE_IEEE_SINGLE,
|
||||||
|
@ -2214,18 +2270,24 @@ int riscv_init_registers(struct target *target)
|
||||||
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
|
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
|
||||||
unsigned csr_info_index = 0;
|
unsigned csr_info_index = 0;
|
||||||
|
|
||||||
/* When gdb request register N, gdb_get_register_packet() assumes that this
|
unsigned custom_range_index = 0;
|
||||||
|
int custom_within_range = 0;
|
||||||
|
|
||||||
|
riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
|
||||||
|
shared_reg_info->target = 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
|
* 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
|
* 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
|
* it would be nice not to have yet another set of numbers to translate
|
||||||
* between). */
|
* between). */
|
||||||
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
|
for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) {
|
||||||
struct reg *r = &target->reg_cache->reg_list[number];
|
struct reg *r = &target->reg_cache->reg_list[number];
|
||||||
r->dirty = false;
|
r->dirty = false;
|
||||||
r->valid = false;
|
r->valid = false;
|
||||||
r->exist = true;
|
r->exist = true;
|
||||||
r->type = &riscv_reg_arch_type;
|
r->type = &riscv_reg_arch_type;
|
||||||
r->arch_info = target;
|
r->arch_info = shared_reg_info;
|
||||||
r->number = number;
|
r->number = number;
|
||||||
r->size = riscv_xlen(target);
|
r->size = riscv_xlen(target);
|
||||||
/* r->size is set in riscv_invalidate_register_cache, maybe because the
|
/* r->size is set in riscv_invalidate_register_cache, maybe because the
|
||||||
|
@ -2585,11 +2647,35 @@ int riscv_init_registers(struct target *target)
|
||||||
r->group = "general";
|
r->group = "general";
|
||||||
r->feature = &feature_virtual;
|
r->feature = &feature_virtual;
|
||||||
r->size = 8;
|
r->size = 8;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* Custom registers. */
|
||||||
|
assert(expose_custom);
|
||||||
|
|
||||||
|
range_t *range = &expose_custom[custom_range_index];
|
||||||
|
assert(range->low <= range->high);
|
||||||
|
unsigned custom_number = range->low + custom_within_range;
|
||||||
|
|
||||||
|
r->group = "custom";
|
||||||
|
r->feature = &feature_custom;
|
||||||
|
r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
|
||||||
|
assert(r->arch_info);
|
||||||
|
((riscv_reg_info_t *) r->arch_info)->target = target;
|
||||||
|
((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
|
||||||
|
sprintf(reg_name, "custom%d", custom_number);
|
||||||
|
|
||||||
|
custom_within_range++;
|
||||||
|
if (custom_within_range > range->high - range->low) {
|
||||||
|
custom_within_range = 0;
|
||||||
|
custom_range_index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg_name[0])
|
if (reg_name[0])
|
||||||
r->name = reg_name;
|
r->name = reg_name;
|
||||||
reg_name += strlen(reg_name) + 1;
|
reg_name += strlen(reg_name) + 1;
|
||||||
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
|
assert(reg_name < info->reg_names + target->reg_cache->num_regs *
|
||||||
|
max_reg_name_len);
|
||||||
r->value = &info->reg_cache_values[number];
|
r->value = &info->reg_cache_values[number];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,11 @@ enum riscv_halt_reason {
|
||||||
RISCV_HALT_ERROR
|
RISCV_HALT_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct target *target;
|
||||||
|
unsigned custom_number;
|
||||||
|
} riscv_reg_info_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned dtm_version;
|
unsigned dtm_version;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue