Lots of RISC-V improvements.

This represents months of continuing RISC-V work, with too many changes
to list individually. Some improvements:
* Fixed memory leaks.
* Better handling of dbus timeouts.
* Add `riscv expose_custom` command.
* Somewhat deal with cache coherency.
* Deal with more timeouts during block memory accesses.
* Basic debug compliance test.
* Tell gdb which watchpoint hit.
* SMP support for use with -rtos hwthread
* Add `riscv set_ir`

Change-Id: Ica507ee2a57eaf51b578ab1d9b7de71512fdf47f
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/4922
Tested-by: jenkins
Reviewed-by: Philipp Guehring <pg@futureware.at>
Reviewed-by: Liviu Ionescu <ilg@livius.net>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
reverse-resume-order
Tim Newsome 2019-02-15 12:08:51 -08:00 committed by Matthias Welwarsky
parent 89f07325f2
commit bc72695f67
8 changed files with 1850 additions and 446 deletions

View File

@ -9466,6 +9466,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_custom} 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).
@ -9486,6 +9494,17 @@ When on, prefer to use System Bus Access to access memory. When off, prefer to
use the Program Buffer to access memory. use the Program Buffer to access memory.
@end deffn @end deffn
@deffn Command {riscv set_ir} (@option{idcode}|@option{dtmcs}|@option{dmi}) [value]
Set the IR value for the specified JTAG register. This is useful, for
example, when using the existing JTAG interface on a Xilinx FPGA by
way of BSCANE2 primitives that only permit a limited selection of IR
values.
When utilizing version 0.11 of the RISC-V Debug Specification,
@option{dtmcs} and @option{dmi} set the IR values for the DTMCONTROL
and DBUS registers, respectively.
@end deffn
@subsection RISC-V Authentication Commands @subsection RISC-V Authentication Commands
The following commands can be used to authenticate to a RISC-V system. Eg. a The following commands can be used to authenticate to a RISC-V system. Eg. a

View File

