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:
Tim Newsome
2018-07-18 13:34:23 -07:00
committed by Matthias Welwarsky
parent 9363705820
commit a51ab8ddf6
22 changed files with 12313 additions and 1 deletions
+78
View File
@@ -8946,6 +8946,84 @@ Display all registers in @emph{group}.
"timer" or any new group created with addreg command.
@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}
@section Software Debug Messages and Tracing
@cindex Linux-ARM DCC support
+2
View File
@@ -149,6 +149,8 @@ extern int debug_level;
*/
#define ERROR_FAIL (-4)
#define ERROR_WAIT (-5)
/* ERROR_TIMEOUT is already taken by winerror.h. */
#define ERROR_TIMEOUT_REACHED (-6)
#endif /* OPENOCD_HELPER_LOG_H */
+4 -1
View File
@@ -4,7 +4,9 @@ else
OOCD_TRACE_FILES =
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
@@ -218,3 +220,4 @@ INTEL_IA32_SRC = \
%D%/arm_cti.h
include %D%/openrisc/Makefile.am
include %D%/riscv/Makefile.am
+16
View File
@@ -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
+38
View File
@@ -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
+172
View File
@@ -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);
}
}
+64
View File
@@ -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
+93
View File
@@ -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
+310
View File
@@ -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);
}
+162
View File
@@ -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;
}
+75
View File
@@ -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
+262
View File
@@ -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
+194
View File
@@ -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;
}
+2
View File
@@ -107,6 +107,7 @@ extern struct target_type or1k_target;
extern struct target_type quark_x10xx_target;
extern struct target_type quark_d20xx_target;
extern struct target_type stm8_target;
extern struct target_type riscv_target;
static struct target_type *target_types[] = {
&arm7tdmi_target,
@@ -139,6 +140,7 @@ static struct target_type *target_types[] = {
&quark_x10xx_target,
&quark_d20xx_target,
&stm8_target,
&riscv_target,
#if BUILD_TARGET64
&aarch64_target,
#endif
+22
View File
@@ -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"
+22
View File
@@ -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"
+34
View File
@@ -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