Skip to content

Commit a51ab8d

Browse files
timsifivethinkfat
authored andcommitted
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>
1 parent 9363705 commit a51ab8d

22 files changed

+12313
-1
lines changed

doc/openocd.texi

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8946,6 +8946,84 @@ Display all registers in @emph{group}.
89468946
"timer" or any new group created with addreg command.
89478947
@end deffn
89488948

8949+
@section RISC-V Architecture
8950+
8951+
@uref{http://riscv.org/, RISC-V} is a free and open ISA. OpenOCD supports JTAG
8952+
debug of targets that implement version 0.11 and 0.13 of the RISC-V Debug
8953+
Specification.
8954+
8955+
@subsection RISC-V Terminology
8956+
8957+
A @emph{hart} is a hardware thread. A hart may share resources (eg. FPU) with
8958+
another hart, or may be a separate core. RISC-V treats those the same, and
8959+
OpenOCD exposes each hart as a separate core.
8960+
8961+
@subsection RISC-V Debug Configuration Commands
8962+
8963+
@deffn Command {riscv expose_csrs} n0[-m0][,n1[-m1]]...
8964+
Configure a list of inclusive ranges for CSRs to expose in addition to the
8965+
standard ones. This must be executed before `init`.
8966+
8967+
By default OpenOCD attempts to expose only CSRs that are mentioned in a spec,
8968+
and then only if the corresponding extension appears to be implemented. This
8969+
command can be used if OpenOCD gets this wrong, or a target implements custom
8970+
CSRs.
8971+
@end deffn
8972+
8973+
@deffn Command {riscv set_command_timeout_sec} [seconds]
8974+
Set the wall-clock timeout (in seconds) for individual commands. The default
8975+
should work fine for all but the slowest targets (eg. simulators).
8976+
@end deffn
8977+
8978+
@deffn Command {riscv set_reset_timeout_sec} [seconds]
8979+
Set the maximum time to wait for a hart to come out of reset after reset is
8980+
deasserted.
8981+
@end deffn
8982+
8983+
@deffn Command {riscv set_scratch_ram} none|[address]
8984+
Set the address of 16 bytes of scratch RAM the debugger can use, or 'none'.
8985+
This is used to access 64-bit floating point registers on 32-bit targets.
8986+
@end deffn
8987+
8988+
@deffn Command {riscv set_prefer_sba} on|off
8989+
When on, prefer to use System Bus Access to access memory. When off, prefer to
8990+
use the Program Buffer to access memory.
8991+
@end deffn
8992+
8993+
@subsection RISC-V Authentication Commands
8994+
8995+
The following commands can be used to authenticate to a RISC-V system. Eg. a
8996+
trivial challenge-response protocol could be implemented as follows in a
8997+
configuration file, immediately following @command{init}:
8998+
@example
8999+
set challenge [ocd_riscv authdata_read]
9000+
riscv authdata_write [expr $challenge + 1]
9001+
@end example
9002+
9003+
@deffn Command {riscv authdata_read}
9004+
Return the 32-bit value read from authdata. Note that to get read value back in
9005+
a TCL script, it needs to be invoked as @command{ocd_riscv authdata_read}.
9006+
@end deffn
9007+
9008+
@deffn Command {riscv authdata_write} value
9009+
Write the 32-bit value to authdata.
9010+
@end deffn
9011+
9012+
@subsection RISC-V DMI Commands
9013+
9014+
The following commands allow direct access to the Debug Module Interface, which
9015+
can be used to interact with custom debug features.
9016+
9017+
@deffn Command {riscv dmi_read}
9018+
Perform a 32-bit DMI read at address, returning the value. Note that to get
9019+
read value back in a TCL script, it needs to be invoked as @command{ocd_riscv
9020+
dmi_read}.
9021+
@end deffn
9022+
9023+
@deffn Command {riscv dmi_write} address value
9024+
Perform a 32-bit DMI write of value at address.
9025+
@end deffn
9026+
89499027
@anchor{softwaredebugmessagesandtracing}
89509028
@section Software Debug Messages and Tracing
89519029
@cindex Linux-ARM DCC support

src/helper/log.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ extern int debug_level;
149149
*/
150150
#define ERROR_FAIL (-4)
151151
#define ERROR_WAIT (-5)
152+
/* ERROR_TIMEOUT is already taken by winerror.h. */
153+
#define ERROR_TIMEOUT_REACHED (-6)
152154

153155

154156
#endif /* OPENOCD_HELPER_LOG_H */

src/target/Makefile.am

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ else
44
OOCD_TRACE_FILES =
55
endif
66

7-
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la
7+
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
8+
%D%/riscv/libriscv.la
9+
810

911
STARTUP_TCL_SRCS += %D%/startup.tcl
1012

@@ -218,3 +220,4 @@ INTEL_IA32_SRC = \
218220
%D%/arm_cti.h
219221

220222
include %D%/openrisc/Makefile.am
223+
include %D%/riscv/Makefile.am

src/target/riscv/Makefile.am

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
noinst_LTLIBRARIES += %D%/libriscv.la
2+
%C%_libriscv_la_SOURCES = \
3+
%D%/asm.h \
4+
%D%/batch.h \
5+
%D%/debug_defines.h \
6+
%D%/encoding.h \
7+
%D%/gdb_regs.h \
8+
%D%/opcodes.h \
9+
%D%/program.h \
10+
%D%/riscv.h \
11+
%D%/batch.c \
12+
%D%/program.c \
13+
%D%/riscv-011.c \
14+
%D%/riscv-013.c \
15+
%D%/riscv.c \
16+
%D%/riscv_semihosting.c

src/target/riscv/asm.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef TARGET__RISCV__ASM_H
2+
#define TARGET__RISCV__ASM_H
3+
4+
#include "riscv.h"
5+
6+
/*** Version-independent functions that we don't want in the main address space. ***/
7+
8+
static uint32_t load(const struct target *target, unsigned int rd,
9+
unsigned int base, uint16_t offset) __attribute__ ((unused));
10+
static uint32_t load(const struct target *target, unsigned int rd,
11+
unsigned int base, uint16_t offset)
12+
{
13+
switch (riscv_xlen(target)) {
14+
case 32:
15+
return lw(rd, base, offset);
16+
case 64:
17+
return ld(rd, base, offset);
18+
}
19+
assert(0);
20+
return 0; /* Silence -Werror=return-type */
21+
}
22+
23+
static uint32_t store(const struct target *target, unsigned int src,
24+
unsigned int base, uint16_t offset) __attribute__ ((unused));
25+
static uint32_t store(const struct target *target, unsigned int src,
26+
unsigned int base, uint16_t offset)
27+
{
28+
switch (riscv_xlen(target)) {
29+
case 32:
30+
return sw(src, base, offset);
31+
case 64:
32+
return sd(src, base, offset);
33+
}
34+
assert(0);
35+
return 0; /* Silence -Werror=return-type */
36+
}
37+
38+
#endif

src/target/riscv/batch.c

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#ifdef HAVE_CONFIG_H
2+
#include "config.h"
3+
#endif
4+
5+
#include "batch.h"
6+
#include "debug_defines.h"
7+
#include "riscv.h"
8+
9+
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
10+
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
11+
12+
static void dump_field(const struct scan_field *field);
13+
14+
struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
15+
{
16+
scans += 4;
17+
struct riscv_batch *out = malloc(sizeof(*out));
18+
memset(out, 0, sizeof(*out));
19+
out->target = target;
20+
out->allocated_scans = scans;
21+
out->used_scans = 0;
22+
out->idle_count = idle;
23+
out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t));
24+
out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t));
25+
out->fields = malloc(sizeof(*out->fields) * (scans));
26+
out->last_scan = RISCV_SCAN_TYPE_INVALID;
27+
out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
28+
out->read_keys_used = 0;
29+
return out;
30+
}
31+
32+
void riscv_batch_free(struct riscv_batch *batch)
33+
{
34+
free(batch->data_in);
35+
free(batch->data_out);
36+
free(batch->fields);
37+
free(batch);
38+
}
39+
40+
bool riscv_batch_full(struct riscv_batch *batch)
41+
{
42+
return batch->used_scans > (batch->allocated_scans - 4);
43+
}
44+
45+
int riscv_batch_run(struct riscv_batch *batch)
46+
{
47+
if (batch->used_scans == 0) {
48+
LOG_DEBUG("Ignoring empty batch.");
49+
return ERROR_OK;
50+
}
51+
52+
keep_alive();
53+
54+
LOG_DEBUG("running a batch of %ld scans", (long)batch->used_scans);
55+
riscv_batch_add_nop(batch);
56+
57+
for (size_t i = 0; i < batch->used_scans; ++i) {
58+
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
59+
if (batch->idle_count > 0)
60+
jtag_add_runtest(batch->idle_count, TAP_IDLE);
61+
}
62+
63+
LOG_DEBUG("executing queue");
64+
if (jtag_execute_queue() != ERROR_OK) {
65+
LOG_ERROR("Unable to execute JTAG queue");
66+
return ERROR_FAIL;
67+
}
68+
69+
for (size_t i = 0; i < batch->used_scans; ++i)
70+
dump_field(batch->fields + i);
71+
72+
return ERROR_OK;
73+
}
74+
75+
void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data)
76+
{
77+
assert(batch->used_scans < batch->allocated_scans);
78+
struct scan_field *field = batch->fields + batch->used_scans;
79+
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
80+
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
81+
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
82+
riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
83+
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
84+
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
85+
batch->used_scans++;
86+
}
87+
88+
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
89+
{
90+
assert(batch->used_scans < batch->allocated_scans);
91+
struct scan_field *field = batch->fields + batch->used_scans;
92+
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
93+
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
94+
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
95+
riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
96+
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
97+
batch->last_scan = RISCV_SCAN_TYPE_READ;
98+
batch->used_scans++;
99+
100+
/* FIXME We get the read response back on the next scan. For now I'm
101+
* just sticking a NOP in there, but this should be coelesced away. */
102+
riscv_batch_add_nop(batch);
103+
104+
batch->read_keys[batch->read_keys_used] = batch->used_scans - 1;
105+
LOG_DEBUG("read key %u for batch 0x%p is %u (0x%p)",
106+
(unsigned) batch->read_keys_used, batch, (unsigned) (batch->used_scans - 1),
107+
batch->data_in + sizeof(uint64_t) * (batch->used_scans + 1));
108+
return batch->read_keys_used++;
109+
}
110+
111+
uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key)
112+
{
113+
assert(key < batch->read_keys_used);
114+
size_t index = batch->read_keys[key];
115+
assert(index <= batch->used_scans);
116+
uint8_t *base = batch->data_in + 8 * index;
117+
return base[0] |
118+
((uint64_t) base[1]) << 8 |
119+
((uint64_t) base[2]) << 16 |
120+
((uint64_t) base[3]) << 24 |
121+
((uint64_t) base[4]) << 32 |
122+
((uint64_t) base[5]) << 40 |
123+
((uint64_t) base[6]) << 48 |
124+
((uint64_t) base[7]) << 56;
125+
}
126+
127+
void riscv_batch_add_nop(struct riscv_batch *batch)
128+
{
129+
assert(batch->used_scans < batch->allocated_scans);
130+
struct scan_field *field = batch->fields + batch->used_scans;
131+
field->num_bits = riscv_dmi_write_u64_bits(batch->target);
132+
field->out_value = (void *)(batch->data_out + batch->used_scans * sizeof(uint64_t));
133+
field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t));
134+
riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
135+
riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
136+
batch->last_scan = RISCV_SCAN_TYPE_NOP;
137+
batch->used_scans++;
138+
LOG_DEBUG(" added NOP with in_value=0x%p", field->in_value);
139+
}
140+
141+
void dump_field(const struct scan_field *field)
142+
{
143+
static const char * const op_string[] = {"-", "r", "w", "?"};
144+
static const char * const status_string[] = {"+", "?", "F", "b"};
145+
146+
if (debug_level < LOG_LVL_DEBUG)
147+
return;
148+
149+
assert(field->out_value != NULL);
150+
uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
151+
unsigned int out_op = get_field(out, DTM_DMI_OP);
152+
unsigned int out_data = get_field(out, DTM_DMI_DATA);
153+
unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
154+
155+
if (field->in_value) {
156+
uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
157+
unsigned int in_op = get_field(in, DTM_DMI_OP);
158+
unsigned int in_data = get_field(in, DTM_DMI_DATA);
159+
unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
160+
161+
log_printf_lf(LOG_LVL_DEBUG,
162+
__FILE__, __LINE__, __PRETTY_FUNCTION__,
163+
"%db %s %08x @%02x -> %s %08x @%02x",
164+
field->num_bits,
165+
op_string[out_op], out_data, out_address,
166+
status_string[in_op], in_data, in_address);
167+
} else {
168+
log_printf_lf(LOG_LVL_DEBUG,
169+
__FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?",
170+
field->num_bits, op_string[out_op], out_data, out_address);
171+
}
172+
}

0 commit comments

Comments
 (0)