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
parent
89f07325f2
commit
bc72695f67
|
@ -9466,6 +9466,14 @@ command can be used if OpenOCD gets this wrong, or a target implements custom
|
|||
CSRs.
|
||||
@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]
|
||||
Set the wall-clock timeout (in seconds) for individual commands. The default
|
||||
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.
|
||||
@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
|
||||
|
||||
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 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)
|
||||
{
|
||||
scans += 4;
|
||||
struct riscv_batch *out = malloc(sizeof(*out));
|
||||
memset(out, 0, sizeof(*out));
|
||||
struct riscv_batch *out = calloc(1, sizeof(*out));
|
||||
out->target = target;
|
||||
out->allocated_scans = scans;
|
||||
out->used_scans = 0;
|
||||
out->idle_count = idle;
|
||||
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->fields = malloc(sizeof(*out->fields) * (scans));
|
||||
out->last_scan = RISCV_SCAN_TYPE_INVALID;
|
||||
out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
|
||||
out->read_keys_used = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -51,7 +48,6 @@ int riscv_batch_run(struct riscv_batch *batch)
|
|||
|
||||
keep_alive();
|
||||
|
||||
LOG_DEBUG("running a batch of %ld scans", (long)batch->used_scans);
|
||||
riscv_batch_add_nop(batch);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
LOG_DEBUG("executing queue");
|
||||
if (jtag_execute_queue() != ERROR_OK) {
|
||||
LOG_ERROR("Unable to execute JTAG queue");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -98,13 +93,10 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
|
|||
batch->used_scans++;
|
||||
|
||||
/* 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);
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
|
@ -135,10 +127,9 @@ void riscv_batch_add_nop(struct riscv_batch *batch)
|
|||
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
|
||||
batch->last_scan = RISCV_SCAN_TYPE_NOP;
|
||||
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 status_string[] = {"+", "?", "F", "b"};
|
||||
|
@ -160,13 +151,13 @@ void dump_field(const struct scan_field *field)
|
|||
|
||||
log_printf_lf(LOG_LVL_DEBUG,
|
||||
__FILE__, __LINE__, __PRETTY_FUNCTION__,
|
||||
"%db %s %08x @%02x -> %s %08x @%02x",
|
||||
field->num_bits,
|
||||
"%db %di %s %08x @%02x -> %s %08x @%02x",
|
||||
field->num_bits, idle,
|
||||
op_string[out_op], out_data, out_address,
|
||||
status_string[in_op], in_data, in_address);
|
||||
} else {
|
||||
log_printf_lf(LOG_LVL_DEBUG,
|
||||
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?",
|
||||
field->num_bits, op_string[out_op], out_data, out_address);
|
||||
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %di %s %08x @%02x -> ?",
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -52,8 +52,8 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
|
|||
* memory. */
|
||||
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
|
||||
* assembly into a multi-instruction sequence that overwrites some other
|
||||
/* Helpers to assemble various instructions. Return 0 on success. These might
|
||||
* assemble into a multi-instruction sequence that overwrites some other
|
||||
* 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_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)
|
||||
{
|
||||
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->in_value = in_value;
|
||||
|
@ -1408,12 +1417,6 @@ static int strict_step(struct target *target, bool announce)
|
|||
|
||||
LOG_DEBUG("enter");
|
||||
|
||||
struct breakpoint *breakpoint = target->breakpoints;
|
||||
while (breakpoint) {
|
||||
riscv_remove_breakpoint(target, breakpoint);
|
||||
breakpoint = breakpoint->next;
|
||||
}
|
||||
|
||||
struct watchpoint *watchpoint = target->watchpoints;
|
||||
while (watchpoint) {
|
||||
riscv_remove_watchpoint(target, watchpoint);
|
||||
|
@ -1424,12 +1427,6 @@ static int strict_step(struct target *target, bool announce)
|
|||
if (result != ERROR_OK)
|
||||
return result;
|
||||
|
||||
breakpoint = target->breakpoints;
|
||||
while (breakpoint) {
|
||||
riscv_add_breakpoint(target, breakpoint);
|
||||
breakpoint = breakpoint->next;
|
||||
}
|
||||
|
||||
watchpoint = target->watchpoints;
|
||||
while (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)
|
||||
return result;
|
||||
} else {
|
||||
return resume(target, 0, true);
|
||||
return full_step(target, false);
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
|
@ -1676,7 +1673,7 @@ static riscv_error_t handle_halt_routine(struct target *target)
|
|||
break;
|
||||
default:
|
||||
LOG_ERROR("Got invalid bus access status: %d", status);
|
||||
return ERROR_FAIL;
|
||||
goto error;
|
||||
}
|
||||
if (data & DMCONTROL_INTERRUPT) {
|
||||
interrupt_set++;
|
||||
|
@ -1850,7 +1847,7 @@ static int handle_halt(struct target *target, bool announce)
|
|||
target->debug_reason = DBG_REASON_BREAKPOINT;
|
||||
break;
|
||||
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
|
||||
* the disable-breakpoints-step-enable-breakpoints dance. */
|
||||
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
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct target *target;
|
||||
unsigned custom_number;
|
||||
} riscv_reg_info_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned dtm_version;
|
||||
|
||||
|
@ -91,6 +96,10 @@ typedef struct {
|
|||
|
||||
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
|
||||
* implementations. */
|
||||
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_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;
|
||||
|
||||
/* 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; }
|
||||
#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 uint8_t ir_dbus[1];
|
||||
extern uint8_t ir_dbus[4];
|
||||
extern struct scan_field select_dbus;
|
||||
extern uint8_t ir_idcode[1];
|
||||
extern uint8_t ir_idcode[4];
|
||||
extern struct scan_field select_idcode;
|
||||
|
||||
/*** 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_remove_watchpoint(struct target *target,
|
||||
struct watchpoint *watchpoint);
|
||||
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address);
|
||||
|
||||
int riscv_init_registers(struct target *target);
|
||||
|
||||
|
|
Loading…
Reference in New Issue