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.
|
||||
@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
|
||||
|
|
|
@ -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,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
|
||||
|
|
|
@ -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_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
|
||||
|
|
|
@ -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