Add RISC-V support.
This supports both 0.11 and 0.13 versions of the debug spec. Support for `-rtos riscv` will come in a separate commit since it was easy to separate out, and is likely to be more controversial. Flash support for the SiFive boards will also come in a later commit. Change-Id: I1d38fe669c2041b4e21a5c54a091594aac3e2190 Signed-off-by: Tim Newsome <tim@sifive.com> Reviewed-on: http://openocd.zylin.com/4578 Tested-by: jenkins Reviewed-by: Liviu Ionescu <ilg@livius.net> Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
This commit is contained in:
parent
9363705820
commit
a51ab8ddf6
|
@ -8946,6 +8946,84 @@ Display all registers in @emph{group}.
|
||||||
"timer" or any new group created with addreg command.
|
"timer" or any new group created with addreg command.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@section RISC-V Architecture
|
||||||
|
|
||||||
|
@uref{http://riscv.org/, RISC-V} is a free and open ISA. OpenOCD supports JTAG
|
||||||
|
debug of targets that implement version 0.11 and 0.13 of the RISC-V Debug
|
||||||
|
Specification.
|
||||||
|
|
||||||
|
@subsection RISC-V Terminology
|
||||||
|
|
||||||
|
A @emph{hart} is a hardware thread. A hart may share resources (eg. FPU) with
|
||||||
|
another hart, or may be a separate core. RISC-V treats those the same, and
|
||||||
|
OpenOCD exposes each hart as a separate core.
|
||||||
|
|
||||||
|
@subsection RISC-V Debug Configuration Commands
|
||||||
|
|
||||||
|
@deffn Command {riscv expose_csrs} n0[-m0][,n1[-m1]]...
|
||||||
|
Configure a list of inclusive ranges for CSRs to expose in addition to the
|
||||||
|
standard ones. This must be executed before `init`.
|
||||||
|
|
||||||
|
By default OpenOCD attempts to expose only CSRs that are mentioned in a spec,
|
||||||
|
and then only if the corresponding extension appears to be implemented. This
|
||||||
|
command can be used if OpenOCD gets this wrong, or a target implements custom
|
||||||
|
CSRs.
|
||||||
|
@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).
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv set_reset_timeout_sec} [seconds]
|
||||||
|
Set the maximum time to wait for a hart to come out of reset after reset is
|
||||||
|
deasserted.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv set_scratch_ram} none|[address]
|
||||||
|
Set the address of 16 bytes of scratch RAM the debugger can use, or 'none'.
|
||||||
|
This is used to access 64-bit floating point registers on 32-bit targets.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv set_prefer_sba} on|off
|
||||||
|
When on, prefer to use System Bus Access to access memory. When off, prefer to
|
||||||
|
use the Program Buffer to access memory.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@subsection RISC-V Authentication Commands
|
||||||
|
|
||||||
|
The following commands can be used to authenticate to a RISC-V system. Eg. a
|
||||||
|
trivial challenge-response protocol could be implemented as follows in a
|
||||||
|
configuration file, immediately following @command{init}:
|
||||||
|
@example
|
||||||
|
set challenge [ocd_riscv authdata_read]
|
||||||
|
riscv authdata_write [expr $challenge + 1]
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@deffn Command {riscv authdata_read}
|
||||||
|
Return the 32-bit value read from authdata. Note that to get read value back in
|
||||||
|
a TCL script, it needs to be invoked as @command{ocd_riscv authdata_read}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv authdata_write} value
|
||||||
|
Write the 32-bit value to authdata.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@subsection RISC-V DMI Commands
|
||||||
|
|
||||||
|
The following commands allow direct access to the Debug Module Interface, which
|
||||||
|
can be used to interact with custom debug features.
|
||||||
|
|
||||||
|
@deffn Command {riscv dmi_read}
|
||||||
|
Perform a 32-bit DMI read at address, returning the value. Note that to get
|
||||||
|
read value back in a TCL script, it needs to be invoked as @command{ocd_riscv
|
||||||
|
dmi_read}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {riscv dmi_write} address value
|
||||||
|
Perform a 32-bit DMI write of value at address.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@anchor{softwaredebugmessagesandtracing}
|
@anchor{softwaredebugmessagesandtracing}
|
||||||
@section Software Debug Messages and Tracing
|
@section Software Debug Messages and Tracing
|
||||||
@cindex Linux-ARM DCC support
|
@cindex Linux-ARM DCC support
|
||||||
|
|
|
@ -149,6 +149,8 @@ extern int debug_level;
|
||||||
*/
|
*/
|
||||||
#define ERROR_FAIL (-4)
|
#define ERROR_FAIL (-4)
|
||||||
#define ERROR_WAIT (-5)
|
#define ERROR_WAIT (-5)
|
||||||
|
/* ERROR_TIMEOUT is already taken by winerror.h. */
|
||||||
|
#define ERROR_TIMEOUT_REACHED (-6)
|
||||||
|
|
||||||
|
|
||||||
#endif /* OPENOCD_HELPER_LOG_H */
|
#endif /* OPENOCD_HELPER_LOG_H */
|
||||||
|
|
|
@ -4,7 +4,9 @@ else
|
||||||
OOCD_TRACE_FILES =
|
OOCD_TRACE_FILES =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la
|
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
|
||||||
|
%D%/riscv/libriscv.la
|
||||||
|
|
||||||
|
|
||||||
STARTUP_TCL_SRCS += %D%/startup.tcl
|
STARTUP_TCL_SRCS += %D%/startup.tcl
|
||||||
|
|
||||||
|
@ -218,3 +220,4 @@ INTEL_IA32_SRC = \
|
||||||
%D%/arm_cti.h
|
%D%/arm_cti.h
|
||||||
|
|
||||||
include %D%/openrisc/Makefile.am
|
include %D%/openrisc/Makefile.am
|
||||||
|
include %D%/riscv/Makefile.am
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
noinst_LTLIBRARIES += %D%/libriscv.la
|
||||||
|
%C%_libriscv_la_SOURCES = \
|
||||||
|
%D%/asm.h \
|
||||||
|
%D%/batch.h \
|
||||||
|
%D%/debug_defines.h \
|
||||||
|
%D%/encoding.h \
|
||||||
|
%D%/gdb_regs.h \
|
||||||
|
%D%/opcodes.h \
|
||||||
|
%D%/program.h \
|
||||||
|
%D%/riscv.h \
|
||||||
|
%D%/batch.c \
|
||||||
|
%D%/program.c \
|
||||||
|
%D%/riscv-011.c \
|
||||||
|
%D%/riscv-013.c \
|
||||||
|
%D%/riscv.c \
|
||||||
|
%D%/riscv_semihosting.c
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef TARGET__RISCV__ASM_H
|
||||||
|
#define TARGET__RISCV__ASM_H
|
||||||
|
|
||||||
|
#include "riscv.h"
|
||||||
|
|
||||||
|
/*** Version-independent functions that we don't want in the main address space. ***/
|
||||||
|
|
||||||
|
static uint32_t load(const struct target *target, unsigned int rd,
|
||||||
|
unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t load(const struct target *target, unsigned int rd,
|
||||||
|
unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
switch (riscv_xlen(target)) {
|
||||||
|
case 32:
|
||||||
|
return lw(rd, base, offset);
|
||||||
|
case 64:
|
||||||
|
return ld(rd, base, offset);
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
return 0; /* Silence -Werror=return-type */
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t store(const struct target *target, unsigned int src,
|
||||||
|
unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t store(const struct target *target, unsigned int src,
|
||||||
|
unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
switch (riscv_xlen(target)) {
|
||||||
|
case 32:
|
||||||
|
return sw(src, base, offset);
|
||||||
|
case 64:
|
||||||
|
return sd(src, base, offset);
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
return 0; /* Silence -Werror=return-type */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,172 @@
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "batch.h"
|
||||||
|
#include "debug_defines.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
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));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void riscv_batch_free(struct riscv_batch *batch)
|
||||||
|
{
|
||||||
|
free(batch->data_in);
|
||||||
|
free(batch->data_out);
|
||||||
|
free(batch->fields);
|
||||||
|
free(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool riscv_batch_full(struct riscv_batch *batch)
|
||||||
|
{
|
||||||
|
return batch->used_scans > (batch->allocated_scans - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_batch_run(struct riscv_batch *batch)
|
||||||
|
{
|
||||||
|
if (batch->used_scans == 0) {
|
||||||
|
LOG_DEBUG("Ignoring empty batch.");
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
|
||||||
|
if (batch->idle_count > 0)
|
||||||
|
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);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data)
|
||||||
|
{
|
||||||
|
assert(batch->used_scans < batch->allocated_scans);
|
||||||
|
struct scan_field *field = batch->fields + batch->used_scans;
|
||||||
|
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
|
||||||
|
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
|
||||||
|
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
|
||||||
|
riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
|
||||||
|
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
|
||||||
|
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
|
||||||
|
batch->used_scans++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
|
||||||
|
{
|
||||||
|
assert(batch->used_scans < batch->allocated_scans);
|
||||||
|
struct scan_field *field = batch->fields + batch->used_scans;
|
||||||
|
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
|
||||||
|
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
|
||||||
|
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
|
||||||
|
riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
|
||||||
|
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
|
||||||
|
batch->last_scan = RISCV_SCAN_TYPE_READ;
|
||||||
|
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. */
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key)
|
||||||
|
{
|
||||||
|
assert(key < batch->read_keys_used);
|
||||||
|
size_t index = batch->read_keys[key];
|
||||||
|
assert(index <= batch->used_scans);
|
||||||
|
uint8_t *base = batch->data_in + 8 * index;
|
||||||
|
return base[0] |
|
||||||
|
((uint64_t) base[1]) << 8 |
|
||||||
|
((uint64_t) base[2]) << 16 |
|
||||||
|
((uint64_t) base[3]) << 24 |
|
||||||
|
((uint64_t) base[4]) << 32 |
|
||||||
|
((uint64_t) base[5]) << 40 |
|
||||||
|
((uint64_t) base[6]) << 48 |
|
||||||
|
((uint64_t) base[7]) << 56;
|
||||||
|
}
|
||||||
|
|
||||||
|
void riscv_batch_add_nop(struct riscv_batch *batch)
|
||||||
|
{
|
||||||
|
assert(batch->used_scans < batch->allocated_scans);
|
||||||
|
struct scan_field *field = batch->fields + batch->used_scans;
|
||||||
|
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
|
||||||
|
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
|
||||||
|
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
|
||||||
|
riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
static const char * const op_string[] = {"-", "r", "w", "?"};
|
||||||
|
static const char * const status_string[] = {"+", "?", "F", "b"};
|
||||||
|
|
||||||
|
if (debug_level < LOG_LVL_DEBUG)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(field->out_value != NULL);
|
||||||
|
uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
|
||||||
|
unsigned int out_op = get_field(out, DTM_DMI_OP);
|
||||||
|
unsigned int out_data = get_field(out, DTM_DMI_DATA);
|
||||||
|
unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
|
||||||
|
|
||||||
|
if (field->in_value) {
|
||||||
|
uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
|
||||||
|
unsigned int in_op = get_field(in, DTM_DMI_OP);
|
||||||
|
unsigned int in_data = get_field(in, DTM_DMI_DATA);
|
||||||
|
unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
|
||||||
|
|
||||||
|
log_printf_lf(LOG_LVL_DEBUG,
|
||||||
|
__FILE__, __LINE__, __PRETTY_FUNCTION__,
|
||||||
|
"%db %s %08x @%02x -> %s %08x @%02x",
|
||||||
|
field->num_bits,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
#ifndef TARGET__RISCV__SCANS_H
|
||||||
|
#define TARGET__RISCV__SCANS_H
|
||||||
|
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "jtag/jtag.h"
|
||||||
|
|
||||||
|
enum riscv_scan_type {
|
||||||
|
RISCV_SCAN_TYPE_INVALID,
|
||||||
|
RISCV_SCAN_TYPE_NOP,
|
||||||
|
RISCV_SCAN_TYPE_READ,
|
||||||
|
RISCV_SCAN_TYPE_WRITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A batch of multiple JTAG scans, which are grouped together to avoid the
|
||||||
|
* overhead of some JTAG adapters when sending single commands. This is
|
||||||
|
* designed to support block copies, as that's what we actually need to go
|
||||||
|
* fast. */
|
||||||
|
struct riscv_batch {
|
||||||
|
struct target *target;
|
||||||
|
|
||||||
|
size_t allocated_scans;
|
||||||
|
size_t used_scans;
|
||||||
|
|
||||||
|
size_t idle_count;
|
||||||
|
|
||||||
|
uint8_t *data_out;
|
||||||
|
uint8_t *data_in;
|
||||||
|
struct scan_field *fields;
|
||||||
|
|
||||||
|
/* In JTAG we scan out the previous value's output when performing a
|
||||||
|
* scan. This is a pain for users, so we just provide them the
|
||||||
|
* illusion of not having to do this by eliding all but the last NOP.
|
||||||
|
* */
|
||||||
|
enum riscv_scan_type last_scan;
|
||||||
|
|
||||||
|
/* The read keys. */
|
||||||
|
size_t *read_keys;
|
||||||
|
size_t read_keys_used;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
|
||||||
|
* scans that can be issued to this object, and idle is the number of JTAG idle
|
||||||
|
* cycles between every real scan. */
|
||||||
|
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle);
|
||||||
|
void riscv_batch_free(struct riscv_batch *batch);
|
||||||
|
|
||||||
|
/* Checks to see if this batch is full. */
|
||||||
|
bool riscv_batch_full(struct riscv_batch *batch);
|
||||||
|
|
||||||
|
/* Executes this scan batch. */
|
||||||
|
int riscv_batch_run(struct riscv_batch *batch);
|
||||||
|
|
||||||
|
/* Adds a DMI write to this batch. */
|
||||||
|
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data);
|
||||||
|
|
||||||
|
/* DMI reads must be handled in two parts: the first one schedules a read and
|
||||||
|
* provides a key, the second one actually obtains the value of that read .*/
|
||||||
|
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address);
|
||||||
|
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key);
|
||||||
|
|
||||||
|
/* Scans in a NOP. */
|
||||||
|
void riscv_batch_add_nop(struct riscv_batch *batch);
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef TARGET__RISCV__GDB_REGS_H
|
||||||
|
#define TARGET__RISCV__GDB_REGS_H
|
||||||
|
|
||||||
|
/* gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in
|
||||||
|
* its source tree. We must interpret the numbers the same here. */
|
||||||
|
enum gdb_regno {
|
||||||
|
GDB_REGNO_ZERO = 0, /* Read-only register, always 0. */
|
||||||
|
GDB_REGNO_RA = 1, /* Return Address. */
|
||||||
|
GDB_REGNO_SP = 2, /* Stack Pointer. */
|
||||||
|
GDB_REGNO_GP = 3, /* Global Pointer. */
|
||||||
|
GDB_REGNO_TP = 4, /* Thread Pointer. */
|
||||||
|
GDB_REGNO_T0,
|
||||||
|
GDB_REGNO_T1,
|
||||||
|
GDB_REGNO_T2,
|
||||||
|
GDB_REGNO_S0 = 8,
|
||||||
|
GDB_REGNO_FP = 8, /* Frame Pointer. */
|
||||||
|
GDB_REGNO_S1,
|
||||||
|
GDB_REGNO_A0 = 10, /* First argument. */
|
||||||
|
GDB_REGNO_A1 = 11, /* Second argument. */
|
||||||
|
GDB_REGNO_A2,
|
||||||
|
GDB_REGNO_A3,
|
||||||
|
GDB_REGNO_A4,
|
||||||
|
GDB_REGNO_A5,
|
||||||
|
GDB_REGNO_A6,
|
||||||
|
GDB_REGNO_A7,
|
||||||
|
GDB_REGNO_S2,
|
||||||
|
GDB_REGNO_S3,
|
||||||
|
GDB_REGNO_S4,
|
||||||
|
GDB_REGNO_S5,
|
||||||
|
GDB_REGNO_S6,
|
||||||
|
GDB_REGNO_S7,
|
||||||
|
GDB_REGNO_S8,
|
||||||
|
GDB_REGNO_S9,
|
||||||
|
GDB_REGNO_S10,
|
||||||
|
GDB_REGNO_S11,
|
||||||
|
GDB_REGNO_T3,
|
||||||
|
GDB_REGNO_T4,
|
||||||
|
GDB_REGNO_T5,
|
||||||
|
GDB_REGNO_T6,
|
||||||
|
GDB_REGNO_XPR31 = GDB_REGNO_T6,
|
||||||
|
|
||||||
|
GDB_REGNO_PC = 32,
|
||||||
|
GDB_REGNO_FPR0 = 33,
|
||||||
|
GDB_REGNO_FT0 = GDB_REGNO_FPR0,
|
||||||
|
GDB_REGNO_FT1,
|
||||||
|
GDB_REGNO_FT2,
|
||||||
|
GDB_REGNO_FT3,
|
||||||
|
GDB_REGNO_FT4,
|
||||||
|
GDB_REGNO_FT5,
|
||||||
|
GDB_REGNO_FT6,
|
||||||
|
GDB_REGNO_FT7,
|
||||||
|
GDB_REGNO_FS0,
|
||||||
|
GDB_REGNO_FS1,
|
||||||
|
GDB_REGNO_FA0,
|
||||||
|
GDB_REGNO_FA1,
|
||||||
|
GDB_REGNO_FA2,
|
||||||
|
GDB_REGNO_FA3,
|
||||||
|
GDB_REGNO_FA4,
|
||||||
|
GDB_REGNO_FA5,
|
||||||
|
GDB_REGNO_FA6,
|
||||||
|
GDB_REGNO_FA7,
|
||||||
|
GDB_REGNO_FS2,
|
||||||
|
GDB_REGNO_FS3,
|
||||||
|
GDB_REGNO_FS4,
|
||||||
|
GDB_REGNO_FS5,
|
||||||
|
GDB_REGNO_FS6,
|
||||||
|
GDB_REGNO_FS7,
|
||||||
|
GDB_REGNO_FS8,
|
||||||
|
GDB_REGNO_FS9,
|
||||||
|
GDB_REGNO_FS10,
|
||||||
|
GDB_REGNO_FS11,
|
||||||
|
GDB_REGNO_FT8,
|
||||||
|
GDB_REGNO_FT9,
|
||||||
|
GDB_REGNO_FT10,
|
||||||
|
GDB_REGNO_FT11,
|
||||||
|
GDB_REGNO_FPR31 = GDB_REGNO_FT11,
|
||||||
|
GDB_REGNO_CSR0 = 65,
|
||||||
|
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_DSCRATCH = CSR_DSCRATCH + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_MSTATUS = CSR_MSTATUS + GDB_REGNO_CSR0,
|
||||||
|
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
|
||||||
|
GDB_REGNO_PRIV = 4161,
|
||||||
|
GDB_REGNO_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *gdb_regno_name(enum gdb_regno regno);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,310 @@
|
||||||
|
#include "encoding.h"
|
||||||
|
|
||||||
|
#define ZERO 0
|
||||||
|
#define T0 5
|
||||||
|
#define S0 8
|
||||||
|
#define S1 9
|
||||||
|
|
||||||
|
static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo)
|
||||||
|
{
|
||||||
|
return (value >> lo) & ((1 << (hi+1-lo)) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bit(uint32_t value, unsigned int b)
|
||||||
|
{
|
||||||
|
return (value >> b) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t jal(unsigned int rd, uint32_t imm)
|
||||||
|
{
|
||||||
|
return (bit(imm, 20) << 31) |
|
||||||
|
(bits(imm, 10, 1) << 21) |
|
||||||
|
(bit(imm, 11) << 20) |
|
||||||
|
(bits(imm, 19, 12) << 12) |
|
||||||
|
(rd << 7) |
|
||||||
|
MATCH_JAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t csrsi(unsigned int csr, uint16_t imm)
|
||||||
|
{
|
||||||
|
return (csr << 20) |
|
||||||
|
(bits(imm, 4, 0) << 15) |
|
||||||
|
MATCH_CSRRSI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(src << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_SW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(src << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_SD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(src << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_SH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(src << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_SB;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(rd, 4, 0) << 7) |
|
||||||
|
MATCH_LD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(rd, 4, 0) << 7) |
|
||||||
|
MATCH_LW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(rd, 4, 0) << 7) |
|
||||||
|
MATCH_LH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(rd, 4, 0) << 7) |
|
||||||
|
MATCH_LB;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused));
|
||||||
|
static uint32_t csrw(unsigned int source, unsigned int csr)
|
||||||
|
{
|
||||||
|
return (csr << 20) | (source << 15) | MATCH_CSRRW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm)
|
||||||
|
{
|
||||||
|
return (bits(imm, 11, 0) << 20) |
|
||||||
|
(src << 15) |
|
||||||
|
(dest << 7) |
|
||||||
|
MATCH_ADDI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused));
|
||||||
|
static uint32_t csrr(unsigned int rd, unsigned int csr)
|
||||||
|
{
|
||||||
|
return (csr << 20) | (rd << 7) | MATCH_CSRRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
|
||||||
|
static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr)
|
||||||
|
{
|
||||||
|
return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
|
||||||
|
static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr)
|
||||||
|
{
|
||||||
|
return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(bits(src, 4, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_FSW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(bits(src, 4, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_FSD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(dest, 4, 0) << 7) |
|
||||||
|
MATCH_FLW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(dest, 4, 0) << 7) |
|
||||||
|
MATCH_FLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fmv_x_w(unsigned dest, unsigned src) __attribute__ ((unused));
|
||||||
|
static uint32_t fmv_x_w(unsigned dest, unsigned src)
|
||||||
|
{
|
||||||
|
return src << 15 |
|
||||||
|
dest << 7 |
|
||||||
|
MATCH_FMV_X_W;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fmv_x_d(unsigned dest, unsigned src) __attribute__ ((unused));
|
||||||
|
static uint32_t fmv_x_d(unsigned dest, unsigned src)
|
||||||
|
{
|
||||||
|
return src << 15 |
|
||||||
|
dest << 7 |
|
||||||
|
MATCH_FMV_X_D;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fmv_w_x(unsigned dest, unsigned src) __attribute__ ((unused));
|
||||||
|
static uint32_t fmv_w_x(unsigned dest, unsigned src)
|
||||||
|
{
|
||||||
|
return src << 15 |
|
||||||
|
dest << 7 |
|
||||||
|
MATCH_FMV_W_X;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fmv_d_x(unsigned dest, unsigned src) __attribute__ ((unused));
|
||||||
|
static uint32_t fmv_d_x(unsigned dest, unsigned src)
|
||||||
|
{
|
||||||
|
return src << 15 |
|
||||||
|
dest << 7 |
|
||||||
|
MATCH_FMV_D_X;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t ebreak(void) __attribute__ ((unused));
|
||||||
|
static uint32_t ebreak(void)
|
||||||
|
{
|
||||||
|
return MATCH_EBREAK;
|
||||||
|
}
|
||||||
|
static uint32_t ebreak_c(void) __attribute__ ((unused));
|
||||||
|
static uint32_t ebreak_c(void)
|
||||||
|
{
|
||||||
|
return MATCH_C_EBREAK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fence_i(void) __attribute__ ((unused));
|
||||||
|
static uint32_t fence_i(void)
|
||||||
|
{
|
||||||
|
return MATCH_FENCE_I;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t lui(unsigned int dest, uint32_t imm)
|
||||||
|
{
|
||||||
|
return (bits(imm, 19, 0) << 12) |
|
||||||
|
(dest << 7) |
|
||||||
|
MATCH_LUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t csrci(unsigned int csr, uint16_t imm)
|
||||||
|
{
|
||||||
|
return (csr << 20) |
|
||||||
|
(bits(imm, 4, 0) << 15) |
|
||||||
|
MATCH_CSRRCI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t li(unsigned int dest, uint16_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t li(unsigned int dest, uint16_t imm)
|
||||||
|
{
|
||||||
|
return addi(dest, 0, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
|
||||||
|
static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
|
||||||
|
{
|
||||||
|
return (bits(offset, 11, 5) << 25) |
|
||||||
|
(bits(src, 4, 0) << 20) |
|
||||||
|
(base << 15) |
|
||||||
|
(bits(offset, 4, 0) << 7) |
|
||||||
|
MATCH_FSD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm)
|
||||||
|
{
|
||||||
|
return (bits(imm, 11, 0) << 20) |
|
||||||
|
(src << 15) |
|
||||||
|
(dest << 7) |
|
||||||
|
MATCH_ORI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t nop(void) __attribute__ ((unused));
|
||||||
|
static uint32_t nop(void)
|
||||||
|
{
|
||||||
|
return addi(0, 0, 0);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
|
||||||
|
static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm)
|
||||||
|
{
|
||||||
|
return (bits(imm, 11, 0) << 20) |
|
||||||
|
(src << 15) |
|
||||||
|
(dest << 7) |
|
||||||
|
MATCH_XORI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused));
|
||||||
|
static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
|
||||||
|
{
|
||||||
|
return (bits(shamt, 4, 0) << 20) |
|
||||||
|
(src << 15) |
|
||||||
|
(dest << 7) |
|
||||||
|
MATCH_SRLI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t fence(void) __attribute__((unused));
|
||||||
|
static uint32_t fence(void)
|
||||||
|
{
|
||||||
|
return MATCH_FENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t auipc(unsigned int dest) __attribute__((unused));
|
||||||
|
static uint32_t auipc(unsigned int dest)
|
||||||
|
{
|
||||||
|
return MATCH_AUIPC | (dest << 7);
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "target/register.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
#include "program.h"
|
||||||
|
#include "helper/log.h"
|
||||||
|
|
||||||
|
#include "asm.h"
|
||||||
|
#include "encoding.h"
|
||||||
|
|
||||||
|
/* Program interface. */
|
||||||
|
int riscv_program_init(struct riscv_program *p, struct target *target)
|
||||||
|
{
|
||||||
|
memset(p, 0, sizeof(*p));
|
||||||
|
p->target = target;
|
||||||
|
p->instruction_count = 0;
|
||||||
|
p->target_xlen = riscv_xlen(target);
|
||||||
|
for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i)
|
||||||
|
p->writes_xreg[i] = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i)
|
||||||
|
p->debug_buffer[i] = -1;
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_write(struct riscv_program *program)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < program->instruction_count; ++i) {
|
||||||
|
LOG_DEBUG("%p: debug_buffer[%02x] = DASM(0x%08x)", program, i, program->debug_buffer[i]);
|
||||||
|
if (riscv_write_debug_buffer(program->target, i,
|
||||||
|
program->debug_buffer[i]) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add ebreak and execute the program. */
|
||||||
|
int riscv_program_exec(struct riscv_program *p, struct target *t)
|
||||||
|
{
|
||||||
|
keep_alive();
|
||||||
|
|
||||||
|
riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
|
||||||
|
for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) {
|
||||||
|
if (p->writes_xreg[i]) {
|
||||||
|
LOG_DEBUG("Saving register %d as used by program", (int)i);
|
||||||
|
int result = riscv_get_register(t, &saved_registers[i], i);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (riscv_program_ebreak(p) != ERROR_OK) {
|
||||||
|
LOG_ERROR("Unable to write ebreak");
|
||||||
|
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
|
||||||
|
LOG_ERROR("ram[%02x]: DASM(0x%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (riscv_program_write(p) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (riscv_execute_debug_buffer(t) != ERROR_OK) {
|
||||||
|
LOG_DEBUG("Unable to execute program %p", p);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
|
||||||
|
if (i >= riscv_debug_buffer_size(p->target))
|
||||||
|
p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
|
||||||
|
|
||||||
|
for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i)
|
||||||
|
if (p->writes_xreg[i])
|
||||||
|
riscv_set_register(t, i, saved_registers[i]);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, sw(d, b, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, sh(d, b, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, sb(d, b, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, lw(d, b, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, lh(d, b, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, lb(d, b, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr)
|
||||||
|
{
|
||||||
|
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
|
||||||
|
return riscv_program_insert(p, csrrs(d, GDB_REGNO_ZERO, csr - GDB_REGNO_CSR0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr)
|
||||||
|
{
|
||||||
|
assert(csr >= GDB_REGNO_CSR0);
|
||||||
|
return riscv_program_insert(p, csrrw(GDB_REGNO_ZERO, s, csr - GDB_REGNO_CSR0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_fence_i(struct riscv_program *p)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, fence_i());
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_fence(struct riscv_program *p)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, fence());
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_ebreak(struct riscv_program *p)
|
||||||
|
{
|
||||||
|
struct target *target = p->target;
|
||||||
|
RISCV_INFO(r);
|
||||||
|
if (p->instruction_count == riscv_debug_buffer_size(p->target) &&
|
||||||
|
r->impebreak) {
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
return riscv_program_insert(p, ebreak());
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t u)
|
||||||
|
{
|
||||||
|
return riscv_program_insert(p, addi(d, s, u));
|
||||||
|
}
|
||||||
|
|
||||||
|
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
|
||||||
|
{
|
||||||
|
if (p->instruction_count >= riscv_debug_buffer_size(p->target)) {
|
||||||
|
LOG_ERROR("Unable to insert instruction:");
|
||||||
|
LOG_ERROR(" instruction_count=%d", (int)p->instruction_count);
|
||||||
|
LOG_ERROR(" buffer size =%d", (int)riscv_debug_buffer_size(p->target));
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->debug_buffer[p->instruction_count] = i;
|
||||||
|
p->instruction_count++;
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef TARGET__RISCV__PROGRAM_H
|
||||||
|
#define TARGET__RISCV__PROGRAM_H
|
||||||
|
|
||||||
|
#include "riscv.h"
|
||||||
|
|
||||||
|
#define RISCV_MAX_DEBUG_BUFFER_SIZE 32
|
||||||
|
#define RISCV_REGISTER_COUNT 32
|
||||||
|
#define RISCV_DSCRATCH_COUNT 2
|
||||||
|
|
||||||
|
/* The various RISC-V debug specifications all revolve around setting up
|
||||||
|
* program buffers and executing them on the target. This structure contains a
|
||||||
|
* single program, which can then be executed on targets. */
|
||||||
|
struct riscv_program {
|
||||||
|
struct target *target;
|
||||||
|
|
||||||
|
uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/* Number of 32-bit instructions in the program. */
|
||||||
|
size_t instruction_count;
|
||||||
|
|
||||||
|
/* Side effects of executing this program. These must be accounted for
|
||||||
|
* in order to maintain correct executing of the target system. */
|
||||||
|
bool writes_xreg[RISCV_REGISTER_COUNT];
|
||||||
|
|
||||||
|
/* XLEN on the target. */
|
||||||
|
int target_xlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initializes a program with the header. */
|
||||||
|
int riscv_program_init(struct riscv_program *p, struct target *t);
|
||||||
|
|
||||||
|
/* Write the program to the program buffer. */
|
||||||
|
int riscv_program_write(struct riscv_program *program);
|
||||||
|
|
||||||
|
/* Executes a program, returning 0 if the program successfully executed. Note
|
||||||
|
* that this may cause registers to be saved or restored, which could result to
|
||||||
|
* calls to things like riscv_save_register which itself could require a
|
||||||
|
* program to execute. That's OK, just make sure this eventually terminates.
|
||||||
|
* */
|
||||||
|
int riscv_program_exec(struct riscv_program *p, struct target *t);
|
||||||
|
int riscv_program_load(struct riscv_program *p, struct target *t);
|
||||||
|
|
||||||
|
/* Clears a program, removing all the state associated with it. */
|
||||||
|
int riscv_program_clear(struct riscv_program *p, struct target *t);
|
||||||
|
|
||||||
|
/* A lower level interface, you shouldn't use this unless you have a reason. */
|
||||||
|
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
|
||||||
|
|
||||||
|
/* There is hardware support for saving at least one register. This register
|
||||||
|
* doesn't need to be saved/restored the usual way, which is useful during
|
||||||
|
* early initialization when we can't save/restore arbitrary registerrs to host
|
||||||
|
* 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
|
||||||
|
* 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);
|
||||||
|
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
|
||||||
|
|
||||||
|
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
|
||||||
|
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
|
||||||
|
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
|
||||||
|
|
||||||
|
int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr);
|
||||||
|
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
|
||||||
|
|
||||||
|
int riscv_program_fence_i(struct riscv_program *p);
|
||||||
|
int riscv_program_fence(struct riscv_program *p);
|
||||||
|
int riscv_program_ebreak(struct riscv_program *p);
|
||||||
|
|
||||||
|
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,262 @@
|
||||||
|
#ifndef RISCV_H
|
||||||
|
#define RISCV_H
|
||||||
|
|
||||||
|
struct riscv_program;
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "opcodes.h"
|
||||||
|
#include "gdb_regs.h"
|
||||||
|
|
||||||
|
/* The register cache is statically allocated. */
|
||||||
|
#define RISCV_MAX_HARTS 32
|
||||||
|
#define RISCV_MAX_REGISTERS 5000
|
||||||
|
#define RISCV_MAX_TRIGGERS 32
|
||||||
|
#define RISCV_MAX_HWBPS 16
|
||||||
|
|
||||||
|
#define DEFAULT_COMMAND_TIMEOUT_SEC 2
|
||||||
|
#define DEFAULT_RESET_TIMEOUT_SEC 30
|
||||||
|
|
||||||
|
extern struct target_type riscv011_target;
|
||||||
|
extern struct target_type riscv013_target;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions shared by code supporting all RISC-V versions.
|
||||||
|
*/
|
||||||
|
typedef uint64_t riscv_reg_t;
|
||||||
|
typedef uint32_t riscv_insn_t;
|
||||||
|
typedef uint64_t riscv_addr_t;
|
||||||
|
|
||||||
|
enum riscv_halt_reason {
|
||||||
|
RISCV_HALT_INTERRUPT,
|
||||||
|
RISCV_HALT_BREAKPOINT,
|
||||||
|
RISCV_HALT_SINGLESTEP,
|
||||||
|
RISCV_HALT_TRIGGER,
|
||||||
|
RISCV_HALT_UNKNOWN,
|
||||||
|
RISCV_HALT_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned dtm_version;
|
||||||
|
|
||||||
|
struct command_context *cmd_ctx;
|
||||||
|
void *version_specific;
|
||||||
|
|
||||||
|
/* The number of harts on this system. */
|
||||||
|
int hart_count;
|
||||||
|
|
||||||
|
/* The hart that the RTOS thinks is currently being debugged. */
|
||||||
|
int rtos_hartid;
|
||||||
|
|
||||||
|
/* The hart that is currently being debugged. Note that this is
|
||||||
|
* different than the hartid that the RTOS is expected to use. This
|
||||||
|
* one will change all the time, it's more of a global argument to
|
||||||
|
* every function than an actual */
|
||||||
|
int current_hartid;
|
||||||
|
|
||||||
|
/* Enough space to store all the registers we might need to save. */
|
||||||
|
/* FIXME: This should probably be a bunch of register caches. */
|
||||||
|
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
|
||||||
|
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
|
||||||
|
|
||||||
|
/* OpenOCD's register cache points into here. This is not per-hart because
|
||||||
|
* we just invalidate the entire cache when we change which hart is
|
||||||
|
* selected. */
|
||||||
|
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
|
||||||
|
|
||||||
|
/* Single buffer that contains all register names, instead of calling
|
||||||
|
* malloc for each register. Needs to be freed when reg_list is freed. */
|
||||||
|
char *reg_names;
|
||||||
|
|
||||||
|
/* It's possible that each core has a different supported ISA set. */
|
||||||
|
int xlen[RISCV_MAX_HARTS];
|
||||||
|
riscv_reg_t misa[RISCV_MAX_HARTS];
|
||||||
|
|
||||||
|
/* The number of triggers per hart. */
|
||||||
|
unsigned trigger_count[RISCV_MAX_HARTS];
|
||||||
|
|
||||||
|
/* For each physical trigger, contains -1 if the hwbp is available, or the
|
||||||
|
* unique_id of the breakpoint/watchpoint that is using it.
|
||||||
|
* Note that in RTOS mode the triggers are the same across all harts the
|
||||||
|
* target controls, while otherwise only a single hart is controlled. */
|
||||||
|
int trigger_unique_id[RISCV_MAX_HWBPS];
|
||||||
|
|
||||||
|
/* The number of entries in the debug buffer. */
|
||||||
|
int debug_buffer_size[RISCV_MAX_HARTS];
|
||||||
|
|
||||||
|
/* This avoids invalidating the register cache too often. */
|
||||||
|
bool registers_initialized;
|
||||||
|
|
||||||
|
/* This hart contains an implicit ebreak at the end of the program buffer. */
|
||||||
|
bool impebreak;
|
||||||
|
|
||||||
|
bool triggers_enumerated;
|
||||||
|
|
||||||
|
/* Helper functions that target the various RISC-V debug spec
|
||||||
|
* implementations. */
|
||||||
|
int (*get_register)(struct target *target,
|
||||||
|
riscv_reg_t *value, int hid, int rid);
|
||||||
|
int (*set_register)(struct target *, int hartid, int regid,
|
||||||
|
uint64_t value);
|
||||||
|
int (*select_current_hart)(struct target *);
|
||||||
|
bool (*is_halted)(struct target *target);
|
||||||
|
int (*halt_current_hart)(struct target *);
|
||||||
|
int (*resume_current_hart)(struct target *target);
|
||||||
|
int (*step_current_hart)(struct target *target);
|
||||||
|
int (*on_halt)(struct target *target);
|
||||||
|
int (*on_resume)(struct target *target);
|
||||||
|
int (*on_step)(struct target *target);
|
||||||
|
enum riscv_halt_reason (*halt_reason)(struct target *target);
|
||||||
|
int (*write_debug_buffer)(struct target *target, unsigned index,
|
||||||
|
riscv_insn_t d);
|
||||||
|
riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned index);
|
||||||
|
int (*execute_debug_buffer)(struct target *target);
|
||||||
|
int (*dmi_write_u64_bits)(struct target *target);
|
||||||
|
void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
|
||||||
|
void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
|
||||||
|
void (*fill_dmi_nop_u64)(struct target *target, char *buf);
|
||||||
|
|
||||||
|
int (*authdata_read)(struct target *target, uint32_t *value);
|
||||||
|
int (*authdata_write)(struct target *target, uint32_t value);
|
||||||
|
|
||||||
|
int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
|
||||||
|
int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
|
||||||
|
} riscv_info_t;
|
||||||
|
|
||||||
|
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
|
||||||
|
extern int riscv_command_timeout_sec;
|
||||||
|
|
||||||
|
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
|
||||||
|
extern int riscv_reset_timeout_sec;
|
||||||
|
|
||||||
|
extern bool riscv_prefer_sba;
|
||||||
|
|
||||||
|
/* Everything needs the RISC-V specific info structure, so here's a nice macro
|
||||||
|
* that provides that. */
|
||||||
|
static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused));
|
||||||
|
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 struct scan_field select_dtmcontrol;
|
||||||
|
extern uint8_t ir_dbus[1];
|
||||||
|
extern struct scan_field select_dbus;
|
||||||
|
extern uint8_t ir_idcode[1];
|
||||||
|
extern struct scan_field select_idcode;
|
||||||
|
|
||||||
|
/*** OpenOCD Interface */
|
||||||
|
int riscv_openocd_poll(struct target *target);
|
||||||
|
|
||||||
|
int riscv_openocd_halt(struct target *target);
|
||||||
|
|
||||||
|
int riscv_openocd_resume(
|
||||||
|
struct target *target,
|
||||||
|
int current,
|
||||||
|
target_addr_t address,
|
||||||
|
int handle_breakpoints,
|
||||||
|
int debug_execution
|
||||||
|
);
|
||||||
|
|
||||||
|
int riscv_openocd_step(
|
||||||
|
struct target *target,
|
||||||
|
int current,
|
||||||
|
target_addr_t address,
|
||||||
|
int handle_breakpoints
|
||||||
|
);
|
||||||
|
|
||||||
|
int riscv_openocd_assert_reset(struct target *target);
|
||||||
|
int riscv_openocd_deassert_reset(struct target *target);
|
||||||
|
|
||||||
|
/*** RISC-V Interface ***/
|
||||||
|
|
||||||
|
/* Initializes the shared RISC-V structure. */
|
||||||
|
void riscv_info_init(struct target *target, riscv_info_t *r);
|
||||||
|
|
||||||
|
/* Run control, possibly for multiple harts. The _all_harts versions resume
|
||||||
|
* all the enabled harts, which when running in RTOS mode is all the harts on
|
||||||
|
* the system. */
|
||||||
|
int riscv_halt_all_harts(struct target *target);
|
||||||
|
int riscv_halt_one_hart(struct target *target, int hartid);
|
||||||
|
int riscv_resume_all_harts(struct target *target);
|
||||||
|
int riscv_resume_one_hart(struct target *target, int hartid);
|
||||||
|
|
||||||
|
/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
|
||||||
|
* then the only hart. */
|
||||||
|
int riscv_step_rtos_hart(struct target *target);
|
||||||
|
|
||||||
|
bool riscv_supports_extension(struct target *target, int hartid, char letter);
|
||||||
|
|
||||||
|
/* Returns XLEN for the given (or current) hart. */
|
||||||
|
int riscv_xlen(const struct target *target);
|
||||||
|
int riscv_xlen_of_hart(const struct target *target, int hartid);
|
||||||
|
|
||||||
|
bool riscv_rtos_enabled(const struct target *target);
|
||||||
|
|
||||||
|
/* Sets the current hart, which is the hart that will actually be used when
|
||||||
|
* issuing debug commands. */
|
||||||
|
int riscv_set_current_hartid(struct target *target, int hartid);
|
||||||
|
int riscv_current_hartid(const struct target *target);
|
||||||
|
|
||||||
|
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
|
||||||
|
* without requiring multiple targets. */
|
||||||
|
|
||||||
|
/* When using the RTOS to debug, this selects the hart that is currently being
|
||||||
|
* debugged. This doesn't propogate to the hardware. */
|
||||||
|
void riscv_set_all_rtos_harts(struct target *target);
|
||||||
|
void riscv_set_rtos_hartid(struct target *target, int hartid);
|
||||||
|
|
||||||
|
/* Lists the number of harts in the system, which are assumed to be
|
||||||
|
* concecutive and start with mhartid=0. */
|
||||||
|
int riscv_count_harts(struct target *target);
|
||||||
|
|
||||||
|
/* Returns TRUE if the target has the given register on the given hart. */
|
||||||
|
bool riscv_has_register(struct target *target, int hartid, int regid);
|
||||||
|
|
||||||
|
/* Returns the value of the given register on the given hart. 32-bit registers
|
||||||
|
* are zero extended to 64 bits. */
|
||||||
|
int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
|
||||||
|
int riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
|
||||||
|
int riscv_get_register(struct target *target, riscv_reg_t *value,
|
||||||
|
enum gdb_regno r);
|
||||||
|
int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
|
||||||
|
int hartid, enum gdb_regno regid);
|
||||||
|
|
||||||
|
/* Checks the state of the current hart -- "is_halted" checks the actual
|
||||||
|
* on-device register. */
|
||||||
|
bool riscv_is_halted(struct target *target);
|
||||||
|
enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
|
||||||
|
|
||||||
|
/* These helper functions let the generic program interface get target-specific
|
||||||
|
* information. */
|
||||||
|
size_t riscv_debug_buffer_size(struct target *target);
|
||||||
|
|
||||||
|
riscv_insn_t riscv_read_debug_buffer(struct target *target, int index);
|
||||||
|
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn);
|
||||||
|
int riscv_execute_debug_buffer(struct target *target);
|
||||||
|
|
||||||
|
void riscv_fill_dmi_nop_u64(struct target *target, char *buf);
|
||||||
|
void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
|
||||||
|
void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a);
|
||||||
|
int riscv_dmi_write_u64_bits(struct target *target);
|
||||||
|
|
||||||
|
/* Invalidates the register cache. */
|
||||||
|
void riscv_invalidate_register_cache(struct target *target);
|
||||||
|
|
||||||
|
/* Returns TRUE when a hart is enabled in this target. */
|
||||||
|
bool riscv_hart_enabled(struct target *target, int hartid);
|
||||||
|
|
||||||
|
int riscv_enumerate_triggers(struct target *target);
|
||||||
|
|
||||||
|
int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint);
|
||||||
|
int riscv_remove_breakpoint(struct target *target,
|
||||||
|
struct breakpoint *breakpoint);
|
||||||
|
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
|
||||||
|
int riscv_remove_watchpoint(struct target *target,
|
||||||
|
struct watchpoint *watchpoint);
|
||||||
|
|
||||||
|
int riscv_init_registers(struct target *target);
|
||||||
|
|
||||||
|
void riscv_semihosting_init(struct target *target);
|
||||||
|
int riscv_semihosting(struct target *target, int *retval);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,194 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2018 by Liviu Ionescu *
|
||||||
|
* ilg@livius.net *
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2009 by Marvell Technology Group Ltd. *
|
||||||
|
* Written by Nicolas Pitre <nico@marvell.com> *
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2010 by Spencer Oliver *
|
||||||
|
* spen@spen-soft.co.uk *
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2016 by Square, Inc. *
|
||||||
|
* Steven Stallion <stallion@squareup.com> *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Hold RISC-V semihosting support.
|
||||||
|
*
|
||||||
|
* The RISC-V code is inspired from ARM semihosting.
|
||||||
|
*
|
||||||
|
* Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
|
||||||
|
* from ARM Ltd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include "target/target.h"
|
||||||
|
#include "target/semihosting_common.h"
|
||||||
|
#include "riscv.h"
|
||||||
|
|
||||||
|
static int riscv_semihosting_setup(struct target *target, int enable);
|
||||||
|
static int riscv_semihosting_post_result(struct target *target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize RISC-V semihosting. Use common ARM code.
|
||||||
|
*/
|
||||||
|
void riscv_semihosting_init(struct target *target)
|
||||||
|
{
|
||||||
|
semihosting_common_init(target, riscv_semihosting_setup,
|
||||||
|
riscv_semihosting_post_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for and process a semihosting request using the ARM protocol). This
|
||||||
|
* is meant to be called when the target is stopped due to a debug mode entry.
|
||||||
|
* If the value 0 is returned then there was nothing to process. A non-zero
|
||||||
|
* return value signifies that a request was processed and the target resumed,
|
||||||
|
* or an error was encountered, in which case the caller must return
|
||||||
|
* immediately.
|
||||||
|
*
|
||||||
|
* @param target Pointer to the target to process.
|
||||||
|
* @param retval Pointer to a location where the return code will be stored
|
||||||
|
* @return non-zero value if a request was processed or an error encountered
|
||||||
|
*/
|
||||||
|
int riscv_semihosting(struct target *target, int *retval)
|
||||||
|
{
|
||||||
|
struct semihosting *semihosting = target->semihosting;
|
||||||
|
if (!semihosting)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!semihosting->is_active)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
riscv_reg_t dpc;
|
||||||
|
int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint8_t tmp[12];
|
||||||
|
|
||||||
|
/* Read the current instruction, including the bracketing */
|
||||||
|
*retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
|
||||||
|
if (*retval != ERROR_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The instructions that trigger a semihosting call,
|
||||||
|
* always uncompressed, should look like:
|
||||||
|
*
|
||||||
|
* 01f01013 slli zero,zero,0x1f
|
||||||
|
* 00100073 ebreak
|
||||||
|
* 40705013 srai zero,zero,0x7
|
||||||
|
*/
|
||||||
|
uint32_t pre = target_buffer_get_u32(target, tmp);
|
||||||
|
uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
|
||||||
|
uint32_t post = target_buffer_get_u32(target, tmp + 8);
|
||||||
|
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
|
||||||
|
|
||||||
|
if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
|
||||||
|
|
||||||
|
/* Not the magic sequence defining semihosting. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform semihosting call if we are not waiting on a fileio
|
||||||
|
* operation to complete.
|
||||||
|
*/
|
||||||
|
if (!semihosting->hit_fileio) {
|
||||||
|
|
||||||
|
/* RISC-V uses A0 and A1 to pass function arguments */
|
||||||
|
riscv_reg_t r0;
|
||||||
|
riscv_reg_t r1;
|
||||||
|
|
||||||
|
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
semihosting->op = r0;
|
||||||
|
semihosting->param = r1;
|
||||||
|
semihosting->word_size_bytes = riscv_xlen(target) / 8;
|
||||||
|
|
||||||
|
/* Check for ARM operation numbers. */
|
||||||
|
if (0 <= semihosting->op && semihosting->op <= 0x31) {
|
||||||
|
*retval = semihosting_common(target);
|
||||||
|
if (*retval != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed semihosting operation");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Unknown operation number, not a semihosting call. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resume target if we are not waiting on a fileio
|
||||||
|
* operation to complete.
|
||||||
|
*/
|
||||||
|
if (semihosting->is_resumable && !semihosting->hit_fileio) {
|
||||||
|
/* Resume right after the EBREAK 4 bytes instruction. */
|
||||||
|
*retval = target_resume(target, 0, dpc+4, 0, 0);
|
||||||
|
if (*retval != ERROR_OK) {
|
||||||
|
LOG_ERROR("Failed to resume target");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Local functions. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called via semihosting->setup() later, after the target is known,
|
||||||
|
* usually on the first semihosting command.
|
||||||
|
*/
|
||||||
|
static int riscv_semihosting_setup(struct target *target, int enable)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("enable=%d", enable);
|
||||||
|
|
||||||
|
struct semihosting *semihosting = target->semihosting;
|
||||||
|
if (semihosting)
|
||||||
|
semihosting->setup_time = clock();
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int riscv_semihosting_post_result(struct target *target)
|
||||||
|
{
|
||||||
|
struct semihosting *semihosting = target->semihosting;
|
||||||
|
if (!semihosting) {
|
||||||
|
/* If not enabled, silently ignored. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("0x%" PRIx64, semihosting->result);
|
||||||
|
riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -107,6 +107,7 @@ extern struct target_type or1k_target;
|
||||||
extern struct target_type quark_x10xx_target;
|
extern struct target_type quark_x10xx_target;
|
||||||
extern struct target_type quark_d20xx_target;
|
extern struct target_type quark_d20xx_target;
|
||||||
extern struct target_type stm8_target;
|
extern struct target_type stm8_target;
|
||||||
|
extern struct target_type riscv_target;
|
||||||
|
|
||||||
static struct target_type *target_types[] = {
|
static struct target_type *target_types[] = {
|
||||||
&arm7tdmi_target,
|
&arm7tdmi_target,
|
||||||
|
@ -139,6 +140,7 @@ static struct target_type *target_types[] = {
|
||||||
&quark_x10xx_target,
|
&quark_x10xx_target,
|
||||||
&quark_d20xx_target,
|
&quark_d20xx_target,
|
||||||
&stm8_target,
|
&stm8_target,
|
||||||
|
&riscv_target,
|
||||||
#if BUILD_TARGET64
|
#if BUILD_TARGET64
|
||||||
&aarch64_target,
|
&aarch64_target,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# Be sure you include the speed and interface before this file
|
||||||
|
# Example:
|
||||||
|
# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e31arty.cfg"
|
||||||
|
|
||||||
|
set _CHIPNAME riscv
|
||||||
|
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000001
|
||||||
|
|
||||||
|
set _TARGETNAME $_CHIPNAME.cpu
|
||||||
|
|
||||||
|
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
|
||||||
|
$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
|
||||||
|
|
||||||
|
flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
|
||||||
|
init
|
||||||
|
if {[ info exists pulse_srst]} {
|
||||||
|
ftdi_set_signal nSRST 0
|
||||||
|
ftdi_set_signal nSRST z
|
||||||
|
}
|
||||||
|
halt
|
||||||
|
flash protect 0 64 last off
|
||||||
|
echo "Ready for Remote Connections"
|
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# Be sure you include the speed and interface before this file
|
||||||
|
# Example:
|
||||||
|
# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e51arty.cfg"
|
||||||
|
|
||||||
|
set _CHIPNAME riscv
|
||||||
|
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000001
|
||||||
|
|
||||||
|
set _TARGETNAME $_CHIPNAME.cpu
|
||||||
|
|
||||||
|
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
|
||||||
|
$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
|
||||||
|
|
||||||
|
flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
|
||||||
|
init
|
||||||
|
if {[ info exists pulse_srst]} {
|
||||||
|
ftdi_set_signal nSRST 0
|
||||||
|
ftdi_set_signal nSRST z
|
||||||
|
}
|
||||||
|
halt
|
||||||
|
flash protect 0 64 last off
|
||||||
|
echo "Ready for Remote Connections"
|
|
@ -0,0 +1,34 @@
|
||||||
|
adapter_khz 10000
|
||||||
|
|
||||||
|
interface ftdi
|
||||||
|
ftdi_device_desc "Dual RS232-HS"
|
||||||
|
ftdi_vid_pid 0x0403 0x6010
|
||||||
|
|
||||||
|
ftdi_layout_init 0x0008 0x001b
|
||||||
|
ftdi_layout_signal nSRST -oe 0x0020 -data 0x0020
|
||||||
|
|
||||||
|
#Reset Stretcher logic on FE310 is ~1 second long
|
||||||
|
#This doesn't apply if you use
|
||||||
|
# ftdi_set_signal, but still good to document
|
||||||
|
#adapter_nsrst_delay 1500
|
||||||
|
|
||||||
|
set _CHIPNAME riscv
|
||||||
|
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
|
||||||
|
|
||||||
|
set _TARGETNAME $_CHIPNAME.cpu
|
||||||
|
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
|
||||||
|
$_TARGETNAME configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
|
||||||
|
|
||||||
|
flash bank onboard_spi_flash fespi 0x20000000 0 0 0 $_TARGETNAME
|
||||||
|
init
|
||||||
|
#reset -- This type of reset is not implemented yet
|
||||||
|
if {[ info exists pulse_srst]} {
|
||||||
|
ftdi_set_signal nSRST 0
|
||||||
|
ftdi_set_signal nSRST z
|
||||||
|
#Wait for the reset stretcher
|
||||||
|
#It will work without this, but
|
||||||
|
#will incur lots of delays for later commands.
|
||||||
|
sleep 1500
|
||||||
|
}
|
||||||
|
halt
|
||||||
|
flash protect 0 64 last off
|
Loading…
Reference in New Issue