@ -9,23 +9,20 @@
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
static void dump_field(const struct scan_field *field); static void dump_field(int idle, const struct scan_field *field);
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle) struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
{ {
scans += 4; scans += 4;
struct riscv_batch *out = malloc(sizeof(*out)); struct riscv_batch *out = calloc(1, sizeof(*out));
memset(out, 0, sizeof(*out));
out->target = target; out->target = target;
out->allocated_scans = scans; out->allocated_scans = scans;
out->used_scans = 0;
out->idle_count = idle; out->idle_count = idle;
out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t)); out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t));
out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t)); out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t));
out->fields = malloc(sizeof(*out->fields) * (scans)); out->fields = malloc(sizeof(*out->fields) * (scans));
out->last_scan = RISCV_SCAN_TYPE_INVALID; out->last_scan = RISCV_SCAN_TYPE_INVALID;
out->read_keys = malloc(sizeof(*out->read_keys) * (scans)); out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
out->read_keys_used = 0;
return out; return out;
} }
@ -51,7 +48,6 @@ int riscv_batch_run(struct riscv_batch *batch)
keep_alive(); keep_alive();
LOG_DEBUG("running a batch of %ld scans", (long)batch->used_scans);
riscv_batch_add_nop(batch); riscv_batch_add_nop(batch);
for (size_t i = 0; i < batch->used_scans; ++i) { for (size_t i = 0; i < batch->used_scans; ++i) {
@ -60,14 +56,13 @@ int riscv_batch_run(struct riscv_batch *batch)
jtag_add_runtest(batch->idle_count, TAP_IDLE); jtag_add_runtest(batch->idle_count, TAP_IDLE);
} }
LOG_DEBUG("executing queue");
if (jtag_execute_queue() != ERROR_OK) { if (jtag_execute_queue() != ERROR_OK) {
LOG_ERROR("Unable to execute JTAG queue"); LOG_ERROR("Unable to execute JTAG queue");
return ERROR_FAIL; return ERROR_FAIL;
} }
for (size_t i = 0; i < batch->used_scans; ++i) for (size_t i = 0; i < batch->used_scans; ++i)
dump_field(batch->fields + i); dump_field(batch->idle_count, batch->fields + i);
return ERROR_OK; return ERROR_OK;
} }
@ -98,13 +93,10 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
batch->used_scans++; batch->used_scans++;
/* FIXME We get the read response back on the next scan. For now I'm /* FIXME We get the read response back on the next scan. For now I'm
* just sticking a NOP in there, but this should be coelesced away. */ * just sticking a NOP in there, but this should be coalesced away. */
riscv_batch_add_nop(batch); riscv_batch_add_nop(batch);
batch->read_keys[batch->read_keys_used] = batch->used_scans - 1; batch->read_keys[batch->read_keys_used] = batch->used_scans - 1;
LOG_DEBUG("read key %u for batch 0x%p is %u (0x%p)",
(unsigned) batch->read_keys_used, batch, (unsigned) (batch->used_scans - 1),
batch->data_in + sizeof(uint64_t) * (batch->used_scans + 1));
return batch->read_keys_used++; return batch->read_keys_used++;
} }
@ -135,10 +127,9 @@ void riscv_batch_add_nop(struct riscv_batch *batch)
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value); riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_NOP; batch->last_scan = RISCV_SCAN_TYPE_NOP;
batch->used_scans++; batch->used_scans++;
LOG_DEBUG(" added NOP with in_value=0x%p", field->in_value);
} }
void dump_field(const struct scan_field *field) void dump_field(int idle, const struct scan_field *field)
{ {
static const char * const op_string[] = {"-", "r", "w", "?"}; static const char * const op_string[] = {"-", "r", "w", "?"};
static const char * const status_string[] = {"+", "?", "F", "b"}; static const char * const status_string[] = {"+", "?", "F", "b"};
@ -160,13 +151,13 @@ void dump_field(const struct scan_field *field)
log_printf_lf(LOG_LVL_DEBUG, log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__, __FILE__, __LINE__, __PRETTY_FUNCTION__,
"%db %s %08x @%02x -> %s %08x @%02x", "%db %di %s %08x @%02x -> %s %08x @%02x",
field->num_bits, field->num_bits, idle,
op_string[out_op], out_data, out_address, op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address); status_string[in_op], in_data, in_address);
} else { } else {
log_printf_lf(LOG_LVL_DEBUG, log_printf_lf(LOG_LVL_DEBUG,
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?", __FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %di %s %08x @%02x -> ?",
field->num_bits, op_string[out_op], out_data, out_address); field->num_bits, idle, op_string[out_op], out_data, out_address);
} }
} }

View File

@ -224,6 +224,9 @@ static uint32_t ebreak_c(void)
return MATCH_C_EBREAK; return MATCH_C_EBREAK;
} }
static uint32_t wfi(void) __attribute__ ((unused));
static uint32_t wfi(void) { return MATCH_WFI; }
static uint32_t fence_i(void) __attribute__ ((unused)); static uint32_t fence_i(void) __attribute__ ((unused));
static uint32_t fence_i(void) static uint32_t fence_i(void)
{ {

View File

@ -52,8 +52,8 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
* memory. */ * memory. */
int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save); int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
/* Helpers to assembly various instructions. Return 0 on success. These might /* Helpers to assemble various instructions. Return 0 on success. These might
* assembly into a multi-instruction sequence that overwrites some other * assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */ * register, but those will be properly saved and restored. */
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);

View File

