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>
This commit is contained in:
parent
89f07325f2
commit
bc72695f67
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue