Skip to content

Commit

Permalink
target/riscv: allow hexadecimal values to expose_csr-like commands
Browse files Browse the repository at this point in the history
hexadecimal values are often used in the documentation. Forcing user to convert
CSRs addresses to decimal is unnecessary.
  • Loading branch information
aap-sc committed Nov 11, 2024
1 parent f9a1292 commit 6f88874
Showing 1 changed file with 66 additions and 46 deletions.
112 changes: 66 additions & 46 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -4010,74 +4010,85 @@ COMMAND_HANDLER(riscv_set_mem_access)
return ERROR_OK;
}

static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
{
char *args = strdup(tcl_arg);
if (!args)
return ERROR_FAIL;

/* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */
char *arg = strtok(args, ",");
while (arg) {
static bool parse_csr_address(const char *reg_address_str, unsigned int *reg_addr)
{
*reg_addr = -1;
/* skip initial spaces */
while (isspace(reg_address_str[0]))
++reg_address_str;
/* try to detect if string starts with 0x or 0X */
bool is_hex_address = strncmp(reg_address_str, "0x", 2) == 0 ||
strncmp(reg_address_str, "0X", 2) == 0;

unsigned int scanned_chars;
if (is_hex_address) {
reg_address_str += 2;
if (sscanf(reg_address_str, "%x%n", reg_addr, &scanned_chars) != 1)
return false;
} else {
if (sscanf(reg_address_str, "%u%n", reg_addr, &scanned_chars) != 1)
return false;
}
return scanned_chars == strlen(reg_address_str);
}

static int parse_reg_ranges_impl(struct list_head *ranges, char *args,
const char *reg_type, unsigned int max_val, char ** const name_buffer)
{
/* For backward compatibility, allow multiple parameters within one TCL
* argument, separated by ',' */
for (char *arg = strtok(args, ","); arg; arg = strtok(NULL, ",")) {
unsigned int low = 0;
unsigned int high = 0;
char *name = NULL;

char *dash = strchr(arg, '-');
char *equals = strchr(arg, '=');
unsigned int pos;

if (!dash && !equals) {
/* Expecting single register number. */
if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
if (!parse_csr_address(arg, &low)) {
LOG_ERROR("Failed to parse single register number from '%s'.", arg);
free(args);
return ERROR_COMMAND_SYNTAX_ERROR;
}
} else if (dash && !equals) {
/* Expecting register range - two numbers separated by a dash: ##-## */
*dash = 0;
dash++;
if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
*dash = '\0';
if (!parse_csr_address(arg, &low)) {
LOG_ERROR("Failed to parse single register number from '%s'.", arg);
free(args);
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) {
LOG_ERROR("Failed to parse single register number from '%s'.", dash);
free(args);
const char *high_num_in = dash + 1;
if (!parse_csr_address(high_num_in, &high)) {
LOG_ERROR("Failed to parse single register number from '%s'.", high_num_in);
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (high < low) {
LOG_ERROR("Incorrect range encountered [%u, %u].", low, high);
free(args);
return ERROR_FAIL;
}
} else if (!dash && equals) {
/* Expecting single register number with textual name specified: ##=name */
*equals = 0;
equals++;
if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
*equals = '\0';
if (!parse_csr_address(arg, &low)) {
LOG_ERROR("Failed to parse single register number from '%s'.", arg);
free(args);
return ERROR_COMMAND_SYNTAX_ERROR;
}

name = calloc(1, strlen(equals) + strlen(reg_type) + 2);
const char * const reg_name_in = equals + 1;
*name_buffer = calloc(1, strlen(reg_name_in) + strlen(reg_type) + 2);
name = *name_buffer;
if (!name) {
LOG_ERROR("Failed to allocate register name.");
free(args);
return ERROR_FAIL;
}

/* Register prefix: "csr_" or "custom_" */
strcpy(name, reg_type);
name[strlen(reg_type)] = '_';

if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) {
LOG_ERROR("Failed to parse register name from '%s'.", equals);
free(args);
free(name);
unsigned int scanned_chars;
char *scan_dst = name + strlen(reg_type) + 1;
if (sscanf(reg_name_in, "%[_a-zA-Z0-9]%n", scan_dst, &scanned_chars) != 1 ||
scanned_chars != strlen(reg_name_in)) {
LOG_ERROR("Failed to parse register name from '%s'.", reg_name_in);
return ERROR_COMMAND_SYNTAX_ERROR;
}
} else {
Expand All @@ -4089,9 +4100,8 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha
high = MAX(high, low);

if (high > max_val) {
LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val);
free(name);
free(args);
LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.",
reg_type, high, max_val);
return ERROR_FAIL;
}

Expand All @@ -4109,32 +4119,42 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha

if (entry->name && name && (strcasecmp(entry->name, name) == 0)) {
LOG_ERROR("Duplicate register name \"%s\" found.", name);
free(name);
free(args);
return ERROR_FAIL;
}
}

range_list_t *range = calloc(1, sizeof(range_list_t));
if (!range) {
LOG_ERROR("Failed to allocate range list.");
free(name);
free(args);
return ERROR_FAIL;
}

range->low = low;
range->high = high;
range->name = name;
/* ownership over name_buffer contents is transferred to list item here */
*name_buffer = NULL;
list_add(&range->list, ranges);

arg = strtok(NULL, ",");
}

free(args);
return ERROR_OK;
}

static int parse_reg_ranges(struct list_head *ranges, const char *tcl_arg,
const char *reg_type, unsigned int max_val)
{
char *args = strdup(tcl_arg);
if (!args) {
LOG_ERROR("Failed to allocate memory for reg range string copy");
return ERROR_FAIL;
}
char *name_buffer = NULL;
int result = parse_reg_ranges_impl(ranges, args, reg_type, max_val, &name_buffer);
free(name_buffer);
free(args);
return result;
}

COMMAND_HANDLER(riscv_set_expose_csrs)
{
if (CMD_ARGC == 0) {
Expand All @@ -4147,7 +4167,7 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
int ret = ERROR_OK;

for (unsigned int i = 0; i < CMD_ARGC; i++) {
ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
ret = parse_reg_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
if (ret != ERROR_OK)
break;
}
Expand All @@ -4167,7 +4187,7 @@ COMMAND_HANDLER(riscv_set_expose_custom)
int ret = ERROR_OK;

for (unsigned int i = 0; i < CMD_ARGC; i++) {
ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
ret = parse_reg_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
if (ret != ERROR_OK)
break;
}
Expand All @@ -4187,7 +4207,7 @@ COMMAND_HANDLER(riscv_hide_csrs)
int ret = ERROR_OK;

for (unsigned int i = 0; i < CMD_ARGC; i++) {
ret = parse_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff);
ret = parse_reg_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff);
if (ret != ERROR_OK)
break;
}
Expand Down

0 comments on commit 6f88874

Please sign in to comment.