@ -358,6 +358,15 @@ static void add_dbus_scan(const struct target *target, struct scan_field *field,
uint16_t address, uint64_t data) uint16_t address, uint64_t data)
{ {
riscv011_info_t *info = get_info(target); riscv011_info_t *info = get_info(target);
RISCV_INFO(r);
if (r->reset_delays_wait >= 0) {
r->reset_delays_wait--;
if (r->reset_delays_wait < 0) {
info->dbus_busy_delay = 0;
info->interrupt_high_delay = 0;
}
}
field->num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE; field->num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
field->in_value = in_value; field->in_value = in_value;
@ -1408,12 +1417,6 @@ static int strict_step(struct target *target, bool announce)
LOG_DEBUG("enter"); LOG_DEBUG("enter");
struct breakpoint *breakpoint = target->breakpoints;
while (breakpoint) {
riscv_remove_breakpoint(target, breakpoint);
breakpoint = breakpoint->next;
}
struct watchpoint *watchpoint = target->watchpoints; struct watchpoint *watchpoint = target->watchpoints;
while (watchpoint) { while (watchpoint) {
riscv_remove_watchpoint(target, watchpoint); riscv_remove_watchpoint(target, watchpoint);
@ -1424,12 +1427,6 @@ static int strict_step(struct target *target, bool announce)
if (result != ERROR_OK) if (result != ERROR_OK)
return result; return result;
breakpoint = target->breakpoints;
while (breakpoint) {
riscv_add_breakpoint(target, breakpoint);
breakpoint = breakpoint->next;
}
watchpoint = target->watchpoints; watchpoint = target->watchpoints;
while (watchpoint) { while (watchpoint) {
riscv_add_watchpoint(target, watchpoint); riscv_add_watchpoint(target, watchpoint);
@ -1463,7 +1460,7 @@ static int step(struct target *target, int current, target_addr_t address,
if (result != ERROR_OK) if (result != ERROR_OK)
return result; return result;
} else { } else {
return resume(target, 0, true); return full_step(target, false);
} }
return ERROR_OK; return ERROR_OK;
@ -1676,7 +1673,7 @@ static riscv_error_t handle_halt_routine(struct target *target)
break; break;
default: default:
LOG_ERROR("Got invalid bus access status: %d", status); LOG_ERROR("Got invalid bus access status: %d", status);
return ERROR_FAIL; goto error;
} }
if (data & DMCONTROL_INTERRUPT) { if (data & DMCONTROL_INTERRUPT) {
interrupt_set++; interrupt_set++;
@ -1850,7 +1847,7 @@ static int handle_halt(struct target *target, bool announce)
target->debug_reason = DBG_REASON_BREAKPOINT; target->debug_reason = DBG_REASON_BREAKPOINT;
break; break;
case DCSR_CAUSE_HWBP: case DCSR_CAUSE_HWBP:
target->debug_reason = DBG_REASON_WPTANDBKPT; target->debug_reason = DBG_REASON_WATCHPOINT;
/* If we halted because of a data trigger, gdb doesn't know to do /* If we halted because of a data trigger, gdb doesn't know to do
* the disable-breakpoints-step-enable-breakpoints dance. */ * the disable-breakpoints-step-enable-breakpoints dance. */
info->need_strict_step = true; info->need_strict_step = true;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
@ -91,6 +96,10 @@ typedef struct {
bool triggers_enumerated; bool triggers_enumerated;
/* Decremented every scan, and when it reaches 0 we clear the learned
* delays, causing them to be relearned. Used for testing. */
int reset_delays_wait;
/* Helper functions that target the various RISC-V debug spec /* Helper functions that target the various RISC-V debug spec
* implementations. */ * implementations. */
int (*get_register)(struct target *target, int (*get_register)(struct target *target,
@ -120,6 +129,11 @@ typedef struct {
int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address); int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
int (*dmi_write)(struct target *target, uint32_t address, uint32_t value); int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
int (*test_sba_config_reg)(struct target *target, target_addr_t legal_address,
uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
int (*test_compliance)(struct target *target);
} riscv_info_t; } riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
@ -137,11 +151,11 @@ static inline riscv_info_t *riscv_info(const struct target *target)
{ return target->arch_info; } { return target->arch_info; }
#define RISCV_INFO(R) riscv_info_t *R = riscv_info(target); #define RISCV_INFO(R) riscv_info_t *R = riscv_info(target);
extern uint8_t ir_dtmcontrol[1]; extern uint8_t ir_dtmcontrol[4];
extern struct scan_field select_dtmcontrol; extern struct scan_field select_dtmcontrol;
extern uint8_t ir_dbus[1]; extern uint8_t ir_dbus[4];
extern struct scan_field select_dbus; extern struct scan_field select_dbus;
extern uint8_t ir_idcode[1]; extern uint8_t ir_idcode[4];
extern struct scan_field select_idcode; extern struct scan_field select_idcode;
/*** OpenOCD Interface */ /*** OpenOCD Interface */
@ -253,6 +267,7 @@ int riscv_remove_breakpoint(struct target *target,
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); 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_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address);
int riscv_init_registers(struct target *target); int riscv_init_registers(struct target *target);