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:
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.
@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

View File

@ -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);
}
}

View File

@ -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)
{

View File

@ -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);

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)
{
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

View File

@ -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);